42 lines
1.3 KiB
Python
42 lines
1.3 KiB
Python
"""
|
|
Manages persistent per-user WebSocket connections for the notification channel.
|
|
The DB is the source of truth — this layer just delivers live pushes to connected clients.
|
|
"""
|
|
import logging
|
|
|
|
from fastapi import WebSocket
|
|
|
|
logger = logging.getLogger("app")
|
|
|
|
# user_id (str) -> active WebSocket; replaced on reconnect
|
|
connections: dict[str, WebSocket] = {}
|
|
|
|
|
|
def register(user_id: str, ws: WebSocket) -> None:
|
|
connections[user_id] = ws
|
|
|
|
|
|
def unregister(user_id: str) -> None:
|
|
connections.pop(user_id, None)
|
|
|
|
|
|
async def send_notification(user_id: str, notification: dict) -> None:
|
|
"""Push a single notification to the user if they're connected. No-op otherwise."""
|
|
ws = connections.get(user_id)
|
|
if ws:
|
|
try:
|
|
await ws.send_json({"type": "push", "notification": notification})
|
|
except Exception as e:
|
|
# Stale connection — the disconnect handler will clean it up
|
|
logger.debug(f"WebSocket send failed (stale connection): {e}")
|
|
|
|
|
|
async def send_delete(user_id: str, notification_id: str) -> None:
|
|
"""Tell the client to remove a notification from its local list."""
|
|
ws = connections.get(user_id)
|
|
if ws:
|
|
try:
|
|
await ws.send_json({"type": "delete", "notification_id": notification_id})
|
|
except Exception as e:
|
|
logger.debug(f"WebSocket send failed (stale connection): {e}")
|