mirror of
https://github.com/Sagit-chu/flvx.git
synced 2026-10-01 00:26:38 +08:00
9de240f034
- Add NodeMetric/TunnelMetric/ServiceMonitor models and repository methods - Implement metrics ingestion service with per-minute bucket aggregation - Add health checker for node connectivity monitoring - Wire node metrics from WebSocket SystemInfo messages - Add tunnel metrics ingestion from flow upload endpoint - Create monitoring REST API endpoints for nodes, tunnels, services - Implement service monitor CRUD and execution (TCP/ICMP checks) - Add MonitorPermission for non-admin access control - Create frontend monitor page with node/tunnel/service views - Add tunnel metrics ingestion from agent flow reports - Include schema migration for tunnel_metric unique index - Fix tunnel entry port conflict validation to use transaction Entire-Checkpoint: 030821a7c8e3
103 lines
2.5 KiB
TypeScript
103 lines
2.5 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;
|
|
};
|
|
|
|
export const buildNodeSystemInfo = (
|
|
messageData: unknown,
|
|
previous: NodeSystemInfo | null | undefined,
|
|
): NodeSystemInfo | null => {
|
|
const raw = parseRawSystemInfo(messageData);
|
|
|
|
if (!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),
|
|
};
|
|
};
|