- FastAPI + SQLAlchemy async + Alembic + Postgres backend - Auth: JWT access + rotating refresh tokens, argon2, roles, audit log - React 19 + Vite frontend: login page, protected route, auth context - Docker Compose: postgres, redis, migrate, api, worker (placeholder), frontend/nginx Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
70 lines
2.1 KiB
Python
70 lines
2.1 KiB
Python
"""Проверки живости и готовности."""
|
|
|
|
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,
|
|
)
|