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.
60 lines
2.0 KiB
JavaScript
60 lines
2.0 KiB
JavaScript
const { fetchPeeringDB } = require("../services/peeringdb");
|
|
|
|
// IX Detail endpoint: /api/ix/detail?ix_id=X
|
|
async function handleIxDetail(req, res, url) {
|
|
const ixId = (url.searchParams.get("ix_id") || "").replace(/[^0-9]/g, "");
|
|
if (!ixId) {
|
|
res.writeHead(400);
|
|
return res.end(JSON.stringify({ error: "Missing ix_id parameter" }));
|
|
}
|
|
const start = Date.now();
|
|
try {
|
|
const [ixData, ixlanData] = await Promise.all([
|
|
fetchPeeringDB("/ix/" + ixId),
|
|
fetchPeeringDB("/ixlan?ix_id=" + ixId),
|
|
]);
|
|
|
|
const ix = ixData?.data?.[0] || {};
|
|
const ixlans = ixlanData?.data || [];
|
|
const ixlanId = ixlans.length > 0 ? ixlans[0].id : null;
|
|
|
|
let members = [];
|
|
if (ixlanId) {
|
|
const netixlanData = await fetchPeeringDB("/netixlan?ixlan_id=" + ixlanId);
|
|
members = (netixlanData?.data || []).map(m => ({
|
|
asn: m.asn,
|
|
name: m.name || "",
|
|
speed_mbps: m.speed || 0,
|
|
speed_display: (m.speed || 0) >= 1000 ? ((m.speed || 0) / 1000) + " Gbps" : (m.speed || 0) + " Mbps",
|
|
ipv4: m.ipaddr4 || null,
|
|
ipv6: m.ipaddr6 || null,
|
|
}));
|
|
}
|
|
|
|
// Sort by speed desc for top members
|
|
const sorted = members.slice().sort((a, b) => b.speed_mbps - a.speed_mbps);
|
|
|
|
const duration = Date.now() - start;
|
|
return res.end(JSON.stringify({
|
|
meta: { duration_ms: duration, timestamp: new Date().toISOString() },
|
|
ix: {
|
|
id: parseInt(ixId),
|
|
name: ix.name || "",
|
|
city: ix.city || "",
|
|
country: ix.country || "",
|
|
website: ix.website || "",
|
|
peeringdb_url: "https://www.peeringdb.com/ix/" + ixId,
|
|
},
|
|
total_members: members.length,
|
|
top_members_by_speed: sorted.slice(0, 20),
|
|
all_members: sorted,
|
|
}, null, 2));
|
|
} catch (err) {
|
|
res.writeHead(500);
|
|
return res.end(JSON.stringify({ error: "IX detail failed", message: err.message }));
|
|
}
|
|
|
|
}
|
|
|
|
module.exports = { handleIxDetail };
|