from __future__ import annotations from fastapi import APIRouter, Depends from ..db.session import get_session from ..services.audit_logger import log_event from ..services.job_runner import cancel_job, get_job, list_jobs router = APIRouter(prefix='/api/jobs') @router.get('') def list_all(session=Depends(get_session)): return list_jobs(session) @router.get('/{job_id}') def get_single(job_id: str, session=Depends(get_session)): job = get_job(session, job_id) if not job: return {'success': False, 'message': f'Job {job_id} not found'} return job @router.post('/{job_id}/cancel') def cancel(job_id: str, session=Depends(get_session)): job = get_job(session, job_id) if not job: return {'success': False, 'message': 'Job not found', 'id': job_id} from ..services.privatemuse_deployment_recovery import ( privatemuse_recovery_job_cancellation_reason, ) cancellation_reason = privatemuse_recovery_job_cancellation_reason(session, job) if not cancellation_reason: from ..services.workmesh_first_deployment import ( workmesh_first_deploy_job_cancellation_reason, ) cancellation_reason = workmesh_first_deploy_job_cancellation_reason(session, job) if cancellation_reason: return { 'success': False, 'message': cancellation_reason, 'id': job.id, 'status': job.status, } if job.status in {'succeeded', 'failed', 'cancelled'}: return {'success': False, 'message': f'Job already {job.status}', 'id': job.id} previous_status = job.status updated = cancel_job(session, job) audit = log_event( session, source='manual', identity='operator', action_id='job.cancel', target=updated.id, target_type='job', risk='low', policy_decision='allowed', reason='Operator cancelled an AppOps job record.', result='success', before_state={'status': previous_status}, after_state={'status': updated.status}, redacted_metadata={'jobId': updated.id, 'actionId': updated.action_id}, ) return {'success': True, 'id': updated.id, 'status': updated.status, 'auditId': audit.id}