dev_arc_aws/backend/src/ssh/metrics/network.ts
Claude f32d93947b
Add host metrics widgets (Phase 6): CPU/mem/disk/network/processes/ports/firewall/login dashboard
Ports Termix's per-host metrics collector logic onto ArchNest's own SSH
connection helpers (not its multi-user/cache/session scaffolding), exposed via
a new authenticated REST endpoint and a dedicated /host-metrics page with
client-side polling.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BbJV5nm8KPVH1oNJYKpnoF
2026-06-19 15:38:30 +00:00

41 lines
1.3 KiB
TypeScript

import type { Client } from 'ssh2'
import { execCommand } from './common.js'
export async function collectNetworkMetrics(
client: Client,
): Promise<{ interfaces: Array<{ name: string; ip: string; state: string }> }> {
const interfaces: Array<{ name: string; ip: string; state: string }> = []
try {
const [addrOut, linkOut] = await Promise.all([
execCommand(client, "ip -o addr show | awk '{print $2,$4}' | grep -v '^lo'"),
execCommand(client, "ip -o link show | awk '{gsub(/:/,\"\",$2); print $2,$9}'"),
])
const ipByIface = new Map<string, string>()
for (const line of addrOut.stdout.split('\n')) {
const [iface, addr] = line.trim().split(/\s+/)
if (iface && addr) ipByIface.set(iface, addr)
}
const stateByIface = new Map<string, string>()
for (const line of linkOut.stdout.split('\n')) {
const [iface, state] = line.trim().split(/\s+/)
if (iface) stateByIface.set(iface, state ?? 'UNKNOWN')
}
const names = new Set([...ipByIface.keys(), ...stateByIface.keys()])
for (const name of names) {
if (name === 'lo' || !name) continue
interfaces.push({
name,
ip: ipByIface.get(name) ?? '',
state: stateByIface.get(name) ?? 'UNKNOWN',
})
}
} catch {
// best-effort
}
return { interfaces }
}