"""Daily Briefing Dashboard — FastAPI Application.""" from __future__ import annotations import logging from contextlib import asynccontextmanager from pathlib import Path from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware from fastapi.staticfiles import StaticFiles from server.config import settings from server.services import news_service from server.services.mqtt_service import mqtt_service logger = logging.getLogger("daily-briefing") logging.basicConfig( level=logging.DEBUG if settings.debug else logging.INFO, format="%(asctime)s [%(levelname)s] %(name)s: %(message)s", ) @asynccontextmanager async def lifespan(app: FastAPI): """Startup / shutdown lifecycle.""" logger.info("Starting Daily Briefing Dashboard...") logger.info( "Unraid servers configured: %d", len(settings.unraid_servers), ) # Initialize database pool try: await news_service.init_pool( host=settings.db_host, port=settings.db_port, dbname=settings.db_name, user=settings.db_user, password=settings.db_password, ) logger.info("Database pool initialized") except Exception: logger.exception("Failed to initialize database pool — news will be unavailable") # Start MQTT service if settings.mqtt_host: try: await mqtt_service.start( host=settings.mqtt_host, port=settings.mqtt_port, username=settings.mqtt_username or None, password=settings.mqtt_password or None, topics=settings.mqtt_topics, client_id=settings.mqtt_client_id, ) logger.info("MQTT service started (broker %s:%d)", settings.mqtt_host, settings.mqtt_port) except Exception: logger.exception("Failed to start MQTT service — MQTT will be unavailable") else: logger.info("MQTT disabled — set MQTT_HOST to enable") yield # Shutdown logger.info("Shutting down...") await mqtt_service.stop() await news_service.close_pool() app = FastAPI( title="Daily Briefing", version="2.0.0", lifespan=lifespan, ) # CORS — allow frontend dev server app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) # --- Register Routers --- from server.routers import dashboard, homeassistant, mqtt, news, servers, tasks, weather # noqa: E402 app.include_router(weather.router) app.include_router(news.router) app.include_router(servers.router) app.include_router(homeassistant.router) app.include_router(tasks.router) app.include_router(mqtt.router) app.include_router(dashboard.router) # --- Serve static frontend (production) --- static_dir = Path(__file__).parent.parent / "static" if static_dir.is_dir(): app.mount("/", StaticFiles(directory=str(static_dir), html=True), name="static") logger.info("Serving static frontend from %s", static_dir) else: @app.get("/") async def root(): return { "status": "ok", "message": "Daily Briefing API — Frontend not built yet", "endpoints": ["/api/all", "/api/weather", "/api/news", "/api/servers", "/api/ha", "/api/tasks"], }