# retoor import logging from pathlib import Path from fastapi import APIRouter, Request from fastapi.responses import FileResponse, JSONResponse from devplacepy.services.jobs import queue from devplacepy.services.jobs.zip_service import ZIPS_DIR from devplacepy.schemas import ZipJobOut from devplacepy.utils import not_found logger = logging.getLogger(__name__) router = APIRouter() RETENTION_EXTEND_SECONDS = 7 * 24 * 60 * 60 def _status_payload(job: dict) -> dict: result = job.get("result", {}) done = job.get("status") == queue.DONE return { "uid": job.get("uid", ""), "kind": job.get("kind", ""), "status": job.get("status", ""), "preferred_name": job.get("preferred_name"), "download_url": result.get("download_url") if done else None, "error": job.get("error") or None, "bytes_in": int(job.get("bytes_in") or 0), "bytes_out": int(job.get("bytes_out") or 0), "item_count": int(job.get("item_count") or 0), "file_count": int(result.get("file_count") or 0), "dir_count": int(result.get("dir_count") or 0), "created_at": job.get("created_at"), "completed_at": job.get("completed_at") or None, } @router.get("/{uid}") async def zip_status(request: Request, uid: str): job = queue.get_job(uid) if not job or job.get("kind") != "zip": raise not_found("Job not found") return JSONResponse( ZipJobOut.model_validate(_status_payload(job)).model_dump(mode="json") ) @router.get("/{uid}/download") async def zip_download(request: Request, uid: str): job = queue.get_job(uid) if not job or job.get("kind") != "zip" or job.get("status") != queue.DONE: raise not_found("Archive not available") result = job.get("result", {}) local_path = result.get("local_path", "") resolved = Path(local_path).resolve() if ( not local_path or not resolved.is_relative_to(ZIPS_DIR.resolve()) or not resolved.is_file() ): raise not_found("Archive not available") queue.touch_job(uid, RETENTION_EXTEND_SECONDS) return FileResponse( resolved, filename=result.get("final_name", "download.zip"), media_type="application/zip", )