const localDb = require("../../local-db-client"); const { fetchTopology } = require("../topology"); // Feature 25: Topology endpoint async function handleTopology(req, res, url) { const rawAsn = (url.searchParams.get("asn") || "").replace(/[^0-9]/g, ""); const depth = parseInt(url.searchParams.get("depth") || "2") || 2; if (!rawAsn) { res.writeHead(400); return res.end(JSON.stringify({ error: "Missing or invalid ASN parameter" })); } const start = Date.now(); try { const topology = await fetchTopology(parseInt(rawAsn), depth); // ---- Threat Intelligence Enrichment for Topology Nodes ---- const threatMap = {}; const threatPromises = topology.nodes.slice(0, 100).map(async (node) => { try { const asNum = String(node.asn); const threat = await localDb.getThreatIntel(asNum); if (threat) { threatMap[node.asn] = { threat_level: threat.threat_level, confidence_score: threat.confidence_score, source: threat.source, }; } } catch (e) { console.error(`[Topology Threat Lookup] Error checking ASN ${node.asn}:`, e.message); } }); await Promise.race([ Promise.all(threatPromises), new Promise(r => setTimeout(r, 3000)), ]); // Attach threat status to node objects topology.nodes.forEach((node) => { if (threatMap[node.asn]) { node.threat_level = threatMap[node.asn].threat_level; node.threat_confidence = threatMap[node.asn].confidence_score; node.threat_source = threatMap[node.asn].source; } }); topology.meta = { query: "AS" + rawAsn, depth: depth, duration_ms: Date.now() - start, timestamp: new Date().toISOString(), node_count: topology.nodes.length, edge_count: topology.edges.length, }; return res.end(JSON.stringify(topology, null, 2)); } catch (err) { res.writeHead(500); return res.end(JSON.stringify({ error: "Topology query failed", message: err.message })); } } module.exports = { handleTopology };