import os import time import asyncio from urllib.parse import urlparse from fastapi import APIRouter, Depends from sqlalchemy import text from sqlalchemy.ext.asyncio import AsyncSession from bd.database import get_db, get_redis, DATABASE_URL, REDIS_URL from route.deps import require_admin router = APIRouter() MINIO_ENDPOINT = os.getenv("MINIO_ENDPOINT", "minio:9000") MINIO_ACCESS_KEY = os.getenv("MINIO_ACCESS_KEY", "minioadmin") MINIO_SECRET_KEY = os.getenv("MINIO_SECRET_KEY", "minioadmin") MINIO_BUCKET = os.getenv("MINIO_BUCKET", "news-media") MINIO_PUBLIC_URL = os.getenv("MINIO_PUBLIC_URL", "http://localhost:9000") def _parse_db_url(url: str) -> dict: """Вытащить host/port/db/user из DATABASE_URL без пароля.""" try: # postgresql+asyncpg://user:pass@host:port/db p = urlparse(url.replace("+asyncpg", "")) return { "host": p.hostname or "?", "port": p.port or 5432, "database": (p.path or "/").lstrip("/") or "?", "user": p.username or "?", } except Exception: return {"host": "?", "port": 5432, "database": "?", "user": "?"} def _parse_redis_url(url: str) -> dict: """Вытащить host/port из REDIS_URL без пароля.""" try: p = urlparse(url) return { "host": p.hostname or "?", "port": p.port or 6379, "db": (p.path or "/0").lstrip("/") or "0", } except Exception: return {"host": "?", "port": 6379, "db": "0"} async def _check_postgres(db: AsyncSession) -> dict: info = _parse_db_url(DATABASE_URL) t0 = time.monotonic() try: result = await db.execute(text("SELECT version()")) version = result.scalar() or "" latency_ms = round((time.monotonic() - t0) * 1000, 1) # Извлекаем короткую версию: "PostgreSQL 15.3 ..." short_ver = version.split(",")[0] if version else "?" return {"ok": True, "latency_ms": latency_ms, "version": short_ver, **info} except Exception as e: return {"ok": False, "error": str(e), "latency_ms": None, **info} async def _check_redis() -> dict: info = _parse_redis_url(REDIS_URL) t0 = time.monotonic() try: redis = await get_redis() pong = await redis.ping() latency_ms = round((time.monotonic() - t0) * 1000, 1) server_info = await redis.info("server") version = server_info.get("redis_version", "?") return {"ok": bool(pong), "latency_ms": latency_ms, "version": f"Redis {version}", **info} except Exception as e: return {"ok": False, "error": str(e), "latency_ms": None, **info} async def _check_minio() -> dict: t0 = time.monotonic() try: from minio import Minio loop = asyncio.get_event_loop() client = Minio(MINIO_ENDPOINT, access_key=MINIO_ACCESS_KEY, secret_key=MINIO_SECRET_KEY, secure=False) exists = await loop.run_in_executor(None, client.bucket_exists, MINIO_BUCKET) latency_ms = round((time.monotonic() - t0) * 1000, 1) return { "ok": True, "latency_ms": latency_ms, "endpoint": MINIO_ENDPOINT, "bucket": MINIO_BUCKET, "public_url": MINIO_PUBLIC_URL, "bucket_exists": exists, } except Exception as e: return { "ok": False, "error": str(e), "latency_ms": None, "endpoint": MINIO_ENDPOINT, "bucket": MINIO_BUCKET, "public_url": MINIO_PUBLIC_URL, } @router.get("/status") async def system_status(db: AsyncSession = Depends(get_db), _: str = Depends(require_admin)): pg, redis, minio = await asyncio.gather( _check_postgres(db), _check_redis(), _check_minio(), ) return { "postgres": pg, "redis": redis, "minio": minio, }