import fs from "node:fs" import os from "node:os" import type { SystemMetrics } from "~/types/system" type CpuTimesSnapshot = { idle: number total: number } type NetworkTotals = { rxBytes: number txBytes: number } type NetworkSnapshot = NetworkTotals & { timestamp: number } let lastCpuSnapshot: CpuTimesSnapshot | null = null let lastNetworkSnapshot: NetworkSnapshot | null = null let intervalId: NodeJS.Timeout | null = null let latestSnapshot: SystemMetrics = createSnapshot({ cpuPercent: 0, incomingMbps: 0, outgoingMbps: 0 }) function createSnapshot(overrides: { cpuPercent: number incomingMbps: number outgoingMbps: number }): SystemMetrics { const totalMemory = os.totalmem() const freeMemory = os.freemem() const usedMemory = totalMemory - freeMemory const memoryPercent = totalMemory > 0 ? Math.round((usedMemory / totalMemory) * 100) : 0 return { cpuPercent: overrides.cpuPercent, memoryPercent, totalMemory, usedMemory, incomingMbps: overrides.incomingMbps, outgoingMbps: overrides.outgoingMbps, sampledAt: new Date().toISOString() } } function getCpuSnapshot(): CpuTimesSnapshot { return os.cpus().reduce( (snapshot, cpu) => { const total = Object.values(cpu.times).reduce((sum, value) => sum + value, 0) snapshot.idle += cpu.times.idle snapshot.total += total return snapshot }, { idle: 0, total: 0 } ) } function getCpuUsagePercent() { const currentSnapshot = getCpuSnapshot() if (!lastCpuSnapshot) { lastCpuSnapshot = currentSnapshot return 0 } const idleDelta = currentSnapshot.idle - lastCpuSnapshot.idle const totalDelta = currentSnapshot.total - lastCpuSnapshot.total lastCpuSnapshot = currentSnapshot if (totalDelta <= 0) { return 0 } return Math.max(0, Math.min(100, Math.round((1 - idleDelta / totalDelta) * 100))) } function getNetworkTotals(): NetworkTotals | null { try { const content = fs.readFileSync("/proc/net/dev", "utf8") return content .split("\n") .slice(2) .map((line) => line.trim()) .filter(Boolean) .reduce( (totals, line) => { const [namePart, valuesPart] = line.split(":") const interfaceName = namePart?.trim() if (!interfaceName || interfaceName === "lo" || !valuesPart) { return totals } const values = valuesPart.trim().split(/\s+/) const rxBytes = Number.parseInt(values[0] || "0", 10) const txBytes = Number.parseInt(values[8] || "0", 10) if (Number.isFinite(rxBytes)) { totals.rxBytes += rxBytes } if (Number.isFinite(txBytes)) { totals.txBytes += txBytes } return totals }, { rxBytes: 0, txBytes: 0 } ) } catch { return null } } function getNetworkRatesMbps() { const totals = getNetworkTotals() const now = Date.now() if (!totals) { lastNetworkSnapshot = null return { incomingMbps: 0, outgoingMbps: 0 } } const currentSnapshot: NetworkSnapshot = { ...totals, timestamp: now } if (!lastNetworkSnapshot) { lastNetworkSnapshot = currentSnapshot return { incomingMbps: 0, outgoingMbps: 0 } } const elapsedSeconds = (currentSnapshot.timestamp - lastNetworkSnapshot.timestamp) / 1000 if (elapsedSeconds <= 0) { lastNetworkSnapshot = currentSnapshot return { incomingMbps: 0, outgoingMbps: 0 } } const incomingMbps = Math.max( 0, Number((((currentSnapshot.rxBytes - lastNetworkSnapshot.rxBytes) * 8) / elapsedSeconds / 1000000).toFixed(2)) ) const outgoingMbps = Math.max( 0, Number((((currentSnapshot.txBytes - lastNetworkSnapshot.txBytes) * 8) / elapsedSeconds / 1000000).toFixed(2)) ) lastNetworkSnapshot = currentSnapshot return { incomingMbps, outgoingMbps } } function refreshSnapshot() { const cpuPercent = getCpuUsagePercent() const { incomingMbps, outgoingMbps } = getNetworkRatesMbps() latestSnapshot = createSnapshot({ cpuPercent, incomingMbps, outgoingMbps }) } export function getSystemMetricsSnapshot() { return latestSnapshot } export default defineNitroPlugin(() => { if (intervalId) { return } refreshSnapshot() intervalId = setInterval(refreshSnapshot, 2000) })