"""FlowDeck — v5.13.0 Realtime: WebSocket gateway /ws/pages/{page_id}. Auth via cookie session (flowdeck_session). Rooms in-memory par page. """ from __future__ import annotations import json import logging from fastapi import APIRouter, Request, WebSocket from starlette.websockets import WebSocketDisconnect from app.auth.session import SessionManager from app.services.realtime_server import manager logger = logging.getLogger(__name__) router = APIRouter(tags=["realtime"]) @router.get("/api/realtime/stats") def realtime_stats(request: Request): """Observabilité realtime v6.4.0 : rooms, connexions, ops, merges, conflits. Réservé aux utilisateurs authentifiés (données d'activité internes). """ user = SessionManager.decode_session( request.cookies.get("flowdeck_session", "") ) if not user or not user.get("id"): return {"error": "unauthorized"} return manager.stats() @router.websocket("/ws/pages/{page_id}") async def ws_page(websocket: WebSocket, page_id: int): await websocket.accept() user = SessionManager.decode_session( websocket.cookies.get("flowdeck_session", "") ) if not user or not user.get("id"): try: await websocket.close(code=4401) except Exception: logger.exception("ws_page") return conn = await manager.connect(websocket, page_id, user) if not conn: return try: while True: raw = await websocket.receive_text() try: msg = json.loads(raw) except (TypeError, ValueError): continue await manager.handle(conn, msg) except WebSocketDisconnect: pass except Exception as e: # noqa: BLE001 logger.debug("ws closed: %s", e) finally: await manager.disconnect(conn)