Initial curated Unraid-AppOps-Gateway source from 3bd6f6fee310125bfbb4d4650389a817f498724d

This commit is contained in:
Public Source Publisher committed 2026-10-01 01:35:57 +02:00
commit 1dfbca08a1
528 files changed
+131054

No files matched your search

+177
View File
@@ -0,0 +1,177 @@
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', {}),
}