This commit is contained in:
Mikan
2026-06-20 19:13:05 +03:00
parent 32575e217e
commit 8514c63ec6
193 changed files with 22105 additions and 11660 deletions

74
app/api/misc.py Normal file
View File

@@ -0,0 +1,74 @@
"""Misc endpoints: health, i18n."""
from __future__ import annotations
from fastapi import APIRouter, Depends
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import text
from app import __version__
from app.config import get_settings
from app.core.embeddings import HashEmbedder
from app.core.logging import get_logger
from app.core.qdrant_client import ping_qdrant
from app.db import get_db
from app.schemas import HealthResponse
_logger = get_logger(__name__)
router = APIRouter(prefix="/api", tags=["misc"])
@router.get("/health", response_model=HealthResponse)
async def health(db: AsyncSession = Depends(get_db)) -> HealthResponse:
"""Health-check endpoint — no auth required."""
db_ok = False
qdrant_ok = False
try:
await db.execute(text("SELECT 1"))
db_ok = True
except Exception as e: # noqa: BLE001
_logger.warning("health_db_failed", error=str(e))
try:
qdrant_ok = await ping_qdrant()
except Exception as e: # noqa: BLE001
_logger.warning("health_qdrant_failed", error=str(e))
# LLM health: we treat it as "true" only if api_url is set AND we can avoid a real call.
# For the basic health probe we don't make any LLM calls — return True iff api_url is configured.
cfg = get_settings()
llm_ok = bool(cfg.llm_api_url)
# Embeddings: True if HashEmbedder works (always does) or if openai provider configured
embeddings_ok = True
if cfg.embeddings_provider == "offline_hash":
try:
embedder = HashEmbedder(dimension=cfg.embeddings_dimension)
_ = await embedder.embed(["ping"])
except Exception:
embeddings_ok = False
status_str = "ok" if (db_ok and qdrant_ok) else "degraded"
return HealthResponse(
status=status_str,
db=db_ok,
qdrant=qdrant_ok,
llm=llm_ok,
embeddings=embeddings_ok,
version=__version__,
)
@router.get("/i18n/{lang}")
async def i18n(lang: str) -> dict:
"""Return translation JSON for the given language.
Backend only knows the en/ru bundles used by the frontend; we serve them
statically from the frontend's `public/i18n/` folder in production, but
this endpoint is useful for hot-reloading in dev.
"""
if lang not in ("en", "ru"):
return {"error": "unsupported language"}
# The frontend owns the bundles; this endpoint returns an empty dict
# (the frontend fetches `/i18n/{lang}.json` as a static asset).
return {"language": lang}