aicctv
Two approved data-model changes. P1-9 — ingest idempotency. A POST can succeed server-side but time out client-side; the edge then re-queues and re-sends the same event, so the cloud stored duplicate alerts. The edge Event now carries a stable event_uuid (uuid4 hex) generated once at creation and preserved through the offline queue and every retry. Cloud: events.event_uuid CHAR(32) NULL UNIQUE; EventValidator accepts/validates it; IngestController returns the already-stored event (200, dedup:true) on a repeat uuid, race-safe against the UNIQUE key (catches the insert-race PDOException and returns the winner). P3-2b — site-scoped access control. site_scope was stored in the session but never enforced, so any authenticated user could read every site's worker snapshots (PIPA). Added devices.site; ingest denormalizes it onto events.site (server-derived from the authenticated device_id, never client-supplied). App\Security\SiteScope centralizes the rule (NULL scope = global operator). The event list filters in SQL (page(...,$site)); event detail + acknowledge and the snapshot proxy guard per-row and return 404 (not 403) cross-site so existence never leaks. Schema migration ALTERs and per-device/user provisioning UPDATEs are documented at the bottom of schema.sql. Tests: edge queue round-trip keeps the uuid; repeat-uuid dedups to one row; list scoped to caller site; detail cross-site 404; snapshot cross-site 404 / same-site 200; event_uuid validation. phpunit 97 passing (4 skipped: PDO), pytest 158 passing (2 skipped). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019Xmn8KZWqiQUguEMRtn6rW |
||
|---|---|---|
| cloud | ||
| deploy | ||
| docs | ||
| edge | ||
| tests | ||
| .gitattributes | ||
| .gitignore | ||
| pyproject.toml | ||