feat: wire MQTT → SSE bridge so the event timeline updates live
Closes #1. The Flask event-timeline was dead: `broadcast_sse_event` existed in `vigilar/web/blueprints/events.py` but had zero call sites. Clients subscribed to `/events/stream`, received the initial "connected" message, and then only keepalives — a page refresh was required to see new events. (Web Push via VAPID was independent and already worked.) The root cause was a process-boundary gap: the events subsystem runs in its own OS process and emits to MQTT, while the Flask app runs in a separate process with no MQTT client of its own. This change adds a thin bridge: - EventProcessor._handle_event now publishes a classified summary (id, ts, type, severity, source_id, payload) to a new topic `Topics.EVENTS_PUBLISHED = "vigilar/events/published"` right after `insert_event()`. Classification logic stays in one place. - A new module `vigilar/web/sse_bridge.py` provides `forward_event` (MQTT handler) and `start_sse_bridge(cfg)` (creates a MessageBus, subscribes forward_event to EVENTS_PUBLISHED, connects, returns the bus). - `vigilar/main.py:_run_web` starts the bridge after `create_app(cfg)` and disconnects it on shutdown. Bridge failure is logged but does not kill the web process — the UI still works without live updates. - `create_app` is deliberately NOT changed. Keeping the bridge out of the app factory means no existing test triggers a real MQTT connection, and the bridge stays a production-only concern wired by the supervisor. Tests (all added with TDD, RED verified before GREEN): - tests/unit/test_events.py::TestEventsPublishedBroadcast — asserts `_handle_event` publishes the classified payload for a motion event and does NOT publish for unclassified topics (heartbeats). - tests/unit/test_sse_bridge.py — asserts `forward_event` reaches SSE subscribers, and `start_sse_bridge` wires the handler to `Topics.EVENTS_PUBLISHED` on a connected bus (fake bus, no real MQTT in tests). Also refreshes the docs that previously flagged the dead SSE as a known limitation (operator guide, web architecture doc). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -585,13 +585,6 @@ Do not expose port `49735` directly on the WAN; require the tunnel.
|
||||
|
||||
## Known limitations
|
||||
|
||||
- **Event timeline is not live.** The web UI event timeline requires
|
||||
a page refresh to show new events. `broadcast_sse_event` exists in
|
||||
`vigilar/web/blueprints/events.py` but has zero call sites today;
|
||||
events are not pushed to browsers via SSE. Web Push notifications
|
||||
via VAPID are independent of the timeline and do work: you will
|
||||
get mobile alerts as motion happens, but the in-page timeline lags
|
||||
until you reload.
|
||||
- **Recording integrity is not authenticated.** AES-256-CTR gives you
|
||||
confidentiality, not tamper-evidence. If an attacker reaches the
|
||||
recordings directory they can modify ciphertext unnoticed. See the
|
||||
|
||||
Reference in New Issue
Block a user