Files
Deal/archive/leadradar-legacy/backend/app/sse.py
T
Rustam Khalimov 9e07568ddd Инициализировать репозиторий «Дейл»
Первый коммит: модульный монолит ядра (.NET 10) и gRPC-сервисы
ai/ml/telegram, фронтенд Vue 3/Vite/Tailwind, документация (ТЗ,
инструкция пользователя, техдокументация, код-стайл), бэклог,
скрипты развёртывания и архив прототипа LeadRadar.
2026-09-11 02:50:17 +03:00

52 lines
1.8 KiB
Python

"""SSE-брокер событий.
Сервер рассылает события подключённым браузерам:
new_lead, lead_updated, toast, reminder_due, project_updated, system_status.
Поток однонаправленный, по ТЗ — Server-Sent Events с автопереподключением.
"""
from __future__ import annotations
import asyncio
import json
from typing import Any
class Broker:
def __init__(self) -> None:
self._subscribers: set[asyncio.Queue] = set()
self._lock = asyncio.Lock()
async def subscribe(self) -> asyncio.Queue:
q: asyncio.Queue = asyncio.Queue(maxsize=200)
async with self._lock:
self._subscribers.add(q)
return q
async def unsubscribe(self, q: asyncio.Queue) -> None:
async with self._lock:
self._subscribers.discard(q)
async def publish(self, event_type: str, data: Any) -> None:
payload = f"event: {event_type}\ndata: {json.dumps(data, ensure_ascii=False)}\n\n"
async with self._lock:
subs = list(self._subscribers)
for q in subs:
try:
q.put_nowait(payload)
except asyncio.QueueFull:
# при переполнении сбрасываем очередь подписчика — браузер переподключится
try:
q.get_nowait()
except Exception:
pass
try:
q.put_nowait(payload)
except Exception:
pass
async def publish_toast(self, text: str, icon: str = "check") -> None:
await self.publish("toast", {"text": text, "icon": icon})
broker = Broker()