feat(manager): expose safe VPS edge health
This commit is contained in:
@@ -38,6 +38,7 @@ import type {
|
||||
export type ControlViewId =
|
||||
| "catalog"
|
||||
| "infrastructure"
|
||||
| "hosts"
|
||||
| "sessions"
|
||||
| "bindings"
|
||||
| "commands"
|
||||
@@ -145,6 +146,13 @@ export function DeviceControlView({
|
||||
}))}
|
||||
/>
|
||||
) : null}
|
||||
{view === "hosts" ? (
|
||||
<HostsView
|
||||
workspace={workspace}
|
||||
canManage={platformOwner}
|
||||
onCreateEdge={() => setDialog("edge")}
|
||||
/>
|
||||
) : null}
|
||||
{view === "sessions" ? <SessionsView workspace={workspace} /> : null}
|
||||
{view === "bindings" ? (
|
||||
<BindingsView
|
||||
@@ -372,6 +380,50 @@ function InfrastructureView({ workspace, canManageCatalog, canManageRoutes, onCr
|
||||
);
|
||||
}
|
||||
|
||||
function HostsView({ workspace, canManage, onCreateEdge }: {
|
||||
workspace: ProjectWorkspace;
|
||||
canManage: boolean;
|
||||
onCreateEdge: () => void;
|
||||
}) {
|
||||
return (
|
||||
<ControlStack>
|
||||
<ControlToolbar
|
||||
copy="VPS-хосты показаны через зарегистрированную роль Edge: состояние берётся из pinned mTLS Core↔Edge канала, а связи — из маршрутов проекта. Адреса, ключи и credentials в браузер не выдаются."
|
||||
actions={canManage ? <Button size="compact" variant="primary" onClick={onCreateEdge}>Новый VPS Edge</Button> : null}
|
||||
/>
|
||||
<ControlSection title="VPS и Edge-хосты" count={workspace.edges.length}>
|
||||
<ResourceGrid empty="VPS/Edge-хосты для проекта пока не зарегистрированы.">
|
||||
{workspace.edges.map((edge) => {
|
||||
const routes = workspace.routes.filter((route) => route.edgeRef === edge.edgeRef);
|
||||
const runtimeState = edge.channel?.runtimeState ?? "unobserved";
|
||||
return (
|
||||
<ResourceCard
|
||||
key={edge.edgeRef}
|
||||
eyebrow="VPS / EDGE HOST"
|
||||
title={edge.displayName}
|
||||
description={edge.deploymentRef || edge.edgeKey}
|
||||
status={runtimeState}
|
||||
meta={[
|
||||
`registration · ${edge.lifecycleState}`,
|
||||
`channel · ${edge.channel?.lifecycleState ?? "disabled"}`,
|
||||
`${routes.length} ${routes.length === 1 ? "маршрут" : "маршрутов"}`,
|
||||
...(edge.channel?.generationRef ? [edge.channel.generationRef] : []),
|
||||
...(edge.channel?.lastErrorCode ? [`error · ${edge.channel.lastErrorCode}`] : []),
|
||||
]}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</ResourceGrid>
|
||||
</ControlSection>
|
||||
<GlassSurface padding="md" tone="soft">
|
||||
<p className="device-manager-card-copy">
|
||||
Общий реестр произвольных VPS, deployments, services и управляемая консоль требуют отдельного канонического ontology package. Текущий экран намеренно отображает только уже существующую проверяемую Edge-инфраструктуру.
|
||||
</p>
|
||||
</GlassSurface>
|
||||
</ControlStack>
|
||||
);
|
||||
}
|
||||
|
||||
function SessionsView({ workspace }: { workspace: ProjectWorkspace }) {
|
||||
return (
|
||||
<ControlStack>
|
||||
@@ -931,9 +983,9 @@ function commaList(value: string) {
|
||||
}
|
||||
|
||||
function statusTone(status: string): "neutral" | "success" | "warning" | "danger" {
|
||||
if (["active", "online", "verified", "applied", "recorded", "immutable"].includes(status)) return "success";
|
||||
if (["failed", "rejected", "revoked", "retired"].includes(status)) return "danger";
|
||||
if (["draft", "provisioning", "pending", "pending_external_approval", "unknown", "disabled"].includes(status)) return "warning";
|
||||
if (["active", "accepted", "online", "verified", "applied", "recorded", "immutable"].includes(status)) return "success";
|
||||
if (["absent", "failed", "rejected", "revoked", "retired"].includes(status)) return "danger";
|
||||
if (["connecting", "draft", "provisioning", "pending", "pending_external_approval", "unknown", "unobserved", "disabled"].includes(status)) return "warning";
|
||||
return "neutral";
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user