166 lines
3.6 KiB
TypeScript
166 lines
3.6 KiB
TypeScript
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()
|
|
}
|
|
})
|