diff --git a/src/server/api/system.ts b/src/server/api/system.ts index 5a70175..c1db948 100644 --- a/src/server/api/system.ts +++ b/src/server/api/system.ts @@ -103,7 +103,7 @@ let _appDiskCache: Record = {} let _appDiskLastUpdate = 0 const DISK_CACHE_TTL = 30000 -function getAppMetrics(): Record { +async function getAppMetrics(): Promise> { const apps = allApps() const running = apps.filter(a => a.proc?.pid) const result: Record = {} @@ -117,8 +117,10 @@ function getAppMetrics(): Record { if (pidToName.size > 0) { try { const pids = [...pidToName.keys()].join(',') - const ps = Bun.spawnSync(['ps', '-o', 'pid=,%cpu=,rss=', '-p', pids]) - for (const line of ps.stdout.toString().split('\n')) { + const proc = Bun.spawn(['ps', '-o', 'pid=,%cpu=,rss=', '-p', pids], { stdout: 'pipe', stderr: 'ignore' }) + const output = await new Response(proc.stdout).text() + await proc.exited + for (const line of output.split('\n')) { const parts = line.trim().split(/\s+/) if (parts.length < 3) continue const pid = parseInt(parts[0]!, 10) @@ -135,12 +137,21 @@ function getAppMetrics(): Record { if (now - _appDiskLastUpdate > DISK_CACHE_TTL) { _appDiskLastUpdate = now _appDiskCache = {} - for (const app of apps) { - try { - const du = Bun.spawnSync(['du', '-sk', join(APPS_DIR, app.name)]) - const kb = parseInt(du.stdout.toString().trim().split('\t')[0]!, 10) - if (kb) _appDiskCache[app.name] = kb * 1024 - } catch {} + const duResults = await Promise.all( + apps.map(async app => { + try { + const proc = Bun.spawn(['du', '-sk', join(APPS_DIR, app.name)], { stdout: 'pipe', stderr: 'ignore' }) + const output = await new Response(proc.stdout).text() + await proc.exited + const kb = parseInt(output.trim().split('\t')[0]!, 10) + return { name: app.name, bytes: kb ? kb * 1024 : 0 } + } catch { + return { name: app.name, bytes: 0 } + } + }) + ) + for (const { name, bytes } of duResults) { + if (bytes) _appDiskCache[name] = bytes } } @@ -154,26 +165,30 @@ function getAppMetrics(): Record { } // Get current system metrics -router.get('/metrics', c => { +router.get('/metrics', async c => { const metrics: SystemMetrics = { cpu: getCpuUsage(), ram: getMemoryUsage(), disk: getDiskUsage(), - apps: getAppMetrics(), + apps: await getAppMetrics(), } return c.json(metrics) }) // SSE stream for real-time metrics (updates every 2s) router.sse('/metrics/stream', (send) => { + let queue = Promise.resolve() + const sendMetrics = () => { - const metrics: SystemMetrics = { - cpu: getCpuUsage(), - ram: getMemoryUsage(), - disk: getDiskUsage(), - apps: getAppMetrics(), - } - send(metrics) + queue = queue.then(async () => { + const metrics: SystemMetrics = { + cpu: getCpuUsage(), + ram: getMemoryUsage(), + disk: getDiskUsage(), + apps: await getAppMetrics(), + } + await send(metrics) + }) } // Initial send