Same "Show all N / Show fewer" pattern as the Wardriving tab's
Sessions/Entry Points sections -- both lists on Foreign Traffic can
grow long on a busy network, dwarfing the rest of the tab.
User report: "Hvorfor er der bornholm repeater under Foreign Traffic
igen!?" — this exact bug was already fixed once this session (commit
c4b4453), but that fix only ever existed on foreign-traffic-wip and
was never merged into master. wardriving-analytics branched from
master, so it never had the fix, and every stg deploy of this branch
today silently reintroduced it.
Reapplies c4b4453's fix: renderForeignTrafficTab now classifies
"foreign" live from each node's own lat/lon via nodePassesGeoFilter +
window.MC_GEO_FILTER (same mechanism the Nodes page already uses),
instead of trusting the node's `foreign` DB flag — a one-way flag
written once by the ingestor's MarkNodeForeign and never cleared, so
a node flagged foreign under an old, narrower geo_filter box stays
flagged forever even after the box widens to include it.
Note: while investigating, found that foreign-traffic-wip also has
entire Foreign Traffic tab features (Entry Points, Distance vs Hop
Count sections) that never reached master either — out of scope for
this specific fix, worth a separate look.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- MAJOR: config.example.json was missing geoFilterExemptNodeList
entirely — every other Config field in this repo gets a sibling
_comment_<field> entry (established, actively-used convention
throughout the file, ~20 existing examples), documented here to
match. Also corrected the adjacent geo_filter _comment, which
predates this PR and claimed it "restricts ingestion" — it doesn't;
that's foreignAdverts.mode=drop's job, unrelated to this flag.
- MINOR: ?geoFilter=<v> only recognized the literal string "1" to
enable and treated anything else (including "true") as disabling —
a real footgun since "true" reads as an obviously valid boolean
value. Now accepts 1/true and 0/false as explicit overrides; any
other value (including absent) falls through to the deployment
default instead of silently flipping it off.
- Tightened the Foreign Traffic tab's stop-hook test: it previously
only asserted stop() doesn't throw, which a no-op stub would also
pass. Sandbox's setInterval/clearInterval are now real spies
tracking live/cleared ids, so the test verifies render() registers
exactly one interval and stop() actually clears that exact id
(idempotently on a second call).
- NIT: relays.sort now wraps unscoped_relay_count_24h in Number(...)
defensively (AGENTS.md § Type Safety — cast at the boundary).
- NIT: foreignNodes.sort now parses last_seen once per node
(decorate-sort-undecorate) instead of twice per comparator call.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Reordered so the node list (what's actually accumulating right now,
worth checking back on) comes first, with the unscoped-relay-volume
table — the part that needs more data to be interesting — below it.
Added a heading to the relay table for symmetry with the new
sub-heading above it.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds a second table to the tab: every node currently foreign_advert
-tagged (name, role, lat/lon, first/last seen), newest-heard first —
so there's something to look at while more foreign_advert data
accumulates, instead of only the one-sentence summary count.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Per bot review on PR #1852 (comment 5012304813): renderForeignTrafficTab
shipped without a test, repeating the same TDD-policy finding flagged
on the prior round, and the tab lacked the 60s auto-refresh + stop-hook
convention its Roles/Scopes siblings use (data would go stale until the
user re-navigated away and back).
- Added _stopForeignTrafficRefresh + a 60s setInterval loop, wired into
the tab-switch handler and destroy(), matching _stopRolesRefresh/
_stopScopesRefresh exactly.
- New test-analytics-foreign-traffic-tab.js: loads analytics.js in a vm
sandbox (same pattern as test-frontend-helpers.js's
makeAnalyticsSandbox), stubs fetchAllNodes with a fixed node mix, and
asserts row sort order, role/zero-count exclusion, the empty state,
both foreignCount note variants, and that the new stop-hook is
exported and idempotent.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>