259 lines
8.0 KiB
Vue
259 lines
8.0 KiB
Vue
<script setup>
|
|
import { computed, onMounted, reactive, ref } from "vue";
|
|
import { useRoute, useRouter } from "vue-router";
|
|
|
|
import EdgeGatewayManager from "@/features/edgeGateways/EdgeGatewayManager.vue";
|
|
import EdgeGatewayFleetLanding from "@/features/edgeGateways/EdgeGatewayFleetLanding.vue";
|
|
import PageTitle from "@/components/global/PageTitle.vue";
|
|
import RestrictedPageWrapper from "@/components/page/wrappers/RestrictedPageWrapper.vue";
|
|
import { SessionUser } from "@/components/session/token/SessionUser.vue";
|
|
import ConfigurationSubPageWrapper from "@/views/dashboards/superUserDashboard/configuration/ConfigurationSubPageWrapper.vue";
|
|
import { normalizeEdgeGatewayError } from "@/features/edgeGateways/edgeGatewayErrors.js";
|
|
|
|
const route = useRoute();
|
|
const router = useRouter();
|
|
|
|
const selectedGatewayId = computed(() => route.params.id ?? null);
|
|
const activeView = computed(() => {
|
|
if (route.name === "edgegatewaytasks") {
|
|
return "tasks";
|
|
}
|
|
if (route.name === "edgegatewaylogs") {
|
|
return "logs";
|
|
}
|
|
if (route.name === "edgegatewaystatistics") {
|
|
return "statistics";
|
|
}
|
|
if (route.name === "edgegatewayterminal") {
|
|
return "terminal";
|
|
}
|
|
if (route.name === "edgegatewayinventory") {
|
|
return "inventory";
|
|
}
|
|
if (route.name === "edgegatewaysettings") {
|
|
return "settings";
|
|
}
|
|
return "overview";
|
|
});
|
|
|
|
const moduleConfigEntries = ref([]);
|
|
const moduleConfigMap = ref({
|
|
enabled: true,
|
|
default_release_channel: "stable",
|
|
default_update_window: "02:00-04:00",
|
|
});
|
|
const moduleState = reactive({
|
|
loading: true,
|
|
saving: false,
|
|
unavailable: false,
|
|
error: null,
|
|
});
|
|
|
|
const canonicalPath = (gatewayId, view = "overview") =>
|
|
gatewayId
|
|
? `/superuser/configuration/edgegateway/${encodeURIComponent(String(gatewayId))}/${view}`
|
|
: "/superuser/configuration/edgegateway";
|
|
|
|
const handleNavigation = async ({ gatewayId, view }) => {
|
|
const nextPath = canonicalPath(gatewayId, view || "overview");
|
|
if (window.location.pathname === nextPath) {
|
|
return;
|
|
}
|
|
await router.push(nextPath);
|
|
};
|
|
|
|
const loadModuleConfig = async () => {
|
|
moduleState.loading = true;
|
|
moduleState.error = null;
|
|
moduleState.unavailable = false;
|
|
|
|
try {
|
|
const response = await SessionUser.superUser.modules.edgegateway.config.get_all();
|
|
moduleConfigEntries.value = response?.data?.data || [];
|
|
moduleConfigMap.value = {
|
|
enabled: response?.data?.config?.enabled !== false,
|
|
default_release_channel: response?.data?.config?.default_release_channel || "stable",
|
|
default_update_window: response?.data?.config?.default_update_window || "02:00-04:00",
|
|
};
|
|
} catch (error) {
|
|
moduleState.error = normalizeEdgeGatewayError(error);
|
|
moduleState.unavailable = Number(error?.response?.status) === 404;
|
|
} finally {
|
|
moduleState.loading = false;
|
|
}
|
|
};
|
|
|
|
const saveModuleConfig = async () => {
|
|
moduleState.saving = true;
|
|
moduleState.error = null;
|
|
|
|
try {
|
|
await SessionUser.superUser.modules.edgegateway.config.set_config({
|
|
enabled: Boolean(moduleConfigMap.value.enabled),
|
|
default_release_channel: moduleConfigMap.value.default_release_channel || "stable",
|
|
default_update_window: moduleConfigMap.value.default_update_window || "02:00-04:00",
|
|
});
|
|
await loadModuleConfig();
|
|
} catch (error) {
|
|
moduleState.error = normalizeEdgeGatewayError(error);
|
|
} finally {
|
|
moduleState.saving = false;
|
|
}
|
|
};
|
|
|
|
onMounted(async () => {
|
|
await loadModuleConfig();
|
|
});
|
|
</script>
|
|
|
|
<template>
|
|
<RestrictedPageWrapper :hasPermission="SessionUser.canAccessSuperUser()">
|
|
<ConfigurationSubPageWrapper>
|
|
<template #title>
|
|
<PageTitle
|
|
title="Edge gateway module"
|
|
subtitle="Module defaults, fleet state, rollout visibility, and gateway operations for the edge runtime"
|
|
/>
|
|
</template>
|
|
|
|
<template #content>
|
|
<section class="edge-gateway-module-page">
|
|
<article class="edge-gateway-module-page__panel" data-testid="edge-gateway-module-config">
|
|
<div class="edge-gateway-module-page__header">
|
|
<div>
|
|
<p class="edge-gateway-module-page__eyebrow">Module defaults</p>
|
|
<h2>Control-plane defaults</h2>
|
|
<p>These defaults are applied to newly claimed gateways when the module routes are enabled.</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div v-if="moduleState.error" class="notification is-danger">
|
|
<strong>{{ moduleState.error.title }}</strong>
|
|
<p>{{ moduleState.error.message }}</p>
|
|
</div>
|
|
|
|
<div v-if="moduleState.unavailable" class="edge-gateway-module-page__disabled" data-testid="gateway-module-disabled-state">
|
|
The edge gateway module routes are currently unavailable. Re-enable the module before managing fleet traffic here.
|
|
</div>
|
|
|
|
<div v-else class="edge-gateway-module-page__grid">
|
|
<label class="edge-gateway-module-page__field">
|
|
<span>Enabled</span>
|
|
<input
|
|
v-model="moduleConfigMap.enabled"
|
|
data-testid="gateway-module-enabled"
|
|
type="checkbox"
|
|
/>
|
|
</label>
|
|
|
|
<label class="edge-gateway-module-page__field">
|
|
<span>Default release channel</span>
|
|
<div class="select is-fullwidth">
|
|
<select v-model="moduleConfigMap.default_release_channel" data-testid="gateway-module-release-channel">
|
|
<option value="stable">stable</option>
|
|
<option value="canary">canary</option>
|
|
</select>
|
|
</div>
|
|
</label>
|
|
|
|
<label class="edge-gateway-module-page__field">
|
|
<span>Default update window</span>
|
|
<input
|
|
v-model="moduleConfigMap.default_update_window"
|
|
class="input"
|
|
data-testid="gateway-module-update-window"
|
|
placeholder="02:00-04:00"
|
|
/>
|
|
</label>
|
|
</div>
|
|
|
|
<div class="edge-gateway-module-page__actions">
|
|
<button
|
|
class="button is-dark"
|
|
type="button"
|
|
data-testid="gateway-module-save"
|
|
:disabled="moduleState.loading || moduleState.saving || moduleState.unavailable"
|
|
@click="saveModuleConfig"
|
|
>
|
|
Save module defaults
|
|
</button>
|
|
</div>
|
|
</article>
|
|
|
|
<EdgeGatewayFleetLanding v-if="!moduleState.unavailable && !selectedGatewayId" />
|
|
|
|
<EdgeGatewayManager
|
|
v-if="!moduleState.unavailable"
|
|
:selected-gateway-id="selectedGatewayId"
|
|
:active-view="activeView"
|
|
:route-driven="true"
|
|
:allow-destructive="true"
|
|
@navigate="handleNavigation"
|
|
/>
|
|
</section>
|
|
</template>
|
|
</ConfigurationSubPageWrapper>
|
|
</RestrictedPageWrapper>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.edge-gateway-module-page {
|
|
display: grid;
|
|
gap: 1rem;
|
|
}
|
|
|
|
.edge-gateway-module-page__panel {
|
|
border: 1px solid #e5e7eb;
|
|
border-radius: 20px;
|
|
background: #ffffff;
|
|
padding: 1rem;
|
|
box-shadow: 0 18px 40px rgba(15, 23, 42, 0.07);
|
|
}
|
|
|
|
.edge-gateway-module-page__header h2 {
|
|
margin: 0 0 0.35rem;
|
|
color: #0f172a;
|
|
}
|
|
|
|
.edge-gateway-module-page__header p {
|
|
margin: 0;
|
|
color: #475569;
|
|
}
|
|
|
|
.edge-gateway-module-page__eyebrow {
|
|
margin: 0 0 0.35rem;
|
|
font-size: 0.78rem;
|
|
letter-spacing: 0.08em;
|
|
text-transform: uppercase;
|
|
color: #64748b;
|
|
}
|
|
|
|
.edge-gateway-module-page__grid {
|
|
display: grid;
|
|
gap: 1rem;
|
|
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
|
|
margin-top: 1rem;
|
|
}
|
|
|
|
.edge-gateway-module-page__field {
|
|
display: grid;
|
|
gap: 0.45rem;
|
|
color: #334155;
|
|
}
|
|
|
|
.edge-gateway-module-page__actions {
|
|
margin-top: 1rem;
|
|
display: flex;
|
|
justify-content: flex-end;
|
|
}
|
|
|
|
.edge-gateway-module-page__disabled {
|
|
margin-top: 1rem;
|
|
padding: 1rem;
|
|
border-radius: 14px;
|
|
background: #fff7ed;
|
|
border: 1px solid #fdba74;
|
|
color: #9a3412;
|
|
}
|
|
</style>
|