from __future__ import annotations from fastapi import APIRouter, Depends from sqlmodel import Session from ..connectors.sonarr import SonarrConnector from ..db.session import get_session from ..services.approval_service import create_approval from ..services.audit_logger import log_event from ..services.job_runner import complete_job, create_job, set_job_progress, start_job from ..services.policy_engine import evaluate_policy from .connector_runtime import load_connector_runtime router = APIRouter(prefix='/api/apps/sonarr', dependencies=[Depends(load_connector_runtime)]) _ADD_SERIES_STEPS = [ 'Validate payload', 'Evaluate policy', 'Resolve root folder / quality profile defaults', 'Add series in Sonarr', 'Optional search missing episodes', 'Verify series exists', 'Complete audit/job', ] @router.get('/health') def health(): return SonarrConnector().health() @router.get('/queue') def queue(): return SonarrConnector().queue_status() @router.get('/wanted') def wanted(): return SonarrConnector().wanted_missing() @router.get('/search') def search(q: str = ''): query = q.strip() if not query: return {'success': False, 'status': 'error', 'message': 'query_required', 'items': []} return SonarrConnector().search_series(query) @router.post('/series') def add_series(payload: dict, session: Session = Depends(get_session)): action = 'sonarr.add_series' target = 'sonarr' source = str(payload.get('source', 'manual')).strip() or 'manual' policy = evaluate_policy(session=session, action=action, target=target, source=source) risk = str(policy.get('risk', 'low')) if policy['decision'] == 'blocked': audit = log_event( session, source='action', identity=source, action_id=action, target=target, risk=risk, policy_decision='blocked', reason=policy['reason'], result='blocked', execution_plan={'plan': _ADD_SERIES_STEPS, 'policyReason': policy['reason']}, ) return { 'success': False, 'decision': 'blocked', 'status': 'failed', 'policy_decision': 'blocked', 'result': 'blocked', 'message': policy['reason'], 'auditId': audit.id, } if policy['decision'] == 'approval_required': approval = create_approval( session, action=action, target=target, source=source, risk=risk, reason=policy['reason'], requester=source, ) audit = log_event( session, source='action', identity=source, action_id=action, target=target, risk=risk, policy_decision='approval_required', reason=policy['reason'], result='approval_requested', execution_plan={'plan': _ADD_SERIES_STEPS, 'approvalId': approval.id}, redacted_metadata={'approval_id': approval.id}, ) return { 'success': False, 'decision': 'approval_required', 'status': 'failed', 'policy_decision': 'approval_required', 'result': 'approval_required', 'message': 'Action requires approval before execution.', 'approvalId': approval.id, 'auditId': audit.id, } job = create_job( session, action_id=action, target=target, source=source, risk=risk, policy_decision='allowed', steps=_ADD_SERIES_STEPS, ) start_job(session, job) set_job_progress(session, job, 35, 'Payload validated and policy allowed.') connector_result = SonarrConnector().add_series(payload) success = bool(connector_result.get('success')) result_status = 'succeeded' if success else 'failed' message = str(connector_result.get('message', 'series_added' if success else 'sonarr_failed')) completed = complete_job( session, job, status=result_status, result_summary=message, ) audit = log_event( session, source='action', identity=source, action_id=action, target=target, risk=risk, policy_decision='allowed', reason=policy['reason'], result='success' if success else 'failed', execution_plan={'plan': _ADD_SERIES_STEPS, 'jobId': completed.id}, after_state={'status': connector_result.get('status'), 'message': message}, redacted_metadata={'connector': 'sonarr'}, ) if not success: return { 'success': False, 'decision': 'failed', 'status': 'failed', 'policy_decision': 'allowed', 'result': 'failed', 'message': message, 'jobId': completed.id, 'auditId': audit.id, 'data': connector_result.get('data'), } return { 'success': True, 'decision': 'allowed', 'status': 'success', 'policy_decision': 'allowed', 'result': 'success', 'message': message, 'jobId': completed.id, 'auditId': audit.id, 'data': connector_result.get('data', {}), }