import httpx from fastapi import APIRouter from config import DOCKER_PROXY_URL router = APIRouter() @router.get("/containers") async def list_containers(): async with httpx.AsyncClient() as c: r = await c.get(f"{DOCKER_PROXY_URL}/containers/json", params={"all": "true"}) return [{"id": x["Id"][:12], "name": x["Names"][0].lstrip("/"), "state": x["State"], "status": x["Status"], "image": x["Image"]} for x in r.json()] @router.post("/containers/{container_id}/{action}") async def container_action(container_id: str, action: str): if action not in ("start", "stop", "restart"): return {"error": "invalid action"} async with httpx.AsyncClient() as c: r = await c.post(f"{DOCKER_PROXY_URL}/containers/{container_id}/{action}") return {"ok": r.status_code < 400} @router.get("/containers/{container_id}/logs") async def container_logs(container_id: str, tail: int = 200): async with httpx.AsyncClient() as c: r = await c.get(f"{DOCKER_PROXY_URL}/containers/{container_id}/logs", params={"stdout": "true", "stderr": "true", "tail": str(tail), "timestamps": "true"}) # Strip docker stream header bytes (8 bytes per line) lines = [] raw = r.content i = 0 while i < len(raw): if i + 8 <= len(raw): size = int.from_bytes(raw[i+4:i+8], "big") lines.append(raw[i+8:i+8+size].decode("utf-8", errors="replace")) i += 8 + size else: break return {"logs": "".join(lines)}