"""Проверки живости и готовности.""" from __future__ import annotations from typing import Literal import redis.asyncio as aioredis from fastapi import APIRouter, Response, status from pydantic import BaseModel from sqlalchemy import text from app.api.deps import SessionDep from app.core.config import settings from app.core.logging import get_logger router = APIRouter(tags=["health"]) log = get_logger(__name__) class HealthResponse(BaseModel): status: Literal["ok", "degraded"] environment: str database: bool redis: bool @router.get("/health", response_model=HealthResponse) async def health() -> HealthResponse: """Liveness: процесс жив. Внешние зависимости не проверяются. Оркестратор не должен перезапускать контейнер из-за недоступного Postgres — перезапуск приложения проблему БД не решает. """ return HealthResponse( status="ok", environment=settings.environment, database=True, redis=True ) @router.get("/health/ready", response_model=HealthResponse) async def readiness(session: SessionDep, response: Response) -> HealthResponse: """Readiness: приложение способно обслуживать запросы.""" db_ok = True redis_ok = True try: await session.execute(text("SELECT 1")) except Exception as exc: # noqa: BLE001 — health-check не должен падать сам db_ok = False log.warning("readiness_db_failed", error=str(exc)) client = aioredis.from_url(settings.redis_url) try: await client.ping() except Exception as exc: # noqa: BLE001 redis_ok = False log.warning("readiness_redis_failed", error=str(exc)) finally: await client.aclose() healthy = db_ok and redis_ok if not healthy: response.status_code = status.HTTP_503_SERVICE_UNAVAILABLE return HealthResponse( status="ok" if healthy else "degraded", environment=settings.environment, database=db_ok, redis=redis_ok, )