Files
meshcore-analyzer/public
Kpa-clawbotandopenclaw-bot 26105748ff fix(nodes): paginate /api/nodes — surface all nodes past 500-row server cap (#1606) (#1607)
## Summary

Fixes #1606 — frontend `public/nodes.js` issued a single `?limit=5000`
fetch to `/api/nodes` and trusted the response as the complete node set.
After PR #1540 (v3.8.3) clamped `/api/nodes` `?limit` to 500 as a DoS
guard, that single fetch silently truncated to the top 500 rows by
`last_seen DESC`. On the reporter's 2313-node deployment, **78% of nodes
(1813) were invisible** in the Nodes page, with no UI indication
anything was missing.

Replaces the single fetch in `loadNodes()` with a pagination loop driven
by `data.total` from the first response. Stops when `_allNodes.length >=
total`, when the server returns a short page, or at a 10 000-row safety
cap. `counts` is taken from the first response and refreshed on each
subsequent page (last writer wins; the server returns the same `counts`
payload each call).

Scope is deliberately narrow per the (munger) finding in the triage
comment: the three sibling call sites (`analytics.js:2080,2817`,
`packets.js:791`) are **NOT** touched here. They get their own
follow-up.

## Repro

```bash
curl -s "https://analyzer.marwoj.net/api/nodes?limit=5000" | jq '{nodes_len: (.nodes | length), total}'
# Before fix on >500-node deployment:
#   { "nodes_len": 500, "total": 2313 }   ← frontend silently displays only 500
```

## Before / after evidence

Unit test `test-issue-1606-pagination.js` drives `loadNodes()` against a
mocked `api()` exposing 1200 fixture nodes with a 500-per-page server
cap (mirrors the real `/api/nodes` clamp).

| | `_allNodes.length` | `data.total` |
|---|---:|---:|
| Before (single fetch) | **500** | 1200 |
| After (pagination loop) | **1200** | 1200 |

Red commit: `700a5cc4` (test asserts `_allNodes.length === data.total`,
fails 500 ≠ 1200).
Green commit: `6d51da45` (pagination loop, test passes).

All 611 tests in `test-frontend-helpers.js` continue to pass — the
existing nodes.js WS-handler runtime tests are unaffected.

## Browser verified

Mocked-API unit test only — staging currently has <500 nodes so the bug
isn't reproducible there. The reporter's deployment
(`analyzer.marwoj.net`, 2313 nodes) is where the visible regression
occurs. The unit test reproduces the exact failure mode against a
controllable fixture.

## E2E assertion added

`test-issue-1606-pagination.js:170` — `assert.strictEqual(all.length,
env.fixtureTotal, ...)`

## Files changed

- `public/nodes.js` — `loadNodes()` single fetch → pagination loop
- `test-issue-1606-pagination.js` — new regression test (sandboxed
nodes.js + mock api)

## Out of scope (deferred to follow-up)

Per triage's (munger) note, these three siblings have the same
single-fetch bug and need their own focused PR:

- `public/analytics.js:2080` (`limit=10000`)
- `public/analytics.js:2817` (`limit=10000`)
- `public/packets.js:791` (`limit=2000`)

Closes #1606

---------

Co-authored-by: openclaw-bot <bot@openclaw.local>
2026-06-06 08:50:29 -07:00
..