PeerCortex/server/routes/topology.js
Rene Fichtmueller 49aa46fef3 refactor: extract relationships/compare/quick-ix/peers-find/prefix-detail/ix-detail/topology/whois/enrich routes
Bug fix (agreed pre-existing bug #2 of 3): /api/compare called
fetchRPKIPerPrefix(asn, prefix), never defined anywhere in the codebase --
threw ReferenceError whenever either ASN had >=1 sample prefix, caught by
the outer try/catch, returning 500. Replaced both call sites with
validateRPKIWithCache(asn, prefix), the existing single-prefix RPKI check
used everywhere else in the codebase -- its return shape ({status, ...})
is exactly what the surrounding code already expects.

Verified via node --check, eslint no-undef (only 1 known bug remains:
webhooks POST's `secret`), and smoke-test harness (28/28 match). The
compare fix itself isn't exercised by the smoke test's fixed test ASNs
(both return empty prefix samples locally), but the fix is a direct,
same-shape substitution of an already-proven function.

server.js: 1736 -> 989 lines.
2026-07-17 07:57:04 +02:00

60 lines
2.1 KiB
JavaScript

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 };