import https from "https"; const HOST = process.env.FORTIGATE_HOST; const TOKEN = process.env.FORTIGATE_API_TOKEN; const WAN1_IF = process.env.FORTIGATE_WAN1_INTERFACE; const WAN1_LABEL = process.env.FORTIGATE_WAN1_LABEL ?? WAN1_IF; const WAN2_IF = process.env.FORTIGATE_WAN2_INTERFACE; const WAN2_LABEL = process.env.FORTIGATE_WAN2_LABEL ?? WAN2_IF; const POLL_INTERVAL_MS = 30_000; const HISTORY_POINTS = 60; // 30 minutes at 30s intervals let history = []; let prevReading = null; // Use the https module directly so we can disable rejectUnauthorized — // FortiGate management interface uses a self-signed cert. function fetchJson(urlStr, headers) { return new Promise((resolve, reject) => { const url = new URL(urlStr); const req = https.request( { hostname: url.hostname, port: url.port || 443, path: url.pathname + url.search, method: "GET", headers, rejectUnauthorized: false, }, (res) => { let body = ""; res.on("data", (chunk) => (body += chunk)); res.on("end", () => { if (res.statusCode < 200 || res.statusCode >= 300) { reject(new Error(`HTTP ${res.statusCode}`)); } else { try { resolve(JSON.parse(body)); } catch (e) { reject(new Error("Invalid JSON in response")); } } }); } ); req.on("error", reject); req.end(); }); } function getIfBytes(data, ifname) { const results = data?.results; if (!results) return null; // FortiOS may return results as an object keyed by interface name or as an array const iface = Array.isArray(results) ? results.find((i) => i.name === ifname) : results[ifname]; if (!iface) return null; return { rx: iface.rx_bytes ?? 0, tx: iface.tx_bytes ?? 0 }; } async function poll() { const data = await fetchJson( `https://${HOST}/api/v2/monitor/system/interface`, { Authorization: `Bearer ${TOKEN}` } ); const now = Date.now(); const wan1 = getIfBytes(data, WAN1_IF); const wan2 = getIfBytes(data, WAN2_IF); if (!wan1 || !wan2) { console.warn("[throughput] Interface not found in FortiGate response — check interface names in .env"); return; } if (prevReading) { const elapsed = (now - prevReading.timestamp) / 1000; // seconds const mbps = (curr, prev) => Math.max(0, ((curr - prev) * 8) / elapsed / 1_000_000); const point = { timestamp: now, wan1: { label: WAN1_LABEL, rxMbps: mbps(wan1.rx, prevReading.wan1.rx), txMbps: mbps(wan1.tx, prevReading.wan1.tx), }, wan2: { label: WAN2_LABEL, rxMbps: mbps(wan2.rx, prevReading.wan2.rx), txMbps: mbps(wan2.tx, prevReading.wan2.tx), }, }; history.push(point); if (history.length > HISTORY_POINTS) history.shift(); } prevReading = { timestamp: now, wan1, wan2 }; } export function getHistory() { return history; } export function startPolling() { if (!HOST || !TOKEN) { console.warn("[throughput] FORTIGATE_HOST or FORTIGATE_API_TOKEN not set — throughput polling disabled."); return; } poll().catch((err) => console.error("[throughput] Initial poll failed:", err.message) ); setInterval( () => poll().catch((err) => console.error("[throughput] Poll failed:", err.message) ), POLL_INTERVAL_MS ); }