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.
49 lines
2.2 KiB
JavaScript
49 lines
2.2 KiB
JavaScript
const { fetchJSON } = require("../services/http-helpers");
|
|
const { fetchRipeStatCached } = require("../services/ripe-stat");
|
|
const { quickIxCacheGet, quickIxCacheSet } = require("../caches/response-caches");
|
|
|
|
// Quick-IX endpoint: /api/quick-ix?asn=X
|
|
// Lightweight: only IX connections from PeeringDB, 1h cached
|
|
// Used by Peering Recommendations to avoid 20x full lookups
|
|
async function handleQuickIx(req, res, url) {
|
|
const rawAsn = (url.searchParams.get("asn") || "").replace(/[^0-9]/g, "");
|
|
if (!rawAsn) {
|
|
res.writeHead(400);
|
|
return res.end(JSON.stringify({ error: "Missing asn parameter" }));
|
|
}
|
|
const cached = quickIxCacheGet(rawAsn);
|
|
if (cached !== undefined) {
|
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
return res.end(JSON.stringify(cached));
|
|
}
|
|
try {
|
|
const [pdbNetData, pdbIxlanData] = await Promise.all([
|
|
fetchJSON("https://www.peeringdb.com/api/net?asn=" + rawAsn + "&depth=0", { timeout: 5000 }).catch(() => null),
|
|
fetchJSON("https://www.peeringdb.com/api/netixlan?asn=" + rawAsn + "&limit=100", { timeout: 6000 }).catch(() => null),
|
|
]);
|
|
const netName = pdbNetData?.data?.[0]?.name || "";
|
|
const ixConnections = [];
|
|
if (pdbIxlanData && pdbIxlanData.data) {
|
|
pdbIxlanData.data.forEach((row) => {
|
|
ixConnections.push({ ix_id: row.ixlan_id, name: row.name || "", speed: row.speed || 0 });
|
|
});
|
|
}
|
|
// Fall back to RIPE Stat if PeeringDB returns nothing
|
|
if (ixConnections.length === 0) {
|
|
const rsStat = await fetchRipeStatCached("https://stat.ripe.net/data/ixs/data.json?resource=AS" + rawAsn, { timeout: 5000 }).catch(() => null);
|
|
const ixs = rsStat?.data?.ixs || [];
|
|
ixs.forEach((ix) => ixConnections.push({ ix_id: ix.ixp_id || 0, name: ix.name || "", speed: 0 }));
|
|
}
|
|
const result = { asn: parseInt(rawAsn), name: netName, ix_connections: ixConnections };
|
|
quickIxCacheSet(rawAsn, result);
|
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
return res.end(JSON.stringify(result));
|
|
} catch (err) {
|
|
res.writeHead(500);
|
|
return res.end(JSON.stringify({ error: "quick-ix lookup failed", message: err.message }));
|
|
}
|
|
|
|
}
|
|
|
|
module.exports = { handleQuickIx };
|