diff --git a/public/index.html b/public/index.html index 8f25840..ade4f37 100644 --- a/public/index.html +++ b/public/index.html @@ -201,6 +201,15 @@ a{color:var(--blue);text-decoration:none;transition:color .2s}a:hover{color:var(
+ +
+
+ + RIPE Atlas Probes +
+
+
+
@@ -269,6 +278,7 @@ a{color:var(--blue);text-decoration:none;transition:color .2s}a:hover{color:var(
Network Overview
Announced Prefixes
RPKI Compliance
+
RIPE Atlas Probes
ASPA Status
bgproutes.io
AS Neighbours
@@ -604,6 +614,9 @@ function renderDashboard(d) { } $('rpkiContent').innerHTML = rk; + // Atlas Probes + renderAtlas(d.atlas); + // Neighbours let ne = '
'; ne += '
' + nb.total + '
Total
'; @@ -665,6 +678,54 @@ function renderDashboard(d) { $('facContent').innerHTML = fh; } + +function renderAtlas(atlas) { + if (!atlas) { + $('atlasContent').innerHTML = '
No RIPE Atlas data available.
'; + return; + } + + var h = ''; + + // Summary badges + h += '
'; + h += '
' + atlas.total_probes + '
Total Probes
'; + h += '
' + atlas.connected + '
Connected
'; + h += '
' + atlas.disconnected + '
Disconnected
'; + h += '
' + atlas.anchors + '
Anchors
'; + h += '
'; + + if (atlas.total_probes > 0) { + // Connection ratio bar + var connPct = pct(atlas.connected, atlas.total_probes); + h += '
Connected ' + connPct + '% / Disconnected ' + (100 - connPct) + '%
'; + h += '
'; + } + + if (atlas.probes && atlas.probes.length > 0) { + h += '
Show probe details (' + atlas.probes.length + (atlas.total_probes > atlas.probes.length ? ' of ' + atlas.total_probes : '') + ')
'; + h += '
'; + atlas.probes.forEach(function(p) { + var statusClass = p.status === 'Connected' ? 'badge-green' : 'badge-red'; + var anchorBadge = p.is_anchor ? 'Anchor' : '-'; + var prefix = p.prefix_v4 || p.prefix_v6 || '-'; + h += ''; + h += ''; + h += ''; + h += ''; + h += ''; + h += ''; + h += ''; + h += ''; + }); + h += '
IDStatusAnchorCountryPrefixDescription
' + p.id + '' + escHtml(p.status) + '' + anchorBadge + '' + countryFlag(p.country) + ' ' + escHtml(p.country) + '' + escHtml(prefix) + '' + escHtml(p.description) + '
'; + } else if (atlas.total_probes === 0) { + h += '
No RIPE Atlas probes found for this network. Host a probe?
'; + } + + $('atlasContent').innerHTML = h; +} + async function doCompare() { if (!currentAsn) return; const raw2 = $('compareAsn').value.trim().replace(/[^0-9]/g, ''); diff --git a/server.js b/server.js index b9a8f3e..00020a9 100644 --- a/server.js +++ b/server.js @@ -383,12 +383,13 @@ const server = http.createServer(async (req, res) => { const start = Date.now(); try { - const [pdbNet, prefixData, neighbourData, overviewData, rirData] = await Promise.all([ + const [pdbNet, prefixData, neighbourData, overviewData, rirData, atlasProbeData] = await Promise.all([ fetchJSON("https://www.peeringdb.com/api/net?asn=" + asn), fetchJSON("https://stat.ripe.net/data/announced-prefixes/data.json?resource=AS" + asn), fetchJSON("https://stat.ripe.net/data/asn-neighbours/data.json?resource=AS" + asn), fetchJSON("https://stat.ripe.net/data/as-overview/data.json?resource=AS" + asn), fetchJSON("https://stat.ripe.net/data/rir-stats-country/data.json?resource=AS" + asn), + fetchJSON("https://atlas.ripe.net/api/v2/probes/?asn_v4=" + asn + "&page_size=500"), ]); const net = pdbNet?.data?.[0] || {}; @@ -398,6 +399,11 @@ const server = http.createServer(async (req, res) => { const overview = overviewData?.data || {}; const rirEntries = rirData?.data?.located_resources || rirData?.data?.rir_stats || []; + // Atlas probes + const atlasProbes = atlasProbeData?.results || []; + const atlasConnected = atlasProbes.filter(p => p.status_name === "Connected"); + const atlasAnchors = atlasProbes.filter(p => p.is_anchor === true); + // Phase 2: IX + Facilities + RPKI (batched 20 at a time) const phase2Promises = []; if (netId) { @@ -531,6 +537,21 @@ const server = http.createServer(async (req, res) => { total: facilities.length, list: facilities, }, + atlas: { + total_probes: atlasProbes.length, + connected: atlasConnected.length, + disconnected: atlasProbes.length - atlasConnected.length, + anchors: atlasAnchors.length, + probes: atlasProbes.slice(0, 100).map(p => ({ + id: p.id, + status: p.status_name || p.status || "Unknown", + is_anchor: p.is_anchor || false, + country: p.country_code || "", + prefix_v4: p.prefix_v4 || "", + prefix_v6: p.prefix_v6 || "", + description: p.description || "", + })), + }, }; res.end(JSON.stringify(result, null, 2));