PeerCortex/server/routes/lookup/rir-country.js
Rene Fichtmueller cba7680de0 refactor: decompose /api/lookup into server/routes/lookup/{index,facilities,enrich-neighbours,rir-country,routing-info,cross-check}.js
The 662-line handler (the largest in the file) split into one function
per data-source/merge stage: facility+IX-location coordinate resolution
(facilities.js), neighbour name-resolution + threat-intel enrichment
(enrich-neighbours.js), RIR/country derivation cascade (rir-country.js),
visibility/prefix-size computation (routing-info.js), multi-source
cross-checks + data-quality scoring (cross-check.js), and a slim
orchestrator (index.js) running Phase 0/1 fetch and final result assembly.

Verified via node --check, eslint no-undef (2 known bugs remain: compare,
webhooks), and smoke-test harness (28/28 match, including a byte-for-byte
match on /api/lookup's own large response body -- the strongest possible
confirmation this, the most complex handler in the file, was decomposed
without changing behavior).

server.js: 2917 -> 1734 lines (6838 originally -- 75% reduction so far).
2026-07-17 07:52:22 +02:00

52 lines
2.3 KiB
JavaScript

const { ARIN_CC, APNIC_CC, LACNIC_CC, AFRINIC_CC } = require("../../data/rir-country-codes");
// Derive RIR and country through a cascade of fallback sources: RIPE Stat
// rir-stats-country, RDAP port43, RDAP self-link, bgp.he.net country_code,
// then a country-code-to-RIR table as a last resort.
function deriveRirAndCountry(rirEntries, rirData, rdapData, bgpHeData) {
let rir = "";
let country = "";
// RIPE Stat rir-stats-country uses 'location' field (not 'country' or 'rir')
if (Array.isArray(rirEntries) && rirEntries.length > 0) {
country = rirEntries[0]?.location || rirEntries[0]?.country || "";
rir = rirEntries[0]?.rir || "";
}
if (!rir && rirData?.data) {
const rirField = rirData.data.rirs || [];
if (rirField.length > 0) rir = rirField[0]?.rir || "";
}
// Derive RIR from rdapData.port43 (e.g. "whois.ripe.net" → "RIPE")
if (!rir && rdapData && rdapData.port43) {
const p43 = (rdapData.port43 || "").toLowerCase();
if (p43.includes("ripe")) rir = "RIPE";
else if (p43.includes("arin")) rir = "ARIN";
else if (p43.includes("apnic")) rir = "APNIC";
else if (p43.includes("lacnic")) rir = "LACNIC";
else if (p43.includes("afrinic")) rir = "AFRINIC";
}
// Also derive RIR from RDAP links (URL of the RDAP endpoint that responded)
if (!rir && rdapData && rdapData.links) {
const selfLink = (rdapData.links.find(l => l.rel === "self") || {}).href || "";
if (selfLink.includes("ripe")) rir = "RIPE";
else if (selfLink.includes("arin")) rir = "ARIN";
else if (selfLink.includes("apnic")) rir = "APNIC";
else if (selfLink.includes("lacnic")) rir = "LACNIC";
else if (selfLink.includes("afrinic")) rir = "AFRINIC";
}
// bgp.he.net country_code fallback (for unannounced/reserve ASNs)
if (!country && bgpHeData && bgpHeData.country_code) {
country = bgpHeData.country_code;
}
// Last resort: derive RIR from country code (common assignments)
if (!rir && country) {
if (ARIN_CC.has(country)) rir = "ARIN";
else if (APNIC_CC.has(country)) rir = "APNIC";
else if (LACNIC_CC.has(country)) rir = "LACNIC";
else if (AFRINIC_CC.has(country)) rir = "AFRINIC";
else rir = "RIPE"; // Europe + rest = RIPE NCC
}
return { rir, country };
}
module.exports = { deriveRirAndCountry };