Fixes verified by the 3-agent adversarial bug hunt (hunter, skeptic,
referee). Grouped by area:
Security
- routes: webhook internal-network check now reads the real client IP
(rightmost X-Forwarded-For hop, same as the rate limiter) so the
unauthenticated /api/plate and /api/unifi are no longer reachable
from the internet through Traefik
- config: mask the DB password in the startup log
- frontend: remove the dead /sign-up template that logged the
plaintext password to the console
Correctness and data integrity
- passage_ingest: serialize ingestion per plate to close the debounce
check-then-act race (duplicate passages and duplicate alert fan-out)
- api_vehicles: delete now removes the plate's images from disk (were
left orphaned) and the delete dialog warns about history/image loss
- passagem_repository: cast plate params to bpchar[] to keep the index
on the hot passages queries
- camera_repository: reject out-of-range ids instead of truncating i64
to i32, which could act on the wrong camera
- api_vehicles: reject create with both a driver and a company
- api_users: block an admin from removing their own admin flag
- api_dashboard: clamp available spots to [0, total] and propagate DB
errors instead of reporting capacity as 0
- api_alerts: map FK violation (23503) to 404 instead of an opaque 500
- api_profile: validate that the email contains '@'
- api_drivers: measure name length by chars, not bytes
- dahua: fail list_plates on firmware truncation so reconcile flags the
camera instead of diffing a partial list
- image: passage thumbnails now honor the per-camera crop_percent
- dashboard: keep the live feed bounded instead of growing forever
Regenerated .sqlx for the changed queries and routeTree.gen.ts.