Files
flvx/vite-frontend/src/pages/node/system-info.ts
T
2026-03-21 18:40:28 +08:00

139 lines
3.3 KiB
TypeScript

export interface NodeSystemInfo {
cpuUsage: number;
memoryUsage: number;
uploadTraffic: number;
downloadTraffic: number;
uploadSpeed: number;
downloadSpeed: number;
uptime: number;
diskUsage?: number;
load1?: number;
load5?: number;
load15?: number;
tcpConns?: number;
udpConns?: number;
netInSpeed?: number;
netOutSpeed?: number;
}
type RawSystemInfo = Record<string, string | number | undefined>;
const toInteger = (value: string | number | undefined): number => {
return Number.parseInt(String(value ?? 0), 10) || 0;
};
const toFloat = (value: string | number | undefined): number => {
return Number.parseFloat(String(value ?? 0)) || 0;
};
const parseRawSystemInfo = (messageData: unknown): RawSystemInfo | null => {
if (typeof messageData === "string") {
try {
const parsed = JSON.parse(messageData);
if (parsed && typeof parsed === "object") {
return parsed as RawSystemInfo;
}
return null;
} catch {
return null;
}
}
if (messageData && typeof messageData === "object") {
return messageData as RawSystemInfo;
}
return null;
};
/**
* Heuristic to verify the parsed object actually contains system-info fields.
* Prevents non-metric messages (command responses, etc.) from being mistakenly
* treated as system info, which would reset all values to 0 and cause UI flicker.
*/
const SYSTEM_INFO_KEYS = [
"uptime",
"cpu_usage",
"memory_usage",
"disk_usage",
"bytes_received",
"bytes_transmitted",
"net_in_speed",
"net_out_speed",
"tcp_conns",
"udp_conns",
"load1",
"load5",
"load15",
] as const;
const looksLikeSystemInfo = (raw: RawSystemInfo): boolean => {
let matched = 0;
for (const key of SYSTEM_INFO_KEYS) {
if (key in raw && raw[key] !== undefined) {
matched++;
if (matched >= 3) {
return true;
}
}
}
return false;
};
export const buildNodeSystemInfo = (
messageData: unknown,
previous: NodeSystemInfo | null | undefined,
): NodeSystemInfo | null => {
const raw = parseRawSystemInfo(messageData);
if (!raw || !looksLikeSystemInfo(raw)) {
return null;
}
const uploadTraffic = toInteger(raw.bytes_transmitted);
const downloadTraffic = toInteger(raw.bytes_received);
const uptime = toInteger(raw.uptime);
let uploadSpeed = 0;
let downloadSpeed = 0;
if (previous && previous.uptime) {
const timeDiff = uptime - previous.uptime;
if (timeDiff > 0 && timeDiff <= 10) {
const uploadDiff = uploadTraffic - previous.uploadTraffic;
const downloadDiff = downloadTraffic - previous.downloadTraffic;
if (uploadTraffic >= previous.uploadTraffic && uploadDiff >= 0) {
uploadSpeed = uploadDiff / timeDiff;
}
if (downloadTraffic >= previous.downloadTraffic && downloadDiff >= 0) {
downloadSpeed = downloadDiff / timeDiff;
}
}
}
return {
cpuUsage: toFloat(raw.cpu_usage),
memoryUsage: toFloat(raw.memory_usage),
uploadTraffic,
downloadTraffic,
uploadSpeed,
downloadSpeed,
uptime,
diskUsage: toFloat(raw.disk_usage),
load1: toFloat(raw.load1),
load5: toFloat(raw.load5),
load15: toFloat(raw.load15),
tcpConns: toInteger(raw.tcp_conns),
udpConns: toInteger(raw.udp_conns),
netInSpeed: toInteger(raw.net_in_speed),
netOutSpeed: toInteger(raw.net_out_speed),
};
};