Files
bambuddy/backend/app/api/routes/slice_jobs.py

57 lines
2.2 KiB
Python

"""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, Depends, HTTPException
from backend.app.core.auth import require_ownership_permission
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 the library
# read permission family (own/all). NOTE: SliceJob is in-memory with no
# owner field, so we cannot per-row scope; callers with either OWN or
# ALL can poll any job_id. Adding owner_id to SliceJob is the proper
# follow-up (out of scope for the IDOR fix train).
_: tuple[User | None, bool] = Depends(
require_ownership_permission(
Permission.LIBRARY_READ_ALL,
Permission.LIBRARY_READ_OWN,
)
),
):
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