feat: add system metrics dashboard
This commit is contained in:
165
server/api/system.get.ts
Normal file
165
server/api/system.get.ts
Normal 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()
|
||||
}
|
||||
})
|
||||
Reference in New Issue
Block a user