feat(geofence): watch list, filtered hit log, fence snapshot #47

Merged
sirius merged 1 commit from feat/geofence-watch into master 2026-09-01 01:26:31 -04:00
Owner

Summary

Off-viewport geofence watches, filterable hit log, fence-scoped DVR snapshot.

Phase 2 CRUD already existed; geofence_alert only reached clients whose map viewport contained the point. A user who drew a fence and panned away got silence. This PR is the backend contract the HUD (t_3b1b5414) will consume.

WS contract (/ws/live)

Client → server:

{"type":"viewport","bbox":"minlon,minlat,maxlon,maxlat"}
{"type":"watch_geofences","ids":["<uuid>", ...]}
  • Invalid UUIDs ignored. Empty ids = watch none (viewport-only again).
  • Unregister clears the watch set.

Server → client geofence_alert if either:

  • viewport contains (lat, lon) (unchanged), or
  • payload.geofence_id is in that client's watch set.

AIS / ADS-B / fire_aircraft stay viewport-only.

HTTP

  • GET /api/geofence-alerts?geofence_id=&since=&until=&source_kind=firms|ais|adsb&limit= (1–500, default 100). Same collection path; 422 on bad source_kind.
  • GET /api/geofences/{id}/at?timestamp=<ISO>{geofence_id, timestamp, aircraft, vessels, fires}. 404 if fence missing. Empty lists if DB down. No writes / no record_and_notify.
  • DELETE /api/geofences/{id}404 when the row is gone (was always 204).

Schema

Alembic 011_geofence_alerts_fence (revises 010_bbox_gist):
ix_geofence_alerts_fence_created ON geofence_alerts (geofence_id, created_at DESC).

Tests

pytest tests/test_geofence.py tests/test_phase2_api.py — 27 passed (watch a/b/c, AIS still viewport-only, filtered alerts, /at 404 + empty lists, DELETE 404). Kept test_geofence_post_rejects_point.

## Summary Off-viewport geofence watches, filterable hit log, fence-scoped DVR snapshot. Phase 2 CRUD already existed; `geofence_alert` only reached clients whose map viewport contained the point. A user who drew a fence and panned away got silence. This PR is the backend contract the HUD (`t_3b1b5414`) will consume. ## WS contract (`/ws/live`) Client → server: ``` {"type":"viewport","bbox":"minlon,minlat,maxlon,maxlat"} {"type":"watch_geofences","ids":["<uuid>", ...]} ``` - Invalid UUIDs ignored. Empty `ids` = watch none (viewport-only again). - Unregister clears the watch set. Server → client `geofence_alert` if **either**: - viewport contains `(lat, lon)` (unchanged), **or** - `payload.geofence_id` is in that client's watch set. AIS / ADS-B / `fire_aircraft` stay viewport-only. ## HTTP - `GET /api/geofence-alerts?geofence_id=&since=&until=&source_kind=firms|ais|adsb&limit=` (1–500, default 100). Same collection path; 422 on bad `source_kind`. - `GET /api/geofences/{id}/at?timestamp=<ISO>` → `{geofence_id, timestamp, aircraft, vessels, fires}`. 404 if fence missing. Empty lists if DB down. **No writes / no `record_and_notify`.** - `DELETE /api/geofences/{id}` → **404** when the row is gone (was always 204). ## Schema Alembic `011_geofence_alerts_fence` (revises `010_bbox_gist`): `ix_geofence_alerts_fence_created ON geofence_alerts (geofence_id, created_at DESC)`. ## Tests `pytest tests/test_geofence.py tests/test_phase2_api.py` — 27 passed (watch a/b/c, AIS still viewport-only, filtered alerts, `/at` 404 + empty lists, DELETE 404). Kept `test_geofence_post_rejects_point`.
sirius added 1 commit 2026-09-01 01:22:58 -04:00
Off-viewport clients that send {type:watch_geofences,ids} on /ws/live
still receive geofence_alert; AIS/ADS-B stay viewport-only.

GET /api/geofence-alerts accepts geofence_id/since/until/source_kind.
GET /api/geofences/{id}/at returns CAGG+FIRMS inside the fence at T
(404 if missing, empty lists if DB down). DELETE missing fences 404s.
sirius merged commit 75e065f8c2 into master 2026-09-01 01:26:31 -04:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: sirius/osint-dashboard#47
No description provided.