slice_jobs.py 1.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344
  1. """Polling endpoint for the in-memory slice-job dispatcher.
  2. POST /library/files/{id}/slice and POST /archives/{id}/slice return a
  3. job_id and a status_url pointing here. The frontend polls this until
  4. status flips to `completed` or `failed`.
  5. """
  6. from fastapi import APIRouter, HTTPException
  7. from backend.app.core.auth import RequirePermissionIfAuthEnabled
  8. from backend.app.core.permissions import Permission
  9. from backend.app.models.user import User
  10. from backend.app.services.slice_dispatch import slice_dispatch
  11. router = APIRouter(prefix="/slice-jobs", tags=["slice-jobs"])
  12. @router.get("/{job_id}")
  13. async def get_slice_job(
  14. job_id: int,
  15. # Job IDs are sequential integers and the body leaks source filenames
  16. # plus the resulting library_file_id / archive_id. Gate on LIBRARY_READ
  17. # — same baseline a user needs to see slice sources or results.
  18. _: User | None = RequirePermissionIfAuthEnabled(Permission.LIBRARY_READ),
  19. ):
  20. job = slice_dispatch.get(job_id)
  21. if job is None:
  22. raise HTTPException(status_code=404, detail="Slice job not found or expired")
  23. body: dict = {
  24. "job_id": job.id,
  25. "status": job.status,
  26. "kind": job.kind,
  27. "source_id": job.source_id,
  28. "source_name": job.source_name,
  29. "created_at": job.created_at.isoformat(),
  30. "started_at": job.started_at.isoformat() if job.started_at else None,
  31. "completed_at": job.completed_at.isoformat() if job.completed_at else None,
  32. }
  33. if job.status == "completed":
  34. body["result"] = job.result
  35. elif job.status == "failed":
  36. body["error_status"] = job.error_status
  37. body["error_detail"] = job.error_detail
  38. return body