feat(manager): expose safe VPS edge health

This commit is contained in:
DCCONSTRUCTIONS
2026-08-22 13:08:04 +03:00
parent 07ffb22d38
commit 6ed4414a97
14 changed files with 464 additions and 42 deletions
+55 -3
View File
@@ -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";
}