Repository navigation
[Agent] Add a license plate (LPR) search UI #653
Copy link
Copy link
Open
Labels
enhancementNew feature or requestNew feature or requestjavascriptPull requests that update javascript codePull requests that update javascript code
Description
Problem and evidence
ONVIF license-plate recognition reads are stored and fully exposed by the API (added in 0e091c6c), but the web UI has no screen for them. Nothing in web/js calls /api/lpr.
Backend (src/web/api_handlers_lpr.c, routes in src/web/libuv_api_handlers.c:289-291):
POST /api/lpr/search: JSON body{camera_uuid (required), start_at, end_at (ms epoch, range ≤ 366 days), limit (default 50), match?: "exact"|"partial", plate?}. Returns reads withid, camera_uuid, stream_name, observed_at, received_at, source, plate, confidence, attributes{country, region, plate_type, direction, lane, vehicle_type, vehicle_color, ...}, recording_id?.POST /api/lpr/export: same body (limit default 1000). Responds with a JSON download (lightnvr-lpr-export.json).DELETE /api/lpr/reads/{uuid}.- Authorization is per camera:
AUTHZ_LPR_READ,AUTHZ_LPR_SEARCH,AUTHZ_LPR_EXPORT,AUTHZ_LPR_DELETE(include/core/authorization.h). Every query is audit-logged, and plate strings aresecure_zero'd server side, so treat plates as sensitive data.
Expected behavior and scope
- Add a "Plates" (LPR) view. The Investigation workspace (
web/js/components/preact/investigation/) is a natural home, either as a tab or as a sibling page linked from the Recordings/Timeline sub-nav. Choose whichever fits the existing nav, and explain the choice in the PR. - Filters: camera picker (cameras the user can see, which supply
camera_uuid), time range, plate text with an exact/partial toggle, and a limit. - Results table: plate, time (local), camera, confidence, direction, vehicle type and color, and a "View recording" link using
recording_id/observed_atthat deep-links to the timeline at that time. - Export button, shown only when export is permitted (a 403 should hide or disable it, not show an error toast). Delete per row, with confirmation, shown only when permitted.
- Empty state that explains LPR reads come from ONVIF cameras with plate analytics.
- All strings go through
t()with newlpr.*keys inweb/public/locales/en.json. - Plates must not go into
localStorageor URLs. Keep filter state in memory.
Acceptance criteria
- A user with search permission can find reads by partial plate within a time range and open the matching recording.
- A user without export or delete permission doesn't see those actions, or sees them disabled.
- 400 errors (bad range, etc.) show readable validation messages.
- Keyboard accessible and labelled (the repo runs jsx-a11y lint in CI).
Validation
cd web && npx vite build,npm run lint,npm test.- Jest tests for the request body built from the filters and for permission-driven rendering, with
fetchmocked. - Optional: an e2e spec that seeds reads through the DB test helpers, if any exist.
Constraints and rollback
- UI only. No backend changes needed. If you find an API gap (for example, listing cameras with LPR data), open a separate issue rather than changing the API here.
- Rollback: revert the frontend PR.
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
enhancementNew feature or requestNew feature or requestjavascriptPull requests that update javascript codePull requests that update javascript code
Activity