fix: arch-03 worker system metric
This commit is contained in:
@@ -1,4 +1,3 @@
|
||||
import { Readable } from "node:stream"
|
||||
import {
|
||||
runSsh,
|
||||
shellQuote,
|
||||
@@ -16,37 +15,13 @@ function buildContentDisposition(fileName: string) {
|
||||
return `attachment; filename="${asciiName}"; filename*=UTF-8''${encodeURIComponent(fileName)}`
|
||||
}
|
||||
|
||||
function speedtestStream(event: H3Event) {
|
||||
const size = 128 * 1024 * 1024
|
||||
let sent = 0
|
||||
|
||||
const stream = new Readable({
|
||||
read(chunkSize) {
|
||||
if (sent >= size) {
|
||||
this.push(null)
|
||||
return
|
||||
}
|
||||
|
||||
const remaining = size - sent
|
||||
const chunk = Buffer.alloc(Math.min(chunkSize, remaining), "a")
|
||||
sent += chunk.length
|
||||
this.push(chunk)
|
||||
}
|
||||
})
|
||||
|
||||
setHeader(event, "Content-Type", "application/octet-stream")
|
||||
setHeader(event, "Content-Length", size)
|
||||
return stream
|
||||
}
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
const { folder, file } = getQuery(event)
|
||||
const folderName = typeof folder === "string" ? folder : null
|
||||
const fileName = typeof file === "string" ? file : null
|
||||
|
||||
// Compat mode: utilisé par le test de débit.
|
||||
if (!folderName || !fileName) {
|
||||
return speedtestStream(event)
|
||||
throw createError({ statusCode: 400, statusMessage: "Paramètres manquants" })
|
||||
}
|
||||
|
||||
if (!isSafeFolder(folderName) || !isSafeFile(fileName)) {
|
||||
|
||||
24
server/api/speedtest.get.ts
Normal file
24
server/api/speedtest.get.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { Readable } from "node:stream"
|
||||
|
||||
export default defineEventHandler((event) => {
|
||||
const size = 128 * 1024 * 1024
|
||||
let sent = 0
|
||||
|
||||
const stream = new Readable({
|
||||
read(chunkSize) {
|
||||
if (sent >= size) {
|
||||
this.push(null)
|
||||
return
|
||||
}
|
||||
|
||||
const remaining = size - sent
|
||||
const chunk = Buffer.alloc(Math.min(chunkSize, remaining), "a")
|
||||
sent += chunk.length
|
||||
this.push(chunk)
|
||||
}
|
||||
})
|
||||
|
||||
setHeader(event, "Content-Type", "application/octet-stream")
|
||||
setHeader(event, "Content-Length", size)
|
||||
return stream
|
||||
})
|
||||
195
server/plugins/metrics-worker.ts
Normal file
195
server/plugins/metrics-worker.ts
Normal file
@@ -0,0 +1,195 @@
|
||||
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)
|
||||
})
|
||||
Reference in New Issue
Block a user