Completes securing the web front end (vrek gol-wqf95dq, iss-r5vrjx7).
- /login: Server Action with a generic error, same-site-only redirect
back to ?next=, and throttling of failed logins (10 per address and
100 overall per 15 min). The header shows "Signed in as" with
Sign out (iss-nj9wmwp).
- First run with no admin: instrumentation prints a one-time setup
code, shared with the app through globalThis. /setup requires it,
and 5 wrong codes rotate it. "Skip for now" runs unsecured for the
browser session behind a red warning banner on every page
(iss-9nxdndr).
- src/proxy.ts: optimistic redirects to /login or /setup, 401 for
the API, and the 12 h sliding session refresh. requirePageAccess()
and apiAccessDenied() re-check in the page and all 6 route handlers
(iss-76d5wrb).
- An expired session now shows "Your session has ended" instead of
the camera-login form.
- README documents in-app setup, skipping and signing in.
Verified with unit tests (383, 99.9% line coverage), end to end
against `next start`, and manually in a browser by the user.
Refreshes the vrek export.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- Route tests for camera info, snapshot and credentials (vrek
iss-nc2tj7c): input validation, store-only-after-verify, reconnects
after login changes, and fixed error messages with no upstream text.
- Providers and layout tests (iss-nc5w0j8).
- Discovery tests for onvif.ts (iss-3bg4r6e): multicast parsing,
unicast sweep batching and subnet limits, de-duplication and
merging. They run on a fake network, replacing onvif Discovery,
node:dgram and node:os, per new vrek principle pri-e14bahk
(replaceable transport; tests never touch the real network).
- coverage.thresholds.lines = 95, so `npm run coverage` fails below
the goal (iss-zjpc22k).
200 tests, 99.77% line coverage. Refreshes the vrek export.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Adds src/app/camera-queries.ts: React Query hooks over our own routes,
with each camera's queries keyed ['camera', id]. The camera card,
snapshot polling, login form and network scan use these hooks instead
of hand-rolled fetch/useEffect state (vrek iss-2fm6x2y, iss-ksxmctm,
iss-m032zwq, iss-8hfq2y2).
- Snapshot polling pauses in hidden tabs, never overlaps a slow
frame, and stops after a failure until Retry. Each frame's object
URL is created and revoked in one effect, so none leak under Strict
Mode.
- Saving or forgetting a login resets only that camera's queries.
- New "Forget saved login" action, shown for stored logins.
- Fix: a scan timeout typed below 1 s now clamps to 1 s instead of
falling back to 5 s.
Adds jsdom component tests (test/dom.tsx helpers): 93 tests, line
coverage 54.3%. Refreshes the vrek export.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sets up Vitest (jsdom, Testing Library, v8 coverage) per the Next 16
testing guide, using Vite's native tsconfig path resolution instead of
vite-tsconfig-paths. A server-only stub and a temp-data helper let
server modules run in isolation. @types/node moves to ^24 to match the
Node 24 runtime (a vitest 5 peer requirement).
First 68 tests cover the discover route (including iss-dbwgww8: no raw
errors in responses), the credential store, the camera registry,
camera-route helpers and the refresh-rate schema. Line coverage is
31.2%, toward the 95% goal.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Discovers ONVIF cameras (Hikvision/Annke) over WS-Discovery, keeps a
server-side registry and an encrypted credential store, and serves
info, snapshot and credentials routes for each camera.
The home page is now a Server Component that lists known cameras from
the registry on load, without a scan (vrek iss-a0hz0py). The snapshot
refresh rate lives in the URL (?refresh=), and a scan refreshes the
server-rendered list.
Route input is validated with zod (iss-rjqy3hy), and /api/discover no
longer returns raw error messages (iss-dbwgww8). Adds
@tanstack/react-query and zod as dependencies, with a QueryClient
provider in the root layout.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>