Rene Fichtmueller e2846dd8e8 fix: stop /api/health gating overall status on the vestigial local_db import pipeline
Traced local_db (bgp_routes/rpki_roas) 2026-07-17: two systemd import
timers on Erik have run "successfully" daily since creation (2026-04-28)
while doing zero real work -- bgp-importer's Route Views source URL is
invalid (parses 0 routes every run, the 36 rows are a one-time manual
seed), rpki-importer's feed URLs are both dead and it silently falls
back to 5 hardcoded fake ROAs re-upserted daily. The watchdog meant to
catch this has itself failed every run since creation (Postgres auth
error), so nothing ever alerted.

No user-facing endpoint reads from local_db -- only this health check
and the unused /api/bgp route do. Gating overall `status` on
bgp_routes/rpki_roas row counts meant /api/health reported "degraded"
permanently for a dependency nothing actually needs, which could mask
a real future degradation behind the noise. Still reports the raw
local_db stats (including its own `healthy` sub-field) for diagnostic
visibility; only removed it from the top-level status gate.

Verified live on Erik: status now reports "ok" when RPKI/ASPA freshness
is fine, instead of permanently "degraded".
2026-07-17 09:14:24 +02:00

112 lines
5.3 KiB
JavaScript

const localDb = require("../../local-db-client");
const { getRpkiAspaLastFetch, rpkiAspaMap } = require("../services/rpki");
const { pdbSourceCache } = require("../caches/pdb-source-cache");
const { roaStore } = require("../caches/roa-store");
const { ripeStatCache } = require("../services/ripe-stat");
const { responseCache } = require("../caches/response-caches");
const { aspaAdoptionDailyHistory } = require("../aspa-adoption/tracker");
const BGPROUTES_API_KEY = process.env.BGPROUTES_API_KEY || "";
function aspaAdoptionDelta() {
return aspaAdoptionDailyHistory.length >= 2
? aspaAdoptionDailyHistory[aspaAdoptionDailyHistory.length - 1].aspa_objects - aspaAdoptionDailyHistory[aspaAdoptionDailyHistory.length - 2].aspa_objects
: 0;
}
// Health endpoint — extended with cache status, ASPA metrics, and local DB stats
function handleHealth(req, res) {
const mem = process.memoryUsage();
const rpkiAspaLastFetch = getRpkiAspaLastFetch();
const aspaAge = rpkiAspaLastFetch ? Math.floor((Date.now() - rpkiAspaLastFetch) / 60000) : -1;
// Bug fix 2026-07-17: previously only declared inside the .then() callback
// below, so the .catch() branch's reference to it threw ReferenceError
// (never caught -- the response just hung until client timeout).
const roaAge = roaStore.lastBuild ? Math.floor((Date.now() - roaStore.lastBuild) / 60000) : -1;
const pdbTotal = pdbSourceCache.hits + pdbSourceCache.misses;
// Query local DB stats (async, but return partial if needed)
localDb.getLocalDbStats().then(function(dbStats) {
// hasLocalBgp/hasLocalRpki reflect a >2026-04 prototype (bgp_routes/
// rpki_roas import pipeline) confirmed vestigial 2026-07-17: both import
// timers have run "successfully" daily since creation while doing zero
// real work (dead source URL / silent hardcoded-fallback), and no
// user-facing endpoint reads from this local DB -- only this health
// check and the unused /api/bgp route do. Still reported below as
// diagnostic info, but no longer allowed to hold overall `status` at a
// permanent false "degraded" for a dependency nothing actually needs.
const hasLocalBgp = dbStats && dbStats.bgp_routes > 100000; // should have >2M rows normally
const hasLocalRpki = dbStats && dbStats.rpki_roas > 100000; // should have >500k rows normally
const status = aspaAge < 300 ? "ok" : "degraded";
const healthResponse = {
status,
service: "PeerCortex",
version: "0.6.9",
timestamp: new Date().toISOString(),
uptime_seconds: Math.floor(process.uptime()),
memory_mb: Math.round(mem.heapUsed / 1024 / 1024),
bgproutes_configured: !!BGPROUTES_API_KEY,
caches: {
aspa_map: { entries: rpkiAspaMap.size, age_minutes: aspaAge },
pdb_net: { entries: pdbSourceCache.net.size, hit_rate_pct: pdbTotal > 0 ? Math.round(pdbSourceCache.hits / pdbTotal * 100) : 0 },
pdb_netixlan: { entries: pdbSourceCache.netixlan.size },
pdb_netfac: { entries: pdbSourceCache.netfac.size },
ripe_stat: { entries: ripeStatCache.size },
response_cache: { entries: responseCache.size },
},
local_db: dbStats ? {
bgp_routes: dbStats.bgp_routes,
rpki_roas: dbStats.rpki_roas,
threat_intel: dbStats.threat_intel,
rdap_cache_entries: dbStats.rdap_cache_entries,
source: "PostgreSQL (local)",
healthy: hasLocalBgp && hasLocalRpki,
} : null,
aspa_adoption: {
total_objects: rpkiAspaMap.size,
roa_count: roaStore.count,
history_samples: aspaAdoptionDailyHistory.length,
delta_last: aspaAdoptionDelta(),
},
};
return res.end(JSON.stringify(healthResponse, null, 2));
}).catch(function(e) {
console.error('[/api/health] Local DB stats error:', e.message);
// Return health without local DB stats on error. Local DB stats failed
// to fetch, so "ok" can't be confirmed -- report degraded, matching the
// honest-degradation pattern used throughout this codebase.
const status = "degraded";
return res.end(
JSON.stringify({
status,
service: "PeerCortex",
version: "0.6.9",
timestamp: new Date().toISOString(),
uptime_seconds: Math.floor(process.uptime()),
memory_mb: Math.round(mem.heapUsed / 1024 / 1024),
bgproutes_configured: !!BGPROUTES_API_KEY,
caches: {
roa_store: { entries: roaStore.count, age_minutes: roaAge, ready: roaStore.ready },
aspa_map: { entries: rpkiAspaMap.size, age_minutes: aspaAge },
pdb_net: { entries: pdbSourceCache.net.size, hit_rate_pct: pdbTotal > 0 ? Math.round(pdbSourceCache.hits / pdbTotal * 100) : 0 },
pdb_netixlan: { entries: pdbSourceCache.netixlan.size },
pdb_netfac: { entries: pdbSourceCache.netfac.size },
ripe_stat: { entries: ripeStatCache.size },
response_cache: { entries: responseCache.size },
},
local_db: { error: "Could not fetch local DB stats", message: e.message },
aspa_adoption: {
total_objects: rpkiAspaMap.size,
roa_count: roaStore.count,
history_samples: aspaAdoptionDailyHistory.length,
delta_last: aspaAdoptionDelta(),
},
}, null, 2)
);
});
return;
}
module.exports = { handleHealth };