2026-06-21 02:41:48 +03:00
|
|
|
"""Misc endpoints: health, i18n, public settings."""
|
2026-06-20 19:13:05 +03:00
|
|
|
|
|
|
|
|
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
|
2026-06-21 02:41:48 +03:00
|
|
|
from app.core.settings_service import get_all_settings, get_setting
|
2026-06-20 19:13:05 +03:00
|
|
|
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))
|
|
|
|
|
|
|
|
|
|
cfg = get_settings()
|
|
|
|
|
llm_ok = bool(cfg.llm_api_url)
|
|
|
|
|
|
|
|
|
|
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__,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
2026-06-21 02:41:48 +03:00
|
|
|
@router.get("/settings/public")
|
|
|
|
|
async def public_settings(db: AsyncSession = Depends(get_db)) -> dict:
|
|
|
|
|
"""Return UI-relevant settings (no secrets). No auth required.
|
2026-06-20 19:13:05 +03:00
|
|
|
|
2026-06-21 02:41:48 +03:00
|
|
|
The frontend uses this on app load to set the page title, favicon,
|
|
|
|
|
and logo.
|
2026-06-20 19:13:05 +03:00
|
|
|
"""
|
2026-06-21 02:41:48 +03:00
|
|
|
page_title = await get_setting(db, "ui.page_title")
|
|
|
|
|
favicon_url = await get_setting(db, "ui.favicon_url")
|
|
|
|
|
logo_url = await get_setting(db, "ui.logo_url")
|
|
|
|
|
og_image_url = await get_setting(db, "ui.og_image_url")
|
|
|
|
|
return {
|
|
|
|
|
"page_title": page_title or "AI-RPG",
|
|
|
|
|
"favicon_url": favicon_url or "/icon.png",
|
|
|
|
|
"logo_url": logo_url or "/icon.png",
|
|
|
|
|
"og_image_url": og_image_url or "",
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.get("/i18n/{lang}")
|
|
|
|
|
async def i18n(lang: str) -> dict:
|
|
|
|
|
"""Return translation JSON for the given language."""
|
2026-06-20 19:13:05 +03:00
|
|
|
if lang not in ("en", "ru"):
|
|
|
|
|
return {"error": "unsupported language"}
|
|
|
|
|
return {"language": lang}
|