const localDb = require("../../local-db-client"); const { bgproutesResultCache, resultCacheGet, resultCacheSet } = require("../caches/response-caches"); // BGP endpoint (LOCAL DB): /api/bgp?asn=X (or prefix=X) // Queries local PostgreSQL bgp_routes table — zero external API calls async function handleBgp(req, res, url) { const rawAsn = (url.searchParams.get("asn") || "").replace(/[^0-9]/g, ""); const prefix = url.searchParams.get("prefix") || ""; if (!rawAsn && !prefix) { res.writeHead(400); return res.end(JSON.stringify({ error: "Need asn or prefix parameter" })); } const cacheKey = rawAsn || prefix; const cached = resultCacheGet(bgproutesResultCache, cacheKey); if (cached !== undefined) { res.writeHead(200, { "Content-Type": "application/json", "X-Cache": "HIT" }); return res.end(JSON.stringify(cached)); } const start = Date.now(); try { const result = { meta: { timestamp: new Date().toISOString(), source: "local_bgp_db" }, bgp_status: null, threat_intel: null, }; // ---- BGP Status (local DB lookup) ---- if (prefix) { // Prefix lookup: Get BGP status for this prefix const bgpStatus = await localDb.getBgpStatus(prefix); if (bgpStatus) { result.bgp_status = { prefix, announced: bgpStatus.announced, origin_asns: bgpStatus.origin_asns, visibility_percent: bgpStatus.visibility_percent, last_seen: bgpStatus.last_seen, source: "local_bgp", }; // Check for hijack (multiple origin ASNs). null means the check itself // failed (DB error) -- distinct from a genuine single/no-origin result. const hijackAsns = await localDb.checkBgpHijack(prefix); if (hijackAsns === null) { result.bgp_status.hijack_check_unavailable = true; } else if (hijackAsns.length > 1) { result.bgp_status.hijack_warning = { detected: true, origin_asns: hijackAsns, message: `Multiple origin ASNs detected for ${prefix}`, }; } } } else if (rawAsn) { // ASN lookup: Get all announced prefixes for this ASN const prefixes = await localDb.getAnnouncedPrefixes(rawAsn); if (prefixes && prefixes.length > 0) { result.bgp_status = { asn: rawAsn, announced_count: prefixes.length, prefixes: prefixes.slice(0, 50).map((p) => ({ prefix: p.prefix, origin_asn: p.origin_asn, visibility_percent: p.visibility_percent, last_seen: p.last_seen, })), source: "local_bgp", }; } else { result.bgp_status = { asn: rawAsn, announced: false, announced_count: 0, message: "No prefixes found for this ASN in local BGP table", source: "local_bgp", }; } } // ---- Threat Intelligence (local cache lookup) ---- // If we have an IP context, look up threat intel if (prefix && prefix.includes(".")) { // Extract IP from prefix (e.g., "1.1.1.0/24" → "1.1.1.0") const ipAddr = prefix.split("/")[0]; const threat = await localDb.getThreatIntel(ipAddr); if (threat) { result.threat_intel = { ip_address: threat.ip_address, threat_level: threat.threat_level, confidence_score: threat.confidence_score, source: threat.source, cached_at: threat.cached_at, }; } } result.meta.duration_ms = Date.now() - start; resultCacheSet(bgproutesResultCache, cacheKey, result); res.writeHead(200, { "Content-Type": "application/json" }); return res.end(JSON.stringify(result, null, 2)); } catch (err) { console.error("[/api/bgp] Error:", err.message); res.writeHead(500); return res.end(JSON.stringify({ error: "BGP query failed", message: err.message })); } } module.exports = { handleBgp };