SSE endpoint publishing quest-completed transitions (task 4d7bae00)
The panel needs live transitions, so add ONE in-process publisher (api/events.py, installed on app.state.publisher in the lifespan) and a GET /api/v1/panel/events SSE route on the panel router — it inherits the single require_panel_token check. The complete route now builds the event through the core builder and publishes it only when the core's .appended flag says an actual transition happened; a no-op re-complete publishes nothing. Tests stream from a real uvicorn instance on an ephemeral port because httpx's ASGITransport buffers endless SSE bodies.
This commit is contained in:
@@ -39,6 +39,7 @@ from fastapi import FastAPI
|
||||
|
||||
from api.config import ApiConfig
|
||||
from api.database import DatabaseState, make_database
|
||||
from api.events import TransitionPublisher
|
||||
from api.routes_panel import router as panel_router
|
||||
|
||||
|
||||
@@ -47,12 +48,16 @@ def _build_lifespan(db_path: str):
|
||||
|
||||
Captured as a closure so the app factory can hand the configured
|
||||
path in without threading it through FastAPI's lifespan signature.
|
||||
The lifespan also installs the app's ONE in-process transition
|
||||
publisher on ``app.state.publisher`` (see :mod:`api.events`) so
|
||||
every route and SSE subscriber shares that single instance.
|
||||
"""
|
||||
|
||||
@asynccontextmanager
|
||||
async def lifespan(app: FastAPI) -> AsyncIterator[None]:
|
||||
state = make_database(db_path)
|
||||
app.state.db = state
|
||||
app.state.publisher = TransitionPublisher()
|
||||
try:
|
||||
await state.open_and_migrate()
|
||||
yield
|
||||
|
||||
Reference in New Issue
Block a user