import subprocess from app_images import init_images_router from app_files import init_files_router from app_pods import init_pods_router from app_containers import init_containers_router, start_stats_poller from app_networks import init_networks_router from app_system import init_system_router from fastapi import FastAPI, HTTPException import requests_unixsocket from common import ( _systemctl as _common_systemctl, ) import uvicorn app = FastAPI(title="Podman MVP Control Plane", root_path="/api") SESSION = requests_unixsocket.Session() PODMAN_API_BASE = "http+unix://%2Frun%2Fuser%2F1000%2Fpodman%2Fpodman.sock/v5.4.2" WORKLOADS_DIR = "/app/workloads" @app.on_event("startup") async def _startup_stats_poller(): await start_stats_poller() def _systemctl(cmd): return _common_systemctl(cmd, run) def _run_systemctl_action(action: str, unit: str): cmd = ["systemctl", "--user", action, unit] return _systemctl(cmd) # --- ROUTERS --- # Images API lives in dedicated modules to keep this file from growing further. app.include_router(init_system_router(SESSION, PODMAN_API_BASE, WORKLOADS_DIR)) app.include_router(init_images_router(SESSION, PODMAN_API_BASE)) app.include_router(init_files_router(SESSION, PODMAN_API_BASE, WORKLOADS_DIR)) app.include_router(init_networks_router(SESSION, PODMAN_API_BASE)) app.include_router(init_containers_router( SESSION, PODMAN_API_BASE, WORKLOADS_DIR, _systemctl, )) app.include_router(init_pods_router( SESSION, PODMAN_API_BASE, WORKLOADS_DIR, _systemctl, )) @app.post("/daemon-reload") def api_daemon_reload(): try: code, out = _systemctl(["systemctl", "--user", "daemon-reload"]) return { "cmd": "systemctl --user daemon-reload", "exit": code, "output": out, } except Exception as e: raise HTTPException(status_code=500, detail=str(e)) @app.post("/{action}/{unit}") def api_action(action: str, unit: str): if action not in ("status", "start", "stop", "restart"): raise HTTPException(status_code=400, detail="Invalid action") cmd = ["systemctl", "--user", action, unit] code, out = _run_systemctl_action(action, unit) return {"cmd": " ".join(cmd), "exit": code, "output": out} @app.post("/api//") def legacy_api_action(action: str, unit: str): # legacy flask-like path; keep behavior (even if not used by index.html) if action not in ("status", "start", "stop", "restart"): return {"error": "Invalid action"}, 400 cmd = ["systemctl", "--user", action, unit] code, out = _run_systemctl_action(action, unit) return {"cmd": " ".join(cmd), "exit": code, "output": out} def run(cmd): try: result = subprocess.run(cmd, capture_output=True, text=True, check=False) output = (result.stdout or "") + (result.stderr or "") return result.returncode, output.strip() except Exception as e: return 1, str(e) if __name__ == "__main__": uvicorn.run(app, host="0.0.0.0", port=8000)