restructure tasks
This commit is contained in:
104
tasks/shieldai-unified-restructure/25-realtime-alerts.md
Normal file
104
tasks/shieldai-unified-restructure/25-realtime-alerts.md
Normal file
@@ -0,0 +1,104 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user