# backend/app/routers/ops.py — readiness + 메트릭 노출 (phase-15) from fastapi import APIRouter, Depends from fastapi.responses import PlainTextResponse from sqlmodel import Session, text from ..config import get_settings from ..db import get_session from ..observability import metrics from ..schemas import ReadyOut router = APIRouter() def check_ollama() -> dict: """Ollama 도달성 점검(테스트에서 monkeypatch 가능).""" import httpx st = get_settings() try: r = httpx.get(f"{st.ollama_host}/api/tags", timeout=2.0) return {"reachable": r.status_code == 200, "model": st.ollama_model, "host": st.ollama_host} except Exception: return {"reachable": False, "model": st.ollama_model, "host": st.ollama_host} @router.get("/ready", response_model=ReadyOut) def ready(s: Session = Depends(get_session)): checks: dict[str, str] = {} db_ok = True try: s.exec(text("SELECT 1")) checks["db"] = "ok" except Exception as e: db_ok = False checks["db"] = f"error: {type(e).__name__}" checks["migrations"] = "ok" llm = check_ollama() # LLM 미가용은 차단하지 않음(heuristic 폴백) checks["llm"] = f"ok:{llm['model']}" if llm.get("reachable") else "fallback:heuristic" checks["worker"] = "scheduled" if get_settings().worker_enabled else "manual" return ReadyOut(ready=db_ok, checks=checks) @router.get("/metrics", response_class=PlainTextResponse) def prometheus_metrics(): """Prometheus 텍스트. 운영에선 리버스 프록시(Caddy)가 외부 접근 차단.""" return PlainTextResponse(metrics.render(), media_type="text/plain; version=0.0.4")