| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748 |
- """Polling endpoint for the in-memory slice-job dispatcher.
- POST /library/files/{id}/slice and POST /archives/{id}/slice return a
- job_id and a status_url pointing here. The frontend polls this until
- status flips to `completed` or `failed`.
- """
- from fastapi import APIRouter, HTTPException
- from backend.app.core.auth import RequirePermissionIfAuthEnabled
- from backend.app.core.permissions import Permission
- from backend.app.models.user import User
- from backend.app.services.slice_dispatch import slice_dispatch
- router = APIRouter(prefix="/slice-jobs", tags=["slice-jobs"])
- @router.get("/{job_id}")
- async def get_slice_job(
- job_id: int,
- # Job IDs are sequential integers and the body leaks source filenames
- # plus the resulting library_file_id / archive_id. Gate on LIBRARY_READ
- # — same baseline a user needs to see slice sources or results.
- _: User | None = RequirePermissionIfAuthEnabled(Permission.LIBRARY_READ),
- ):
- job = slice_dispatch.get(job_id)
- if job is None:
- raise HTTPException(status_code=404, detail="Slice job not found or expired")
- body: dict = {
- "job_id": job.id,
- "status": job.status,
- "kind": job.kind,
- "source_id": job.source_id,
- "source_name": job.source_name,
- "created_at": job.created_at.isoformat(),
- "started_at": job.started_at.isoformat() if job.started_at else None,
- "completed_at": job.completed_at.isoformat() if job.completed_at else None,
- # Live progress fed by the sidecar's --pipe channel. Null when
- # the slicer hasn't emitted yet (early "Initializing" phase) or
- # the sidecar doesn't support progress (older versions).
- "progress": job.progress,
- }
- if job.status == "completed":
- body["result"] = job.result
- elif job.status == "failed":
- body["error_status"] = job.error_status
- body["error_detail"] = job.error_detail
- return body
|