"""StackPilot backend — FastAPI application entry point.""" from __future__ import annotations import asyncio import logging from contextlib import asynccontextmanager from fastapi import FastAPI, Request from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import JSONResponse from sqlmodel import Session from config import settings from database import engine, init_db from docker_client import DockerError from routers import ( agents, audit, auth, backups, destinations, editor, files, images, networks, ports, schedules, settings as settings_router, stacks, system, templates, volumes, ws, ) from services import schedule_service, update_service logging.basicConfig(level=logging.INFO) logger = logging.getLogger("stackpilot") @asynccontextmanager async def lifespan(app: FastAPI): init_db() # Register stacks that already exist on disk. try: with Session(engine) as session: stacks.sync_discovered_stacks(session) except Exception as exc: # noqa: BLE001 logger.warning("Stack discovery failed: %s", exc) update_task = asyncio.create_task(update_service.background_loop()) schedule_task = asyncio.create_task(schedule_service.scheduler_loop()) logger.info("StackPilot backend ready on port %s", settings.PORT) yield update_task.cancel() schedule_task.cancel() app = FastAPI(title="StackPilot", version="0.17.0", lifespan=lifespan) app.add_middleware( CORSMiddleware, allow_origins=settings.CORS_ORIGINS, allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) @app.exception_handler(DockerError) async def docker_error_handler(_request: Request, exc: DockerError): return JSONResponse( status_code=502, content={"error": exc.error, "detail": exc.detail}, ) app.include_router(auth.router) app.include_router(stacks.router) app.include_router(system.router) app.include_router(volumes.router) app.include_router(editor.router) app.include_router(files.router) app.include_router(images.router) app.include_router(ports.router) app.include_router(templates.router) app.include_router(audit.router) app.include_router(settings_router.router) app.include_router(backups.router) app.include_router(destinations.router) app.include_router(schedules.router) app.include_router(networks.router) app.include_router(agents.router) app.include_router(ws.router) @app.get("/api/health") def health() -> dict: return {"status": "ok"}