fix: restore node resource status sync

This commit is contained in:
Codex
2026-06-12 14:18:55 +00:00
parent 17b54c685a
commit b41847853a
11 changed files with 347 additions and 109 deletions
File diff suppressed because one or more lines are too long
+48 -11
View File
@@ -784,11 +784,50 @@ function HeartbeatPage({ nodes }: AnyRecord) {
);
}
function metricPointFromCurrent(current: AnyRecord): AnyRecord {
const cpu = current?.cpu || {};
const memory = current?.memory || {};
const disk = current?.disk || {};
return {
at: current?.collectedAt,
cpuPercent: asNumber(cpu.percent),
memoryPercent: asNumber(memory.percent),
diskPercent: asNumber(disk.percent),
memoryUsedBytes: asNumber(memory.usedBytes),
memoryTotalBytes: asNumber(memory.totalBytes),
diskUsedBytes: asNumber(disk.usedBytes),
diskTotalBytes: asNumber(disk.totalBytes),
load1: asNumber(cpu.load1),
};
}
function synchronizedMetricPoints(history: any[], current: AnyRecord | null): AnyRecord[] {
if (!current) return [];
const currentPoint = metricPointFromCurrent(current);
const currentAt = timeMs(currentPoint.at);
const points = (Array.isArray(history) ? history : [])
.filter((point) => point && typeof point === "object")
.filter((point) => currentAt === null || timeMs(point.at) !== currentAt);
points.push(currentPoint);
return points
.sort((left, right) => (timeMs(left.at) ?? 0) - (timeMs(right.at) ?? 0))
.slice(-60);
}
function NodeMonitorPage({ nodes, systemStatuses, tasks, onRaw, refresh }: AnyRecord) {
const [selectedProvider, setSelectedProvider] = useState("");
const merged = useMemo(() => nodes.map((node: any) => {
const status = systemStatuses.find((item: any) => item.providerId === node.providerId);
return { ...node, systemCurrent: status?.current || null, systemHistory: status?.history || [], systemUpdatedAt: status?.updatedAt || null };
return {
...node,
systemCurrent: status?.current || null,
systemLastKnown: status?.lastKnown || null,
systemCurrentCollectedAt: status?.currentCollectedAt || null,
systemStale: Boolean(status?.stale),
systemStaleSeconds: status?.staleSeconds ?? null,
systemHistory: status?.history || [],
systemUpdatedAt: status?.updatedAt || null,
};
}), [nodes, systemStatuses]);
const active = merged.find((node: any) => node.providerId === selectedProvider) || merged[0] || null;
useEffect(() => {
@@ -802,12 +841,10 @@ function NodeMonitorPage({ nodes, systemStatuses, tasks, onRaw, refresh }: AnyRe
const cpu = current?.cpu || {};
const memory = current?.memory || {};
const disk = current?.disk || {};
const points = history.length > 0 ? history : current ? [{
at: current.collectedAt,
cpuPercent: asNumber(cpu.percent),
memoryPercent: asNumber(memory.percent),
diskPercent: asNumber(disk.percent),
}] : [];
const points = synchronizedMetricPoints(history, current);
const staleText = active.systemCurrentCollectedAt
? `最后采样 ${fmtDate(active.systemCurrentCollectedAt)},已过期 ${fmtDuration(asNumber(active.systemStaleSeconds))}`
: "最后采样时间不可用";
return h("div", { className: "monitor-page", "data-testid": "node-monitor-page" },
h("div", { className: "docker-node-strip" },
@@ -820,7 +857,7 @@ function NodeMonitorPage({ nodes, systemStatuses, tasks, onRaw, refresh }: AnyRe
h("span", { className: `pulse ${node.status}` }),
h("strong", null, node.name),
h("code", null, node.providerId),
h("span", null, node.systemCurrent ? `CPU ${fmtPercent(node.systemCurrent.cpu?.percent)} / MEM ${fmtPercent(node.systemCurrent.memory?.percent)}` : "等待指标"),
h("span", null, node.systemCurrent ? `CPU ${fmtPercent(node.systemCurrent.cpu?.percent)} / MEM ${fmtPercent(node.systemCurrent.memory?.percent)}` : node.systemStale ? "指标过期" : "等待指标"),
)),
),
h("div", { className: "monitor-layout" },
@@ -828,9 +865,9 @@ function NodeMonitorPage({ nodes, systemStatuses, tasks, onRaw, refresh }: AnyRe
title: "任务管理器视图",
eyebrow: active.name,
className: "monitor-main-panel",
actions: current ? h(RawButton, { title: `System ${active.providerId}`, data: { current, history }, onOpen: onRaw }) : null,
actions: current || active.systemStale ? h(RawButton, { title: `System ${active.providerId}`, data: { current, lastKnown: active.systemLastKnown, currentCollectedAt: active.systemCurrentCollectedAt, stale: active.systemStale, staleSeconds: active.systemStaleSeconds, history }, onOpen: onRaw }) : null,
},
!current ? h(EmptyState, { title: "系统指标未上报", text: "provider-gateway 会周期性采集 /proc 与 df,并保存历史曲线" }) :
!current ? h(EmptyState, { title: active.systemStale ? "系统指标已过期" : "系统指标未上报", text: active.systemStale ? `${staleText};等待 provider-gateway 恢复 system.status 上报` : "provider-gateway 会周期性采集 /proc 与 df,并保存历史曲线" }) :
h("div", null,
h("div", { className: "monitor-hero" },
h("div", null,
@@ -854,7 +891,7 @@ function NodeMonitorPage({ nodes, systemStatuses, tasks, onRaw, refresh }: AnyRe
h(MetricCard, { label: "CPU 当前", value: fmtPercent(cpu.percent), hint: `history ${points.length} samples`, tone: "ok" }),
h(MetricCard, { label: "实际内存", value: fmtBytes(memory.usedBytes), hint: `${fmtPercent(memory.percent)} 不含缓存` }),
h(MetricCard, { label: "硬盘已用", value: fmtBytes(disk.usedBytes), hint: fmtPercent(disk.percent) }),
h(MetricCard, { label: "更新时间", value: fmtDate(active.systemUpdatedAt || current.collectedAt), hint: active.providerId }),
h(MetricCard, { label: "更新时间", value: fmtDate(current.collectedAt || active.systemUpdatedAt), hint: active.providerId }),
),
h(ProcessResourceTable, { current, onRaw }),
),