PeerCortex/server/ipv6-adoption.js
Rene Fichtmueller 7701edd657 refactor: extract ipv6-adoption.js and pdf-export/{templates,renderer}.js
templates.js and renderer.js content extracted via direct line-slicing
(not retyped) given their size (~660-line HTML/CSS template functions) --
avoids transcription risk on template-literal-heavy code.

Verified via smoke-test harness (28/28 match). server.js: 4492 -> 3784 lines
(6838 originally -- 45% reduction so far).
2026-07-17 00:09:10 +02:00

81 lines
3.1 KiB
JavaScript

const { RIR_DELEGATION_URLS } = require("./data/rir-delegation-urls");
// FEATURE 4: IPv6 Adoption per RIR
// In-memory cache: { data, ts }
let ipv6StatsCache = null;
const IPV6_STATS_TTL = 24 * 60 * 60 * 1000; // 24h
// Fetch and parse one RIR delegation file.
// Returns { ipv4, ipv6, asns } count objects.
function fetchRirDelegation(rir, url) {
return new Promise((resolve) => {
const result = { rir, ipv4: 0, ipv6: 0, asns: 0, ipv4_countries: new Set(), ipv6_countries: new Set() };
const mod = url.startsWith('https') ? require('https') : require('http');
const req = mod.get(url, { headers: { 'User-Agent': 'PeerCortex/1.0 (+https://peercortex.org)' }, timeout: 20000 }, res => {
if (res.statusCode !== 200) { resolve(result); return; }
let buf = '';
res.on('data', d => { buf += d; });
res.on('end', () => {
for (const line of buf.split('\n')) {
if (line.startsWith('#') || line.startsWith(rir.toLowerCase() + '|*') || !line.includes('|')) continue;
const parts = line.split('|');
if (parts.length < 7) continue;
const type = parts[2];
const status = parts[6].trim();
const cc = parts[1];
if (status !== 'allocated' && status !== 'assigned') continue;
if (type === 'ipv4') { result.ipv4++; result.ipv4_countries.add(cc); }
else if (type === 'ipv6') { result.ipv6++; result.ipv6_countries.add(cc); }
else if (type === 'asn') { result.asns++; }
}
resolve(result);
});
});
req.on('error', () => resolve(result));
req.on('timeout', () => { req.destroy(); resolve(result); });
});
}
// Fetch all 5 RIR delegation files and aggregate IPv6 stats.
// Cached for 24 hours.
async function fetchIpv6AdoptionStats(force) {
if (!force && ipv6StatsCache && (Date.now() - ipv6StatsCache.ts) < IPV6_STATS_TTL) {
return ipv6StatsCache.data;
}
console.log('[IPv6] Fetching RIR delegation stats...');
const results = await Promise.all(
Object.entries(RIR_DELEGATION_URLS).map(([rir, url]) => fetchRirDelegation(rir, url))
);
const rirs = results.map(r => ({
rir: r.rir,
ipv4_records: r.ipv4,
ipv6_records: r.ipv6,
asn_records: r.asns,
ipv6_pct: (r.ipv4 + r.ipv6) > 0
? Math.round((r.ipv6 / (r.ipv4 + r.ipv6)) * 10000) / 100
: 0,
countries_with_ipv6: r.ipv6_countries.size,
countries_with_ipv4: r.ipv4_countries.size,
}));
const total_ipv4 = rirs.reduce((s, r) => s + r.ipv4_records, 0);
const total_ipv6 = rirs.reduce((s, r) => s + r.ipv6_records, 0);
const data = {
fetched_at: new Date().toISOString(),
global_ipv6_pct: total_ipv4 + total_ipv6 > 0
? Math.round((total_ipv6 / (total_ipv4 + total_ipv6)) * 10000) / 100
: 0,
total_ipv4_records: total_ipv4,
total_ipv6_records: total_ipv6,
by_rir: rirs,
};
ipv6StatsCache = { data, ts: Date.now() };
console.log(`[IPv6] Done. Global IPv6%: ${data.global_ipv6_pct}% across ${rirs.length} RIRs`);
return data;
}
// Pre-fetch on startup (non-blocking)
fetchIpv6AdoptionStats().catch(() => {});
module.exports = { fetchRirDelegation, fetchIpv6AdoptionStats };