Files
Kordant/tasks/shieldai-unified-restructure/25-realtime-alerts.md
2026-05-25 12:23:23 -04:00

105 lines
5.6 KiB
Markdown

# 25. Real-Time Alerts — WebSocket Push Notifications
meta:
id: shieldai-unified-restructure-25
feature: shieldai-unified-restructure
priority: P1
depends_on: [shieldai-unified-restructure-23]
tags: [backend, frontend, websocket, realtime, notifications]
objective:
- Implement real-time alert push from server to client using WebSockets. When a new alert is generated (exposure detected, synthetic voice found, spam blocked, property changed, broker listing found), the user should receive an immediate notification in the web app.
deliverables:
- `web/src/server/websocket.ts` — WebSocket server:
- Integrates with SolidStart's Nitro server or runs alongside it
- Authenticates connections using JWT from query param or cookie
- Maintains userId → socket mapping
- Broadcasts alerts to connected users
- `web/src/lib/websocket.ts` — WebSocket client:
- Connects to `/ws/alerts` endpoint
- Reconnects with exponential backoff on disconnect
- Authenticates with JWT token
- Exposes `alerts` signal that emits incoming alert objects
- Heartbeat/ping-pong to keep connection alive
- `web/src/hooks/useRealtimeAlerts.ts` — Hook:
- Uses websocket client
- Triggers toast notification on new alert
- Increments unread badge count in Navbar
- Plays subtle notification sound (optional, respects reduced motion)
- `web/src/server/services/alert.publisher.ts` — Alert publisher:
- `publishAlert(userId, alert)` — sends alert to user's connected sockets
- Called from each service's alert pipeline (DarkWatch, VoicePrint, SpamShield, HomeTitle, RemoveBrokers)
- Falls back to push notification (task 14) if user is not connected
steps:
1. Install `ws` npm package in `web/` (server-side WebSocket library).
2. Create `web/src/server/websocket.ts`:
- If using Nitro (SolidStart's server), create a custom Nitro plugin or API route that upgrades to WebSocket
- If standalone, create a separate WebSocket server on a different port (e.g., 3001)
- On connection:
- Parse `token` from query string or cookie
- Verify JWT, extract `userId`
- Store socket in `userSockets` Map
- On message: handle ping with pong, ignore other messages (server pushes only)
- On disconnect: remove socket from map
- `broadcastToUser(userId, data)`: find all sockets for user, send JSON
3. Create `web/src/lib/websocket.ts`:
- `connect()` — create WebSocket, set up event handlers
- `disconnect()` — close connection
- `onMessage` callback or signal for incoming data
- Reconnect logic: 1s, 2s, 5s, 10s, 30s backoff, max 10 attempts
- Heartbeat: send ping every 30s, expect pong within 10s
4. Create `web/src/hooks/useRealtimeAlerts.ts`:
- Call `connect()` on mount if user is authenticated
- On alert received: show toast via `useToast()`, increment unread count
- On disconnect: show "Reconnecting..." indicator
- Cleanup on unmount
5. Create `web/src/server/services/alert.publisher.ts`:
- `publishAlert(userId, alert)`:
- Try WebSocket broadcast first
- If no active sockets, queue push notification (FCM/APNs) via task 14
- If push fails, queue email notification
- `publishToGroup(userIds, alert)` — for family group alerts
6. Integrate with service alert pipelines:
- In DarkWatch alert pipeline (task 15): call `alertPublisher.publishAlert(userId, alert)`
- In VoicePrint analysis result handler (task 16): same
- In SpamShield classification (task 17): same
- In HomeTitle change detector (task 18): same
- In RemoveBrokers listing scanner (task 19): same
7. Update Navbar:
- Add a pulsing dot indicator when WebSocket is connected
- Show "Offline" badge when disconnected
8. Write integration tests for WebSocket flow.
steps:
- Unit: WebSocket server authenticates connections with valid JWT
- Unit: WebSocket server rejects connections with invalid JWT
- Unit: Client reconnects with exponential backoff on disconnect
- Integration: Triggering an alert in DarkWatch service sends real-time notification to connected client
- E2E: User receives toast notification within 2 seconds of alert generation
acceptance_criteria:
- [ ] WebSocket server authenticates connections and maps them to users
- [ ] Alerts are broadcast to connected users within 1 second of generation
- [ ] Client reconnects automatically after network interruption
- [ ] Toast notifications appear for real-time alerts
- [ ] Unread badge count increments immediately on new alert
- [ ] If user is offline, alert falls back to push notification
- [ ] Heartbeat keeps connections alive without timeout
- [ ] WebSocket does not leak memory on disconnect/reconnect cycles
validation:
- Open dashboard in two browser tabs, trigger an alert in one, verify both receive notification
- Disconnect network, reconnect, verify client re-establishes connection
- Check server memory usage after 100 connect/disconnect cycles
- Run `cd web && pnpm test` for WebSocket integration tests
notes:
- Reference legacy: `server/alerts/` (WebSocket alert server), `packages/api/src/routes/websocket.routes.ts`
- Nitro (SolidStart's server) has experimental WebSocket support. If unstable, run a separate `ws` server on a different port and proxy through nginx in production.
- For production scaling, consider using Redis Pub/Sub to broadcast alerts across multiple server instances.
- The WebSocket connection should be lazy: only connect when user is on a protected route and authenticated.
- Respect `prefers-reduced-motion` for notification sounds and aggressive toast animations.
- Consider adding a "Do Not Disturb" mode where real-time toasts are suppressed but badge count still updates.