feat(sse): sync temps réel multi-appareils via Server-Sent Events v0.5.8
- Broadcaster asyncio.Queue avec keepalive 25s (prévient timeout proxy) - Endpoint GET /api/events/stream (StreamingResponse text/event-stream) - Broadcast notes_changed / todos_changed / shopping_changed sur toutes mutations - Hook useServerEvents: EventSource avec reconnexion automatique (3s) - Pages Notes, Todos, Shopping abonnées aux événements SSE - nginx: location SSE dédiée (proxy_buffering off, timeout 24h) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
import asyncio
|
||||
import json
|
||||
from typing import AsyncGenerator
|
||||
|
||||
|
||||
class EventBroadcaster:
|
||||
def __init__(self):
|
||||
self._queues: set[asyncio.Queue] = set()
|
||||
|
||||
async def subscribe(self) -> AsyncGenerator[str, None]:
|
||||
queue: asyncio.Queue[str] = asyncio.Queue(maxsize=32)
|
||||
self._queues.add(queue)
|
||||
try:
|
||||
while True:
|
||||
try:
|
||||
msg = await asyncio.wait_for(queue.get(), timeout=25)
|
||||
yield msg
|
||||
except asyncio.TimeoutError:
|
||||
yield ": keepalive\n\n"
|
||||
except asyncio.CancelledError:
|
||||
pass
|
||||
finally:
|
||||
self._queues.discard(queue)
|
||||
|
||||
def broadcast(self, event_type: str, data: dict | None = None) -> None:
|
||||
if not self._queues:
|
||||
return
|
||||
msg = f"event: {event_type}\ndata: {json.dumps(data or {})}\n\n"
|
||||
for queue in list(self._queues):
|
||||
try:
|
||||
queue.put_nowait(msg)
|
||||
except asyncio.QueueFull:
|
||||
pass
|
||||
|
||||
|
||||
broadcaster = EventBroadcaster()
|
||||
Reference in New Issue
Block a user