Files
stackpilot/backend/main.py
T
menzeljandClaude Opus 4.8 8f6e354b3f Phase 16: volumes page, multi-host (0.17.0)
Adds a dedicated Volumes page (sidebar) with per-host sections (local + each
online agent), matching the Networks/Images layout. Lists volumes with driver,
owning stack, in-use containers and mountpoint; admins can delete (with an
in-use warning + force option) and prune unused, plus an "only unused" filter.

- agent_app.py: /agent/volumes (list/delete with in-use 409 guard/prune)
  reusing volume_service.
- routers/agents.py: proxy routes /api/agents/{id}/volumes/* (audit-logged
  delete/prune).
- Frontend: volumesApi list/remove/prune take an optional agentId; new
  pages/Volumes.tsx (VolumesSection per host) + sidebar entry + /volumes route.
  The volume wizard (generate-yaml/host paths) stays local and unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 12:49:59 +00:00

99 lines
2.5 KiB
Python

"""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"}