1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
| | from typing import Any
| |
| | from fastapi import APIRouter, Depends, HTTPException, Query
| |
| | from app.auth import verify_token
| | from app.ops_runner import run_command
| |
| | router = APIRouter()
| |
| | _DOCKER = "docker"
| |
| |
| | def _container_name(project: str, env: str, service: str) -> str:
| | """
| | Derive the Docker container name from project, env, and service.
| | Docker Compose v2 default: {project}-{env}-{service}-1
| | """
| | return f"{project}-{env}-{service}-1"
| |
| |
| | @router.get("/logs/{project}/{env}/{service}", summary="Get container logs")
| | async def get_logs(
| | project: str,
| | env: str,
| | service: str,
| | lines: int = Query(default=100, ge=1, le=10000, description="Number of log lines to return"),
| | _: str = Depends(verify_token),
| | ) -> dict[str, Any]:
| | """
| | Fetch the last N lines of logs from a container.
| | Uses `docker logs --tail {lines} {container}`.
| | """
| | container = _container_name(project, env, service)
| | result = await run_command(
| | [_DOCKER, "logs", "--tail", str(lines), container],
| | timeout=30,
| | )
| |
| | # docker logs writes to stderr by default; treat combined output as logs
| | combined = result["output"] + result["error"]
| |
| | if not result["success"] and not combined.strip():
| | raise HTTPException(
| | status_code=500,
| | detail=f"Failed to retrieve logs for container '{container}'",
| | )
| |
| | return {
| | "container": container,
| | "lines": lines,
| | "logs": combined,
| | }
| |
| |
| | @router.post("/restart/{project}/{env}/{service}", summary="Restart a container")
| | async def restart_service(
| | project: str,
| | env: str,
| | service: str,
| | _: str = Depends(verify_token),
| | ) -> dict[str, Any]:
| | """
| | Restart a Docker container via `docker restart {container}`.
| | """
| | container = _container_name(project, env, service)
| | result = await run_command(
| | [_DOCKER, "restart", container],
| | timeout=60,
| | )
| |
| | if not result["success"]:
| | raise HTTPException(
| | status_code=500,
| | detail=f"Failed to restart container '{container}': {result['error'] or result['output']}",
| | )
| |
| | return {
| | "success": True,
| | "container": container,
| | "message": f"Container '{container}' restarted successfully",
| | }
|
|