Files
Nexus/server/api/health.py
T

67 lines
2.1 KiB
Python
Raw Normal View History

"""Nexus — Health Check Endpoint
External monitoring endpoint for Shell health_monitor.sh and Supervisor.
Security: Only returns minimal status ("ok") for external probes.
Detailed health diagnostics require admin JWT auth via /health/detail.
"""
import logging
from fastapi import APIRouter, Depends
from fastapi.responses import PlainTextResponse
from sqlalchemy import text
from server.api.auth_jwt import get_current_admin
from server.domain.models import Admin
from server.infrastructure.database.session import AsyncSessionLocal
from server.infrastructure.redis.client import get_redis
from server.api.websocket import manager as ws_manager
logger = logging.getLogger("nexus.health")
router = APIRouter()
@router.get("/health", response_class=PlainTextResponse)
async def health_check():
"""Minimal health check — used by Shell health_monitor.sh (Layer 3 guardian).
Returns plain-text "ok" — no JSON, no backend fingerprint.
health_monitor.sh discards response body (curl -sf > /dev/null),
so this is purely for HTTP status code.
"""
return "ok"
@router.get("/health/detail")
async def health_detail(admin: Admin = Depends(get_current_admin)):
"""Detailed health diagnostics — requires admin JWT authentication.
Returns Redis/MySQL/WebSocket status for admin dashboard use.
"""
checks = {"python": "ok"}
# Redis check
try:
redis = get_redis()
await redis.ping()
checks["redis"] = "ok"
except Exception as e:
logger.error(f"Redis health check failed: {e}")
checks["redis"] = "unavailable"
# MySQL check
try:
async with AsyncSessionLocal() as session:
await session.execute(text("SELECT 1"))
checks["mysql"] = "ok"
except Exception as e:
logger.error(f"MySQL health check failed: {e}")
checks["mysql"] = "error"
# WebSocket connections count
try:
checks["websocket_clients"] = await ws_manager.global_client_count()
except Exception:
checks["websocket_clients"] = ws_manager.client_count
return {"status": "ok", "checks": checks}