"""Small internal operational API. These endpoints are intentionally read-only and are used by future HTMX/API partials and external monitoring. They do not replace the current admin pages. """ from __future__ import annotations from hmac import compare_digest from fastapi import APIRouter, Depends, HTTPException, Request from sqlalchemy import text from app.config import settings from app.db import engine def require_internal_access(request: Request) -> None: expected = (settings.clientflow_admin_token or "").strip() if not expected: return received = ( request.headers.get("X-ClientFlow-Admin-Token") or request.cookies.get("clientflow_admin_token") or request.query_params.get("admin_token") or "" ).strip() if not received or not compare_digest(received, expected): raise HTTPException(status_code=401, detail="internal api auth required") router = APIRouter(prefix="/api/internal", tags=["internal"], dependencies=[Depends(require_internal_access)]) @router.get("/health") def internal_health() -> dict: return { "status": "ok", "app": settings.app_name, "env": settings.env, "jasmin_enabled": bool(settings.jasmin_enabled), "packlink_enabled": bool(settings.packlink_enabled), } @router.get("/outbox/summary") def outbox_summary() -> dict: with engine.begin() as conn: rows = conn.execute(text(""" SELECT target_system, status, count(*)::int AS total FROM integration_outbox GROUP BY target_system, status ORDER BY target_system, status """)).mappings().all() summary: dict[str, dict[str, int]] = {} for row in rows: target = row["target_system"] or "unknown" status = row["status"] or "unknown" summary.setdefault(target, {})[status] = row["total"] return {"items": summary} @router.get("/documents/summary") def documents_summary() -> dict: with engine.begin() as conn: rows = conn.execute(text(""" SELECT document_kind, status, count(*)::int AS total FROM commercial_documents GROUP BY document_kind, status ORDER BY document_kind, status """)).mappings().all() summary: dict[str, dict[str, int]] = {} for row in rows: kind = row["document_kind"] or "unknown" status = row["status"] or "unknown" summary.setdefault(kind, {})[status] = row["total"] return {"items": summary} @router.get("/operations/summary") def operations_summary() -> dict: from app.operations_service import get_operations_summary return get_operations_summary(limit=10) @router.get("/system/health") def system_health_summary() -> dict: from app.operations_service import get_system_health_summary return get_system_health_summary() @router.get("/communications/summary") def communications_summary() -> dict: from app.communication_service import get_communications_summary return get_communications_summary() @router.get("/communications/recent") def communications_recent(limit: int = 20) -> dict: from app.communication_service import list_communications return {"items": list_communications(limit=limit)}