Files
2026-06-06 00:52:52 +03:00

49 lines
1.6 KiB
Python

"""FastAPI application factory for the embedded radar web UI.
The controller (the Qt bridge that forwards to the AppWindow) is created and
owned by the host process and injected here. The app's only owned resource is the
:class:`RingBroadcaster` polling task, created and torn down by the lifespan. The
static single-page frontend is mounted at ``/`` and the JSON/WS API under
``/api`` and ``/ws``.
"""
from __future__ import annotations
from contextlib import asynccontextmanager
import logging
from pathlib import Path
from fastapi import FastAPI
from fastapi.staticfiles import StaticFiles
from python_app.webui.controller import WebController
from python_app.webui.routes import router
from python_app.webui.streaming import RingBroadcaster
logger = logging.getLogger(__name__)
_STATIC_DIR = Path(__file__).resolve().parent / "static"
def create_app(controller: WebController) -> FastAPI:
"""Build the FastAPI app that serves and streams for ``controller``."""
@asynccontextmanager
async def lifespan(app: FastAPI):
broadcaster = RingBroadcaster(controller)
app.state.controller = controller
app.state.broadcaster = broadcaster
broadcaster.start()
logger.info("Web UI application started")
try:
yield
finally:
await broadcaster.stop()
logger.info("Web UI application stopped")
app = FastAPI(title="Radar Web UI", lifespan=lifespan)
app.include_router(router)
# Mount the SPA last so the API routes above always take precedence.
app.mount("/", StaticFiles(directory=_STATIC_DIR, html=True), name="static")
return app