"""API-роуты скелета: healthz/readyz и пример admin-endpoint с гейтом ролей."""
from fastapi import APIRouter, Depends, HTTPException
from pydantic import BaseModel
from redis import Redis
from sqlalchemy import text
from app.auth.deps import AuthenticatedUser, require_admin
from app.config import get_settings
from app.database import SessionLocal
router = APIRouter(prefix="/api")
@router.get("/healthz")
def healthz() -> dict:
"""Liveness: процесс жив, конфигурация не проверяется."""
return {"status": "ok", "service": "synapse"}
@router.get("/readyz")
def readyz() -> dict:
"""Readiness: доступность зависимостей (Postgres, Redis)."""
components: dict[str, str] = {}
try:
with SessionLocal() as db:
db.execute(text("SELECT 1"))
components["postgres"] = "ok"
except Exception as exc:
components["postgres"] = f"fail: {exc.__class__.__name__}"
try:
Redis.from_url(get_settings().redis_url, socket_connect_timeout=2).ping()
components["redis"] = "ok"
except Exception as exc:
components["redis"] = f"fail: {exc.__class__.__name__}"
ready = all(v == "ok" for v in components.values())
return {"ready": ready, "components": components}
class AdminMe(BaseModel):
sub: str
email: str | None
system_role: str
# Профиль gnexus-auth (scope profile) — для карточки пользователя в SPA.
username: str | None = None
display_name: str | None = None
avatar_url: str | None = None
# Страница профиля на сервере gnexus-auth (ссылка из навигации).
account_url: str = ""
@router.get("/v1/admin/me")
def admin_me(user: AuthenticatedUser = Depends(require_admin)) -> AdminMe:
"""Проверка гейта админки: 401 без токена, 403 без роли admin."""
return AdminMe(
sub=str(user.user_id),
email=user.email,
system_role=user.system_role or "unknown",
username=user.profile.get("username"),
display_name=user.profile.get("display_name"),
avatar_url=user.profile.get("avatar_url"),
account_url=f"{get_settings().gauth_base_url.rstrip('/')}/account/profile",
)