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
41 lines
1.3 KiB
TypeScript
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 }
|
|
}
|