feat: add system metrics dashboard

This commit is contained in:
2026-03-10 15:54:45 +01:00
parent ffe463e130
commit 31e101abbd
4 changed files with 755 additions and 3 deletions

165
server/api/system.get.ts Normal file
View File

@@ -0,0 +1,165 @@
import fs from "node:fs"
import os from "node:os"
type CpuTimesSnapshot = {
idle: number
total: number
}
type NetworkSnapshot = {
rxBytes: number
txBytes: number
timestamp: number
}
let previousNetworkSnapshot: NetworkSnapshot | null = null
function getCpuSnapshot(): CpuTimesSnapshot {
const cpus = os.cpus()
return 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 wait(durationMs: number) {
return new Promise((resolve) => {
setTimeout(resolve, durationMs)
})
}
async function getCpuUsagePercent(sampleMs: number) {
const start = getCpuSnapshot()
await wait(sampleMs)
const end = getCpuSnapshot()
const idleDelta = end.idle - start.idle
const totalDelta = end.total - start.total
if (totalDelta <= 0) {
return 0
}
return Math.max(0, Math.min(100, Math.round((1 - idleDelta / totalDelta) * 100)))
}
function getNetworkTotals() {
try {
const content = fs.readFileSync("/proc/net/dev", "utf8")
const totals = content
.split("\n")
.slice(2)
.map((line) => line.trim())
.filter(Boolean)
.reduce(
(accumulator, line) => {
const [namePart, valuesPart] = line.split(":")
const interfaceName = namePart?.trim()
if (!interfaceName || interfaceName === "lo" || !valuesPart) {
return accumulator
}
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)) {
accumulator.rxBytes += rxBytes
}
if (Number.isFinite(txBytes)) {
accumulator.txBytes += txBytes
}
return accumulator
},
{rxBytes: 0, txBytes: 0}
)
return totals
} catch {
return null
}
}
function getNetworkRatesMbps() {
const totals = getNetworkTotals()
const now = Date.now()
if (!totals) {
previousNetworkSnapshot = null
return {
incomingMbps: 0,
outgoingMbps: 0
}
}
const currentSnapshot: NetworkSnapshot = {
...totals,
timestamp: now
}
if (!previousNetworkSnapshot) {
previousNetworkSnapshot = currentSnapshot
return {
incomingMbps: 0,
outgoingMbps: 0
}
}
const elapsedSeconds = (currentSnapshot.timestamp - previousNetworkSnapshot.timestamp) / 1000
if (elapsedSeconds <= 0) {
previousNetworkSnapshot = currentSnapshot
return {
incomingMbps: 0,
outgoingMbps: 0
}
}
const incomingMbps = Math.max(
0,
Number((((currentSnapshot.rxBytes - previousNetworkSnapshot.rxBytes) * 8) / elapsedSeconds / 1000000).toFixed(2))
)
const outgoingMbps = Math.max(
0,
Number((((currentSnapshot.txBytes - previousNetworkSnapshot.txBytes) * 8) / elapsedSeconds / 1000000).toFixed(2))
)
previousNetworkSnapshot = currentSnapshot
return {
incomingMbps,
outgoingMbps
}
}
export default defineEventHandler(async () => {
const totalMemory = os.totalmem()
const freeMemory = os.freemem()
const usedMemory = totalMemory - freeMemory
const memoryPercent = totalMemory > 0 ? Math.round((usedMemory / totalMemory) * 100) : 0
const cpuPercent = await getCpuUsagePercent(200)
const {incomingMbps, outgoingMbps} = getNetworkRatesMbps()
return {
cpuPercent,
memoryPercent,
totalMemory,
usedMemory,
incomingMbps,
outgoingMbps,
sampledAt: new Date().toISOString()
}
})