import { Router } from 'express' import axios from 'axios' const router = Router() const HISTORY_SIZE = 20 const history: { ts: number; rx: number; tx: number }[] = [] function soap(action: string, service: string, body = ''): string { return `${body}` } function tag(xml: string, name: string): string { const m = xml.match(new RegExp(`<(?:[^:>]*:)?${name}>([^<]*)<`)) return m?.[1]?.trim() ?? '' } async function soapReq(host: string, path: string, service: string, action: string): Promise { const res = await axios.post(`${host}:49000${path}`, soap(action, service), { headers: { 'Content-Type': 'text/xml; charset="utf-8"', SOAPAction: `"${service}#${action}"`, }, timeout: 5000, }) return res.data as string } router.get('/status', async (_req, res) => { try { const host = process.env.FRITZBOX_HOST if (!host) { res.status(503).json({ error: 'FRITZBOX_HOST not configured' }) return } const [addonXml, ipXml, statusXml] = await Promise.all([ soapReq(host, '/igdupnp/control/WANCommonIFC1', 'urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1', 'GetAddonInfos'), soapReq(host, '/igdupnp/control/WANIPConn1', 'urn:schemas-upnp-org:service:WANIPConnection:1', 'GetExternalIPAddress'), soapReq(host, '/igdupnp/control/WANIPConn1', 'urn:schemas-upnp-org:service:WANIPConnection:1', 'GetStatusInfo'), ]) // Bytes/sec — FritzBox provides instantaneous rates const rxRate = Number(tag(addonXml, 'NewByteReceiveRate') || tag(addonXml, 'NewBytesReceiveRate') || '0') const txRate = Number(tag(addonXml, 'NewByteSendRate') || tag(addonXml, 'NewBytesSendRate') || '0') history.push({ ts: Date.now(), rx: rxRate, tx: txRate }) if (history.length > HISTORY_SIZE) history.shift() res.json({ connected: tag(statusXml, 'NewConnectionStatus') === 'Connected', externalIp: tag(ipXml, 'NewExternalIPAddress'), rxMbps: parseFloat((rxRate * 8 / 1_000_000).toFixed(2)), txMbps: parseFloat((txRate * 8 / 1_000_000).toFixed(2)), history: history.map(h => ({ rx: h.rx, tx: h.tx })), }) } catch (err: unknown) { const msg = err instanceof Error ? err.message : 'Unknown error' res.status(500).json({ error: msg }) } }) export default router