const fs = require("fs"); const https = require("https"); const path = require("path"); const { UA } = require("./data/constants"); const { PEERINGDB_API_KEY } = require("./services/peeringdb"); // Repo root -- NOT this module's own directory. See hijack-monitoring/store.js // for the same __dirname-fallback fix applied for the same reason. const REPO_ROOT = path.join(__dirname, ".."); // PeeringDB Org → Country Cache (for Lia's Paradise). Exposed via a mutable // state object (not a bare reassigned export) for the same reason as // atlas-probes.js's atlasState -- the Map itself is reassigned wholesale on // refresh, not just mutated in place. const pdbOrgState = { map: new Map() }; // org_id → { country, name } function fetchPdbOrgCountries() { var cacheFile = path.join(REPO_ROOT, ".pdb-org-cache.json"); // Try disk cache first (valid for 24h) try { var stat = fs.statSync(cacheFile); var ageHours = (Date.now() - stat.mtimeMs) / 3600000; if (ageHours < 24) { var cached = JSON.parse(fs.readFileSync(cacheFile, "utf8")); pdbOrgState.map = new Map(Object.entries(cached)); console.log("[PDB-ORG] Loaded " + pdbOrgState.map.size + " orgs from disk cache (" + Math.round(ageHours) + "h old)"); return Promise.resolve(); } } catch (_) { /* no cache or invalid */ } console.log("[PDB-ORG] Fetching PeeringDB org countries (fresh)..."); return new Promise(function(resolve) { var chunks = []; var req = https.get("https://www.peeringdb.com/api/org?status=ok&depth=0", { headers: { "User-Agent": UA, // Bug fix 2026-07-16: a bare `PEERINGDB_API_KEY ? ... : undefined` // sets the Authorization header value to `undefined` when no key is // configured. Node 22's stricter header validation throws // synchronously on that (ERR_HTTP_INVALID_HEADER_VALUE) instead of // omitting the header, crashing the whole process at startup via // this function's unhandled rejection (found empirically while // trying to boot server.js locally without a real PeeringDB key). // Conditionally spread the key in instead so the header key itself // is simply absent when unconfigured. ...(PEERINGDB_API_KEY ? { "Authorization": "Api-Key " + PEERINGDB_API_KEY } : {}), }, timeout: 120000, }, function(res) { if (res.statusCode !== 200) { console.error("[PDB-ORG] HTTP " + res.statusCode + " — using stale cache or empty"); resolve(); return; } res.on("data", function(chunk) { chunks.push(chunk); }); res.on("end", function() { try { var body = Buffer.concat(chunks).toString("utf8"); var data = JSON.parse(body); if (data && data.data) { pdbOrgState.map = new Map(); var cacheObj = {}; data.data.forEach(function(o) { if (o.id && o.country) { pdbOrgState.map.set(o.id, { country: o.country, name: o.name || "" }); cacheObj[o.id] = { country: o.country, name: o.name || "" }; } }); // Save to disk cache try { fs.writeFileSync(cacheFile, JSON.stringify(cacheObj)); } catch (_) {} console.log("[PDB-ORG] Loaded " + pdbOrgState.map.size + " org→country mappings (cached to disk)"); } } catch (e) { console.error("[PDB-ORG] Parse error:", e.message); } resolve(); }); }); req.on("error", function(e) { console.error("[PDB-ORG] Fetch error:", e.message); resolve(); }); req.on("timeout", function() { console.error("[PDB-ORG] Timeout after 120s"); req.destroy(); resolve(); }); }); } module.exports = { pdbOrgState, fetchPdbOrgCountries };