"""Health endpoint - probes Postgres, MinIO, OpenSearch, Qdrant, Redis.""" from __future__ import annotations from typing import Any from fastapi import APIRouter from sqlalchemy import text from app import __version__ from app.api.schemas import ComponentHealth, HealthResponse from app.config import settings from app.db.session import get_engine from app.logging_config import get_logger from app.storage.minio_client import get_storage logger = get_logger(__name__) router = APIRouter(tags=["health"]) def _check_postgres() -> ComponentHealth: try: with get_engine().connect() as conn: conn.execute(text("SELECT 1")) return ComponentHealth(name="postgres", status="ok") except Exception as exc: # noqa: BLE001 return ComponentHealth(name="postgres", status="error", detail={"error": str(exc)}) def _check_minio() -> ComponentHealth: info: dict[str, Any] = get_storage().health() if info.get("status") == "ok": return ComponentHealth(name="minio", status="ok", detail=info) return ComponentHealth(name="minio", status="error", detail=info) def _check_opensearch() -> ComponentHealth: try: from app.indexing.opensearch_client import get_opensearch client = get_opensearch() info = client.cluster.health() cluster_status = info.get("status") status = "ok" if cluster_status in ("green", "yellow") else "degraded" return ComponentHealth( name="opensearch", status=status, # type: ignore[arg-type] detail={"cluster_status": cluster_status, "nodes": info.get("number_of_nodes")}, ) except Exception as exc: # noqa: BLE001 return ComponentHealth(name="opensearch", status="error", detail={"error": str(exc)}) def _check_qdrant() -> ComponentHealth: try: from app.indexing.qdrant_client import get_qdrant client = get_qdrant() cols = client.get_collections() return ComponentHealth( name="qdrant", status="ok", detail={"collections": [c.name for c in cols.collections]}, ) except Exception as exc: # noqa: BLE001 return ComponentHealth(name="qdrant", status="error", detail={"error": str(exc)}) def _check_redis() -> ComponentHealth: try: import redis r = redis.Redis.from_url(settings.redis_url, socket_connect_timeout=2) r.ping() return ComponentHealth(name="redis", status="ok") except Exception as exc: # noqa: BLE001 return ComponentHealth(name="redis", status="error", detail={"error": str(exc)}) @router.get("/health", response_model=HealthResponse) def health() -> HealthResponse: components = [ _check_postgres(), _check_minio(), _check_opensearch(), _check_qdrant(), _check_redis(), ] if any(c.status == "error" for c in components): overall = "error" elif any(c.status == "degraded" for c in components): overall = "degraded" else: overall = "ok" return HealthResponse(status=overall, version=__version__, components=components) # type: ignore[arg-type]