Files
Unraid-AppOps-Gateway-Public/backend/app/api/routes_radarr.py
T

178 lines
5.2 KiB
Python

from __future__ import annotations
from fastapi import APIRouter, Depends
from sqlmodel import Session
from ..connectors.radarr import RadarrConnector
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/radarr', dependencies=[Depends(load_connector_runtime)])
_ADD_MOVIE_STEPS = [
'Validate payload',
'Evaluate policy',
'Resolve root folder / quality profile defaults',
'Add movie in Radarr',
'Optional search movie',
'Verify movie exists',
'Complete audit/job',
]
@router.get('/health')
def health():
return RadarrConnector().health()
@router.get('/queue')
def queue():
return RadarrConnector().queue_status()
@router.get('/wanted')
def wanted():
return RadarrConnector().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 RadarrConnector().search_movie(query)
@router.post('/movie')
def add_movie(payload: dict, session: Session = Depends(get_session)):
action = 'radarr.add_movie'
target = 'radarr'
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_MOVIE_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_MOVIE_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_MOVIE_STEPS,
)
start_job(session, job)
set_job_progress(session, job, 35, 'Payload validated and policy allowed.')
connector_result = RadarrConnector().add_movie(payload)
success = bool(connector_result.get('success'))
result_status = 'succeeded' if success else 'failed'
message = str(connector_result.get('message', 'movie_added' if success else 'radarr_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_MOVIE_STEPS, 'jobId': completed.id},
after_state={'status': connector_result.get('status'), 'message': message},
redacted_metadata={'connector': 'radarr'},
)
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', {}),
}