5.6 KiB
5.6 KiB
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/alertsendpoint - Reconnects with exponential backoff on disconnect
- Authenticates with JWT token
- Exposes
alertssignal that emits incoming alert objects - Heartbeat/ping-pong to keep connection alive
- Connects to
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:
- Install
wsnpm package inweb/(server-side WebSocket library). - 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
tokenfrom query string or cookie - Verify JWT, extract
userId - Store socket in
userSocketsMap
- Parse
- 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
- Create
web/src/lib/websocket.ts:connect()— create WebSocket, set up event handlersdisconnect()— close connectiononMessagecallback 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
- 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
- Call
- 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
- 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
- In DarkWatch alert pipeline (task 15): call
- Update Navbar:
- Add a pulsing dot indicator when WebSocket is connected
- Show "Offline" badge when disconnected
- 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 testfor 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
wsserver 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-motionfor notification sounds and aggressive toast animations. - Consider adding a "Do Not Disturb" mode where real-time toasts are suppressed but badge count still updates.