"""Volume management, NFS/SMB YAML generation, and host path browser.""" from __future__ import annotations from fastapi import APIRouter, Depends, HTTPException, Query, Request from sqlmodel import Session from auth import get_current_user, require_admin from database import get_session from models.user import User from services import audit_service, device_service, volume_service router = APIRouter(tags=["volumes"]) def _ip(request: Request) -> str: return request.client.host if request.client else "unknown" # --------------------------------------------------------------------------- # # Volumes # --------------------------------------------------------------------------- # @router.get("/api/volumes") def list_volumes(_user: User = Depends(get_current_user)) -> list[dict]: return volume_service.list_volumes() @router.get("/api/volumes/orphaned") def orphaned(_user: User = Depends(get_current_user)) -> list[dict]: return volume_service.orphaned_volumes() @router.delete("/api/volumes/{name}") def delete_volume( name: str, request: Request, force: bool = Query(False), session: Session = Depends(get_session), user: User = Depends(require_admin), ) -> dict: vols = {v["name"]: v for v in volume_service.list_volumes()} if name in vols and vols[name]["in_use"] and not force: raise HTTPException( status_code=409, detail={ "error": "volume_in_use", "detail": f"Volume '{name}' is used by: {', '.join(vols[name]['used_by'])}", }, ) volume_service.remove_volume(name, force=force) audit_service.record( session, user=user.username, action="volume.delete", target=name, ip=_ip(request) ) return {"ok": True} @router.post("/api/volumes/prune") def prune( request: Request, session: Session = Depends(get_session), user: User = Depends(require_admin), ) -> dict: result = volume_service.prune_volumes() audit_service.record( session, user=user.username, action="volume.prune", target="*", detail=str(result.get("VolumesDeleted")), ip=_ip(request), ) return result @router.post("/api/volumes/generate-yaml") def generate_yaml( spec: dict, _user: User = Depends(get_current_user), ) -> dict: try: return {"yaml": volume_service.generate_yaml(spec)} except (KeyError, ValueError) as exc: raise HTTPException(status_code=400, detail=f"Invalid volume spec: {exc}") from exc # --------------------------------------------------------------------------- # # Host path browser # --------------------------------------------------------------------------- # @router.get("/api/host/paths") def host_paths( path: str = Query("/"), show_hidden: bool = Query(False), _user: User = Depends(get_current_user), ) -> dict: try: return device_service.browse(path, show_hidden) except device_service.BrowseError as exc: raise HTTPException(status_code=400, detail=str(exc)) from exc