from __future__ import annotations from collections.abc import Callable from typing import Any from fastapi import APIRouter, Depends from sqlmodel import Session from ..connectors.tautulli import TautulliConnector from ..db.session import get_session from ..services.audit_logger import log_event from ..services.policy_engine import evaluate_policy from .connector_runtime import load_connector_runtime router = APIRouter(prefix='/api/apps/tautulli', dependencies=[Depends(load_connector_runtime)]) def _read( session: Session, *, action: str, source: str, risk_default: str, call: Callable[[TautulliConnector], dict[str, Any]], audit_read: bool = False, ) -> dict[str, Any]: identity = source.strip() or 'manual' policy = evaluate_policy(session=session, action=action, target='tautulli', source=identity) risk = str(policy.get('risk', risk_default)) if policy['decision'] == 'blocked': audit = log_event( session, source='read', identity=identity, action_id=action, target='tautulli', risk=risk, policy_decision='blocked', reason=policy['reason'], result='blocked', redacted_metadata={'privacySensitive': audit_read}, ) return {'success': False, 'decision': 'blocked', 'status': 'failed', 'message': policy['reason'], 'auditId': audit.id} if policy['decision'] == 'approval_required': audit = log_event( session, source='read', identity=identity, action_id=action, target='tautulli', risk=risk, policy_decision='approval_required', reason=policy['reason'], result='approval_required', redacted_metadata={'privacySensitive': audit_read}, ) return {'success': False, 'decision': 'approval_required', 'status': 'failed', 'message': 'Action requires approval before execution.', 'auditId': audit.id} result = call(TautulliConnector()) if audit_read: log_event( session, source='read', identity=identity, action_id=action, target='tautulli', risk=risk, policy_decision='allowed', reason=policy['reason'], result='success' if result.get('success') else 'failed', redacted_metadata={'privacySensitive': True, 'status': result.get('status')}, ) return result @router.get('/health') def health(): return TautulliConnector().health() @router.get('/status') def status(): return TautulliConnector().status() @router.get('/activity') def activity(): return TautulliConnector().activity() @router.get('/history') def history(limit: int = 25, source: str = 'manual', session: Session = Depends(get_session)): return _read(session, action='tautulli.history', source=source, risk_default='medium', call=lambda connector: connector.history(limit), audit_read=True) @router.get('/users') def users(source: str = 'manual', session: Session = Depends(get_session)): return _read(session, action='tautulli.users', source=source, risk_default='medium', call=lambda connector: connector.users(), audit_read=True) @router.get('/libraries') def libraries(): return TautulliConnector().libraries() @router.get('/recently-added') def recently_added(): return TautulliConnector().recently_added() @router.get('/watch-stats') def watch_stats(source: str = 'manual', session: Session = Depends(get_session)): return _read(session, action='tautulli.watch_stats', source=source, risk_default='medium', call=lambda connector: connector.watch_stats(), audit_read=True) @router.get('/streams') def streams(): return TautulliConnector().stream_summary() @router.get('/plex-info') def plex_info(): return TautulliConnector().plex_info()