9 Commits

Author SHA1 Message Date
Rene Fichtmueller
50dc4e6b6a fix: network.country was always empty -- both its data sources were dead stubs
All checks were successful
build-verify / build-verify (push) Successful in 25s
/api/lookup's deriveRirAndCountry() can only populate country from two
inputs: rirEntries (RIPE Stat rir-stats-country) and bgpHeData.country_code
(bgp.he.net). Both of their fetches in the Phase 1 Promise.all are
hardcoded to Promise.resolve(null) (see the timedFetch("RIPE Stat RIR", ...)
and timedFetch("bgp.he.net", ...) lines), so country has been unpopulated
for every single lookup regardless of ASN. rir stayed populated separately
via the RDAP port43/links fallback, which masked the issue -- rir looked
fine, country silently didn't.

Confirmed via today's rotating_audit.py run: 100/100 sampled ASNs flagged
country_empty.

Fix: fall back to PeeringDB's org-level country (pdbOrgState, loaded at
startup from /api/org, already used successfully by lia.js's coverage
endpoint for the same purpose) when the RIR-derived country is empty.
Does not touch the two dead stubs -- those look like deliberate stand-ins
for sources that were never wired up, out of scope here.

Verified live post-deploy: AS680 DE, AS847 US, AS1251 BR now populated
(previously all three were empty). AS4628 still empty -- its PeeringDB org
itself has no country set, a genuine upstream data gap, not something this
fallback can fix. npm test: 215/215 passing.
2026-07-18 07:03:06 +00:00
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
Rene Fichtmueller
570c178c61 refactor: extract proxy-stubs/changelog/hijack-alerts/pdf-export/aspa-adoption routes
Some checks failed
build-verify / build-verify (push) Failing after 10s
build-verify / build-verify (pull_request) Failing after 10s
Final Phase C batch: moves the remaining inline route bodies (12 proxy
stubs, /changelog, the webhooks/hijacks group, PDF export, ASPA adoption
tracker + IPv6 stats endpoints) into server/routes/*.js and
server/services/api-proxy.js, verified byte-for-byte via the smoke-test
diff. Also drops the now-duplicate proxyToApiServer()/API_SERVER_PORT
that were left behind in server.js after api-proxy.js absorbed them.

server.js: 6838 -> 578 lines, under the 800-line project limit.
2026-07-17 08:08:48 +02:00
Rene Fichtmueller
49aa46fef3 refactor: extract relationships/compare/quick-ix/peers-find/prefix-detail/ix-detail/topology/whois/enrich routes
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.
2026-07-17 07:57:04 +02:00
Rene Fichtmueller
cba7680de0 refactor: decompose /api/lookup into server/routes/lookup/{index,facilities,enrich-neighbours,rir-country,routing-info,cross-check}.js
The 662-line handler (the largest in the file) split into one function
per data-source/merge stage: facility+IX-location coordinate resolution
(facilities.js), neighbour name-resolution + threat-intel enrichment
(enrich-neighbours.js), RIR/country derivation cascade (rir-country.js),
visibility/prefix-size computation (routing-info.js), multi-source
cross-checks + data-quality scoring (cross-check.js), and a slim
orchestrator (index.js) running Phase 0/1 fetch and final result assembly.

Verified via node --check, eslint no-undef (2 known bugs remain: compare,
webhooks), and smoke-test harness (28/28 match, including a byte-for-byte
match on /api/lookup's own large response body -- the strongest possible
confirmation this, the most complex handler in the file, was decomposed
without changing behavior).

server.js: 2917 -> 1734 lines (6838 originally -- 75% reduction so far).
2026-07-17 07:52:22 +02:00
Rene Fichtmueller
144457f5d2 refactor: decompose /api/validate into server/routes/validate/{checks,bogon,index}.js
The 519-line handler split into 12 independent, named check functions
(checks.js), the bogon-detection nested functions pulled out on their own
(bogon.js, no I/O, easiest to verify), and a slim orchestrator (index.js)
that runs Phase 1 fetch, dispatches all checks via Promise.race+allSettled
(same 5s-cap/10s-total behavior as before), scores, and assembles the
response -- each piece under 50 lines except the orchestrator's main
function itself.

Verified via node --check, eslint no-undef (2 known bugs remain: compare,
webhooks), and smoke-test harness (28/28 match, including a byte-for-byte
match on /api/validate's own response -- the highest-value confirmation
this decomposition preserved behavior exactly).
2026-07-17 07:46:05 +02:00
Rene Fichtmueller
601427b4af refactor: extract aspa-verify.js, aspa-legacy.js, bgp.js route handlers
Verified via node --check, eslint no-undef (2 known bugs remain: compare,
webhooks), and smoke-test harness (28/28 match).
2026-07-17 07:40:10 +02:00
Rene Fichtmueller
b82c61e03c refactor: extract lia.js and health.js routes, fix /api/health closure bug
Bug fix (agreed pre-existing bug #3 of 3): the .catch() branch of
localDb.getLocalDbStats() referenced status/roaAge, both only declared
inside the sibling .then() callback's closure -- out of scope in .catch(),
so any real DB-stats failure threw ReferenceError with no further catch,
hanging the response until client timeout instead of degrading gracefully.
Fixed: roaAge computed once before the promise chain (same pattern as the
existing aspaAge); the .catch() branch gets its own locally-scoped
`status = "degraded"` (a DB-stats fetch failure can't confirm "ok",
matching the honest-degradation pattern already used throughout this
codebase). Also deduplicated the identical delta_last computation that
was repeated verbatim in both branches into one aspaAdoptionDelta() helper.

Verified via node --check, eslint no-undef (2 known bugs remain: compare,
webhooks), and smoke-test harness (28/28 match). server.js: 3556 -> 3346 lines.
2026-07-17 07:36:10 +02:00
Rene Fichtmueller
3399392f1f refactor: extract static/search/feedback route handlers
server/routes/static.js (/, /index.html, /index-editorial.html,
/favicon.ico, /lia), server/routes/search.js (/api/search, /api/visitors),
server/routes/feedback.js (/api/feedback OPTIONS/POST/GET).

Verified via node --check, eslint no-undef (only the 3 known pre-existing
bugs remain, no new ones), and smoke-test harness (28/28 match).
server.js: 3714 -> 3556 lines.
2026-07-17 07:31:19 +02:00