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 app.routers import backups, restore, services, status, system logging.basicConfig( level=logging.INFO, format="%(asctime)s %(levelname)s %(name)s: %(message)s", ) logger = logging.getLogger(__name__) _STATIC_DIR = Path(__file__).parent.parent / "static" @asynccontextmanager async def lifespan(app: FastAPI): logger.info("Ops WebUI server is running") yield app = FastAPI( title="Ops WebUI API", description="Backend API for the ops web dashboard", version="1.0.0", lifespan=lifespan, ) # --------------------------------------------------------------------------- # CORS – open for development; restrict in production via env/reverse proxy # --------------------------------------------------------------------------- app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) # --------------------------------------------------------------------------- # API routers # --------------------------------------------------------------------------- app.include_router(status.router, prefix="/api/status", tags=["status"]) app.include_router(backups.router, prefix="/api/backups", tags=["backups"]) app.include_router(restore.router, prefix="/api/restore", tags=["restore"]) app.include_router(services.router, prefix="/api/services", tags=["services"]) app.include_router(system.router, prefix="/api/system", tags=["system"]) # --------------------------------------------------------------------------- # Static files – serve CSS/JS at /static and HTML at / # Mount /static first for explicit asset paths, then / for SPA fallback # --------------------------------------------------------------------------- if _STATIC_DIR.exists(): app.mount("/static", StaticFiles(directory=str(_STATIC_DIR)), name="static-assets") app.mount("/", StaticFiles(directory=str(_STATIC_DIR), html=True), name="static") else: logger.warning("Static directory not found at %s – frontend will not be served", _STATIC_DIR)