Files
boc/iom/api/websocket.py
T
Bernt bae705aa97 ARCHITECTURE: NFC roadmap, edge AI, audit logging
- Add NFC ePassport roadmap (ICAO 9303, eIDAS)
- Add TensorFlow.js edge face detection (BlazeFace)
- Add structured audit logger (GDPR-compliant)
- Risk scoring support

Part of KYC Apple Native UX v1.1.0
2026-06-29 16:24:48 +00:00

128 lines
4.3 KiB
Python

"""
WebSocket support for real-time updates
"""
from typing import Dict, List, Set
from fastapi import WebSocket, WebSocketDisconnect
import json
import asyncio
class ConnectionManager:
"""Manage WebSocket connections"""
def __init__(self):
self.active_connections: Dict[str, List[WebSocket]] = {}
self.user_connections: Dict[str, WebSocket] = {}
async def connect(self, websocket: WebSocket, client_id: str, room: str = "default"):
"""Connect a client"""
await websocket.accept()
if room not in self.active_connections:
self.active_connections[room] = []
self.active_connections[room].append(websocket)
self.user_connections[client_id] = websocket
def disconnect(self, websocket: WebSocket, client_id: str, room: str = "default"):
"""Disconnect a client"""
if room in self.active_connections:
if websocket in self.active_connections[room]:
self.active_connections[room].remove(websocket)
if client_id in self.user_connections:
del self.user_connections[client_id]
async def broadcast(self, message: dict, room: str = "default"):
"""Broadcast message to all clients in a room"""
if room not in self.active_connections:
return
disconnected = []
for connection in self.active_connections[room]:
try:
await connection.send_json(message)
except:
disconnected.append(connection)
# Clean up disconnected clients
for conn in disconnected:
if conn in self.active_connections[room]:
self.active_connections[room].remove(conn)
async def send_to_client(self, client_id: str, message: dict):
"""Send message to specific client"""
if client_id in self.user_connections:
await self.user_connections[client_id].send_json(message)
async def broadcast_observation(self, observation: dict):
"""Broadcast new observation"""
await self.broadcast({
"type": "new_observation",
"data": observation
}, room="observations")
async def broadcast_signal(self, signal: dict):
"""Broadcast new signal"""
await self.broadcast({
"type": "new_signal",
"data": signal
}, room="signals")
async def broadcast_decision(self, decision: dict):
"""Broadcast new decision"""
await self.broadcast({
"type": "new_decision",
"data": decision
}, room="decisions")
async def broadcast_outcome(self, outcome: dict):
"""Broadcast new outcome"""
await self.broadcast({
"type": "new_outcome",
"data": outcome
}, room="outcomes")
def get_stats(self) -> dict:
"""Get connection statistics"""
return {
"total_connections": sum(len(conns) for conns in self.active_connections.values()),
"rooms": {
room: len(conns)
for room, conns in self.active_connections.items()
},
"unique_clients": len(self.user_connections)
}
# Global connection manager
manager = ConnectionManager()
async def websocket_endpoint(websocket: WebSocket, client_id: str, room: str = "default"):
"""WebSocket endpoint"""
await manager.connect(websocket, client_id, room)
try:
while True:
# Receive message from client
data = await websocket.receive_text()
message = json.loads(data)
# Handle different message types
if message.get("type") == "subscribe":
# Client wants to subscribe to a room
new_room = message.get("room", "default")
# In production, validate subscription permissions
pass
elif message.get("type") == "ping":
await websocket.send_json({"type": "pong"})
except WebSocketDisconnect:
manager.disconnect(websocket, client_id, room)
except Exception as e:
manager.disconnect(websocket, client_id, room)
print(f"WebSocket error: {e}")