from __future__ import annotations import logging from contextlib import asynccontextmanager from pathlib import Path from fastapi import FastAPI from fastapi.responses import FileResponse from fastapi.middleware.cors import CORSMiddleware from fastapi.staticfiles import StaticFiles from app.config import configure_logging, get_settings from app.routes.overview import router as overview_router from app.routes.services import router as services_router from app.routes.storage import router as storage_router from app.routes.system import router as system_router logger = logging.getLogger(__name__) @asynccontextmanager async def lifespan(app: FastAPI): settings = get_settings() configure_logging(settings.app_log_level) logger.info("Starting %s v%s in %s mode", settings.app_name, settings.app_version, settings.app_env) logger.info( "Config loaded: HOME_ASSISTANT_BASE_URL=%s HOME_ASSISTANT_TOKEN_SET=%s BESZEL_BASE_URL=%s DOCKER_PROXY_BASE_URL=%s UPTIME_KUMA_BASE_URL=%s", bool(settings.home_assistant_base_url), bool(settings.home_assistant_token), bool(settings.beszel_base_url), bool(settings.docker_proxy_base_url), bool(settings.uptime_kuma_base_url), ) yield logger.info("Stopping %s", settings.app_name) settings = get_settings() app = FastAPI( title=settings.app_name, version=settings.app_version, lifespan=lifespan, ) app.add_middleware( CORSMiddleware, allow_origins=settings.cors_allow_origins, allow_credentials=True, allow_methods=["GET"], allow_headers=["*"], ) app.include_router(overview_router) app.include_router(system_router) app.include_router(services_router) app.include_router(storage_router) assets_dir = settings.app_root_dir / "assets" dashboard_file = settings.app_root_dir / "dashboard.html" if assets_dir.exists(): app.mount("/assets", StaticFiles(directory=assets_dir), name="assets") @app.get("/health", tags=["health"]) async def health() -> dict[str, str]: return { "status": "ok", "service": settings.app_name, "version": settings.app_version, "environment": settings.app_env, } @app.get("/", include_in_schema=False) async def dashboard() -> FileResponse: return FileResponse(dashboard_file)