"""동기화 실행 + 진행률 폴링 (화면 2). POST /repos/{uuid}/sync → 동기화 시작, 진행률 바 조각 반환 (이후 3초 폴링) GET /repos/{uuid}/progress → 진행률 바 조각 (폴링 대상) 동기화는 외부 미러에서 새 스냅샷을 만들 뿐 운영망 배포가 아니므로 비교적 안전 → 확인 모달 없이 바로 실행한다(스펙 §5-1). 폴링은 화면(HTMX)이 담당한다(스펙 §7). """ from __future__ import annotations import httpx from fastapi import APIRouter, Depends, Request from fastapi.responses import HTMLResponse from .. import task_store, views from ..deps import get_pulp_client, templates from ..pulp_client import PulpClient router = APIRouter() def _render(request: Request, uuid: str, progress: dict, status_code: int = 200): return templates.TemplateResponse( request, "partials/progress.html", {"uuid": uuid, "progress": progress}, status_code=status_code, ) @router.post("/repos/{uuid}/sync", response_class=HTMLResponse) def start_sync( request: Request, uuid: str, pulp: PulpClient = Depends(get_pulp_client) ) -> HTMLResponse: try: repo = pulp.get_repo(uuid) remote = repo.get("remote") if not remote: return _render( request, uuid, { "running": False, "completed": False, "failed": True, "error": "이 저장소에 remote(미러 소스)가 설정되어 있지 않습니다.", }, status_code=400, ) task_href = pulp.sync_repo(uuid, remote) except httpx.HTTPError as exc: return _render( request, uuid, { "running": False, "completed": False, "failed": True, "error": f"동기화 시작 실패: {exc}", }, status_code=502, ) task_store.set_task(uuid, task_href) # 첫 조각: 폴링 시작 (아직 progress_reports 없음 → 준비 중) return _render( request, uuid, { "running": True, "completed": False, "failed": False, "state": "waiting", "done": 0, "total": 0, "percent": 0, }, ) @router.get("/repos/{uuid}/progress", response_class=HTMLResponse) def progress( request: Request, uuid: str, pulp: PulpClient = Depends(get_pulp_client) ) -> HTMLResponse: task_href = task_store.get_task(uuid) if not task_href: # 추적 중인 sync 없음 → 폴링 중단(빈 조각) return _render( request, uuid, {"running": False, "completed": False, "failed": False, "idle": True}, ) try: task = pulp.get_task(task_href) except httpx.HTTPError as exc: task_store.clear_task(uuid) return _render( request, uuid, { "running": False, "completed": False, "failed": True, "error": f"작업 상태 조회 실패: {exc}", }, status_code=502, ) prog = views.task_progress(task) if prog["completed"] or prog["failed"]: task_store.clear_task(uuid) # 폴링 종료 + 동기화중 카운트에서 제외 return _render(request, uuid, prog)