diff --git a/backend/app/routers/jobs.py b/backend/app/routers/jobs.py index 093505c..7d902b4 100644 --- a/backend/app/routers/jobs.py +++ b/backend/app/routers/jobs.py @@ -1,19 +1,63 @@ -from fastapi import APIRouter, HTTPException +import json +from datetime import datetime, timezone -from app.schemas import EntityResponse, Job, ListResponse -from app.services.store import JOBS +from fastapi import APIRouter, Depends, HTTPException, Query +from sqlalchemy.orm import Session + +from app.database import get_db +from app.models import JobModel +from app.schemas import EntityResponse, Job, JobStatus, ListResponse router = APIRouter(prefix='/jobs', tags=['jobs']) +def _to_job(m: JobModel) -> Job: + return Job( + id=m.id, type=m.type, status=m.status, + progress=m.progress, message=m.message, error=m.error, + payload=json.loads(m.payload_json or '{}'), + result=json.loads(m.result_json or 'null'), + created_at=m.created_at, + started_at=m.started_at, + finished_at=m.finished_at, + ) + + @router.get('', response_model=ListResponse[Job]) -def list_jobs() -> ListResponse[Job]: - return ListResponse(count=len(JOBS), items=JOBS) +def list_jobs( + status: JobStatus | None = Query(default=None), + type: str | None = Query(default=None), + db: Session = Depends(get_db), +): + q = db.query(JobModel) + if status: + q = q.filter(JobModel.status == status) + if type: + q = q.filter(JobModel.type == type) + rows = q.order_by(JobModel.created_at.desc()).all() + return ListResponse(count=len(rows), items=[_to_job(r) for r in rows]) @router.get('/{job_id}', response_model=EntityResponse[Job]) -def get_job(job_id: str) -> EntityResponse[Job]: - for item in JOBS: - if item.id == job_id: - return EntityResponse(item=item) - raise HTTPException(status_code=404, detail='Job not found') +def get_job(job_id: str, db: Session = Depends(get_db)): + row = db.query(JobModel).filter(JobModel.id == job_id).first() + if not row: + raise HTTPException(status_code=404, detail='Job not found') + return EntityResponse(item=_to_job(row)) + + +@router.post('/{job_id}/cancel', response_model=EntityResponse[Job]) +def cancel_job(job_id: str, db: Session = Depends(get_db)): + row = db.query(JobModel).filter(JobModel.id == job_id).first() + if not row: + raise HTTPException(status_code=404, detail='Job not found') + if row.status not in ('pending', 'running'): + raise HTTPException( + status_code=409, + detail=f'Cannot cancel job with status {row.status!r}', + ) + row.status = 'cancelled' + row.finished_at = datetime.now(timezone.utc) + db.commit() + db.refresh(row) + return EntityResponse(item=_to_job(row))