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

123 lines
3.9 KiB
Python

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()