| .. | .. |
|---|
| 3 | 3 | from fastapi import APIRouter, Depends, HTTPException |
|---|
| 4 | 4 | |
|---|
| 5 | 5 | from app.auth import verify_token |
|---|
| 6 | | -from app.ops_runner import run_ops, run_ops_json, _BACKUP_TIMEOUT |
|---|
| 6 | +from app.ops_runner import run_ops, run_ops_json, run_ops_host, _BACKUP_TIMEOUT |
|---|
| 7 | 7 | |
|---|
| 8 | 8 | router = APIRouter() |
|---|
| 9 | 9 | |
|---|
| .. | .. |
|---|
| 12 | 12 | async def list_backups( |
|---|
| 13 | 13 | _: str = Depends(verify_token), |
|---|
| 14 | 14 | ) -> list[dict[str, Any]]: |
|---|
| 15 | | - """ |
|---|
| 16 | | - Returns a list of local backup records from `ops backups --json`. |
|---|
| 17 | | - """ |
|---|
| 15 | + """Returns a list of local backup records from `ops backups --json`.""" |
|---|
| 18 | 16 | result = await run_ops_json(["backups"]) |
|---|
| 19 | 17 | if not result["success"]: |
|---|
| 20 | | - raise HTTPException( |
|---|
| 21 | | - status_code=500, |
|---|
| 22 | | - detail=f"Failed to list backups: {result['error']}", |
|---|
| 23 | | - ) |
|---|
| 18 | + raise HTTPException(status_code=500, detail=f"Failed to list backups: {result['error']}") |
|---|
| 24 | 19 | |
|---|
| 25 | 20 | data = result["data"] |
|---|
| 26 | 21 | if isinstance(data, list): |
|---|
| .. | .. |
|---|
| 37 | 32 | async def list_offsite_backups( |
|---|
| 38 | 33 | _: str = Depends(verify_token), |
|---|
| 39 | 34 | ) -> list[dict[str, Any]]: |
|---|
| 40 | | - """ |
|---|
| 41 | | - Returns a list of offsite backup records, querying each project separately. |
|---|
| 42 | | - """ |
|---|
| 35 | + """Returns a list of offsite backup records.""" |
|---|
| 43 | 36 | all_backups = [] |
|---|
| 44 | 37 | for project in ["mdf", "seriousletter"]: |
|---|
| 45 | 38 | result = await run_ops_json(["offsite", "list", project]) |
|---|
| .. | .. |
|---|
| 57 | 50 | _: str = Depends(verify_token), |
|---|
| 58 | 51 | ) -> dict[str, Any]: |
|---|
| 59 | 52 | """ |
|---|
| 60 | | - Runs `ops backup {project} {env}` and returns the result. |
|---|
| 53 | + Runs `ops backup {project} {env}` on the host. |
|---|
| 54 | + |
|---|
| 55 | + Runs via nsenter because ops backup delegates to project CLIs |
|---|
| 56 | + that use host Python venvs. |
|---|
| 61 | 57 | """ |
|---|
| 62 | | - result = await run_ops(["backup", project, env], timeout=_BACKUP_TIMEOUT) |
|---|
| 58 | + result = await run_ops_host(["backup", project, env], timeout=_BACKUP_TIMEOUT) |
|---|
| 63 | 59 | if not result["success"]: |
|---|
| 64 | 60 | raise HTTPException( |
|---|
| 65 | 61 | status_code=500, |
|---|
| .. | .. |
|---|
| 79 | 75 | env: str, |
|---|
| 80 | 76 | _: str = Depends(verify_token), |
|---|
| 81 | 77 | ) -> dict[str, Any]: |
|---|
| 82 | | - """ |
|---|
| 83 | | - Runs `ops offsite upload {project} {env}` and returns the result. |
|---|
| 84 | | - """ |
|---|
| 85 | | - result = await run_ops( |
|---|
| 78 | + """Runs `ops offsite upload {project} {env}` on the host.""" |
|---|
| 79 | + result = await run_ops_host( |
|---|
| 86 | 80 | ["offsite", "upload", project, env], timeout=_BACKUP_TIMEOUT |
|---|
| 87 | 81 | ) |
|---|
| 88 | 82 | if not result["success"]: |
|---|
| .. | .. |
|---|
| 90 | 84 | status_code=500, |
|---|
| 91 | 85 | detail=f"Offsite upload failed: {result['error'] or result['output']}", |
|---|
| 92 | 86 | ) |
|---|
| 93 | | - return { |
|---|
| 94 | | - "success": True, |
|---|
| 95 | | - "output": result["output"], |
|---|
| 96 | | - "project": project, |
|---|
| 97 | | - "env": env, |
|---|
| 98 | | - } |
|---|
| 87 | + return {"success": True, "output": result["output"], "project": project, "env": env} |
|---|
| 99 | 88 | |
|---|
| 100 | 89 | |
|---|
| 101 | 90 | @router.post("/offsite/retention", summary="Apply offsite retention policy") |
|---|
| 102 | 91 | async def apply_retention( |
|---|
| 103 | 92 | _: str = Depends(verify_token), |
|---|
| 104 | 93 | ) -> dict[str, Any]: |
|---|
| 105 | | - """ |
|---|
| 106 | | - Runs `ops offsite retention` and returns the result. |
|---|
| 107 | | - """ |
|---|
| 108 | | - result = await run_ops(["offsite", "retention"], timeout=_BACKUP_TIMEOUT) |
|---|
| 94 | + """Runs `ops offsite retention` on the host.""" |
|---|
| 95 | + result = await run_ops_host(["offsite", "retention"], timeout=_BACKUP_TIMEOUT) |
|---|
| 109 | 96 | if not result["success"]: |
|---|
| 110 | 97 | raise HTTPException( |
|---|
| 111 | 98 | status_code=500, |
|---|
| 112 | 99 | detail=f"Retention policy failed: {result['error'] or result['output']}", |
|---|
| 113 | 100 | ) |
|---|
| 114 | | - return { |
|---|
| 115 | | - "success": True, |
|---|
| 116 | | - "output": result["output"], |
|---|
| 117 | | - } |
|---|
| 101 | + return {"success": True, "output": result["output"]} |
|---|