Architecture
WebSocket Real-time Delivery
How operational events reach a connected dashboard.
Source: hotelmind-backend/docs/WebSocket.md
WebSocket
Endpoint
GET /ws/dashboard — clients connect and receive JSON-encoded dashboard
update messages pushed from the server. There is no client → server protocol;
the server only reads (and discards) incoming frames to detect disconnects.
Message shape
Every broadcast message is whatever was published onto the dashboard:updates
Redis Pub/Sub channel by a consumer handler, e.g.:
{ "type": "booking", "event_type": "ReservationCreated", "payload": { "...": "..." } }
{ "type": "occupancy_changed", "branch_id": "...", "occupancy_pct": 72.5 }
{ "type": "forecast", "forecast_type": "OccupancyForecast", "branch_id": "...", "data": { "...": "..." } }How it works
app/websocket/manager.py—ConnectionManagertracks connectedWebSocketobjects and exposesbroadcast(payload).app/websocket/router.py— the/ws/dashboardroute accepts connections and registers them with a module-levelConnectionManagerinstance.app/websocket/pubsub_bridge.py—redis_pubsub_listener()runs as a background task started inapp/main.py's lifespan. It subscribes to thedashboard:updatesRedis channel and callsmanager.broadcast()for every message received.- Consumers (running in the separate
consumer_workerprocess) never talk to WebSocket clients directly — they only publish to Redis. This is what lets the API and consumer-worker processes/containers scale independently.
Metrics
websocket_connections (gauge) and websocket_messages_sent_total (counter)
are updated by ConnectionManager.
Frontend
hotelmind-frontend/lib/useDashboardSocket.ts is a small React hook that
opens the WebSocket, auto-reconnects on close (3s backoff), and exposes
{ lastUpdate, connected }. It's wired into
app/(app)/dashboard/page.tsx via components/dashboard/LiveOccupancyBadge.tsx,
which shows a live-updating occupancy percentage without polling.