Build and Push / build (release) Successful in 1m16s
- Add PostgreSQL database with async SQLAlchemy - Add Alembic migrations for schema management - Add User, Role, UserPreferences, ApiKey models - Add auth endpoints: /auth/me, /auth/users, /auth/users/sync-from-authentik - Add token validation via Authentik userinfo endpoint - Add bulk user sync from Authentik admin API - Add database health check to diagnostics 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
228 lines
8.0 KiB
Python
228 lines
8.0 KiB
Python
"""
|
|
Health Controller
|
|
|
|
Provides service health and information endpoints
|
|
"""
|
|
from fastapi import APIRouter, Response
|
|
from fastapi.responses import JSONResponse
|
|
|
|
from src.controllers.base import BaseController
|
|
from src.config import get_settings
|
|
from src.logging_config import get_logger
|
|
from src.models.ollama_client import get_ollama_client
|
|
from src.db import get_database
|
|
|
|
|
|
|
|
logger = get_logger(__name__)
|
|
|
|
|
|
class HealthController(BaseController):
|
|
"""
|
|
Controller for service health and information
|
|
|
|
Provides endpoints for:
|
|
- Service information and status
|
|
- Health checks
|
|
"""
|
|
|
|
def __init__(self):
|
|
super().__init__(prefix="", tags=["Health"])
|
|
|
|
def create_router(self) -> APIRouter:
|
|
"""Create and configure the router"""
|
|
router = APIRouter(tags=self.tags)
|
|
settings = get_settings()
|
|
|
|
@router.get(
|
|
"/",
|
|
summary="Service information",
|
|
response_class=JSONResponse
|
|
)
|
|
async def root():
|
|
"""
|
|
Get service information and health status
|
|
|
|
Returns basic information about the API service and available endpoints.
|
|
"""
|
|
logger.debug("Root endpoint accessed")
|
|
return {
|
|
"service": settings.app_name,
|
|
"version": settings.app_version,
|
|
"status": "healthy",
|
|
"docs": "/docs"
|
|
}
|
|
|
|
@router.get(
|
|
"/health",
|
|
summary="Health check",
|
|
response_class=JSONResponse
|
|
)
|
|
async def health_check():
|
|
"""
|
|
Fast health check endpoint for container orchestration
|
|
|
|
Returns a 200 OK immediately if the service is running.
|
|
Does NOT check backend connectivity (use /health/full for that).
|
|
Used by Docker, Kubernetes, and load balancers for liveness probes.
|
|
"""
|
|
return {
|
|
"status": "healthy",
|
|
"version": settings.app_version
|
|
}
|
|
|
|
@router.get(
|
|
"/health/full",
|
|
summary="Fast health check for Docker",
|
|
)
|
|
async def full_health_check(response: Response):
|
|
"""
|
|
Fast health check for container orchestration (Docker/K8s).
|
|
|
|
Checks component availability WITHOUT running expensive operations.
|
|
Returns 200 OK if all components are available, otherwise 503.
|
|
|
|
For detailed diagnostics, use /health/diagnostics instead.
|
|
"""
|
|
import time
|
|
start_time = time.time()
|
|
|
|
# Check 1: Ollama connection + verify agent model is available
|
|
ollama_client = get_ollama_client()
|
|
ollama_healthy = False
|
|
ollama_error = None
|
|
model_available = False
|
|
|
|
try:
|
|
# Ping Ollama
|
|
ollama_healthy = await ollama_client.health_check()
|
|
|
|
# Verify the agent model is pulled and check what's currently loaded
|
|
models_info = {}
|
|
if ollama_healthy:
|
|
try:
|
|
models_response = await ollama_client.list_models()
|
|
available_models = [m.get('name', '') for m in models_response.get('models', [])]
|
|
model_available = settings.agent_model in available_models
|
|
|
|
# Get info about currently loaded models (those with size in memory)
|
|
loaded_models = [
|
|
m.get('name', '') for m in models_response.get('models', [])
|
|
if m.get('size', 0) > 0
|
|
]
|
|
|
|
models_info = {
|
|
"configured": settings.agent_model,
|
|
"available": model_available,
|
|
"total_in_ollama": len(available_models),
|
|
"currently_loaded": loaded_models if loaded_models else ["none"]
|
|
}
|
|
|
|
if not model_available:
|
|
ollama_error = f"Model '{settings.agent_model}' not found in Ollama. Available: {', '.join(available_models[:3])}"
|
|
ollama_healthy = False
|
|
except Exception as e:
|
|
ollama_error = f"Could not list Ollama models: {str(e)}"
|
|
ollama_healthy = False
|
|
|
|
except Exception as e:
|
|
ollama_error = str(e)
|
|
logger.warning(f"Ollama health check failed: {ollama_error}")
|
|
|
|
# Check 2: Database connection
|
|
database = get_database()
|
|
db_healthy = False
|
|
db_error = None
|
|
|
|
try:
|
|
db_healthy = await database.health_check()
|
|
except Exception as e:
|
|
db_error = str(e)
|
|
logger.warning(f"Database health check failed: {db_error}")
|
|
|
|
is_healthy = ollama_healthy and db_healthy
|
|
|
|
elapsed_ms = int((time.time() - start_time) * 1000)
|
|
status_code = 200 if is_healthy else 503
|
|
response.status_code = status_code
|
|
|
|
return {
|
|
"status": "healthy" if is_healthy else "unhealthy",
|
|
"status_code": status_code,
|
|
"response_time_ms": elapsed_ms,
|
|
"components": {
|
|
"ollama": {
|
|
"status": "✅ healthy" if ollama_healthy else "❌ unhealthy",
|
|
"models": models_info if models_info else {
|
|
"configured": settings.agent_model,
|
|
"available": False
|
|
},
|
|
"error": ollama_error
|
|
},
|
|
"database": {
|
|
"status": "✅ healthy" if db_healthy else "❌ unhealthy",
|
|
"error": db_error
|
|
}
|
|
}
|
|
}
|
|
|
|
@router.get(
|
|
"/health/diagnostics",
|
|
summary="Detailed system diagnostics",
|
|
)
|
|
async def diagnostics(deep_test: bool = False):
|
|
"""
|
|
Comprehensive system diagnostics with detailed component information.
|
|
|
|
Query Parameters:
|
|
- deep_test: Set to true to actually test agent generation (slow, ~5-10s)
|
|
|
|
Returns detailed information about all system components.
|
|
"""
|
|
import time
|
|
|
|
start_time = time.time()
|
|
diagnostics = {
|
|
"timestamp": time.time(),
|
|
"service": {
|
|
"name": settings.app_name,
|
|
"version": settings.app_version,
|
|
"purpose": "Infrastructure management and tools API"
|
|
},
|
|
"components": {}
|
|
}
|
|
|
|
# 1. Ollama Connection
|
|
ollama_client = get_ollama_client()
|
|
try:
|
|
ollama_healthy = await ollama_client.health_check()
|
|
diagnostics["components"]["ollama"] = {
|
|
"status": "✅ connected",
|
|
"url": settings.ollama_base_url,
|
|
"timeout": settings.ollama_timeout,
|
|
"default_model": settings.default_model
|
|
}
|
|
except Exception as e:
|
|
diagnostics["components"]["ollama"] = {
|
|
"status": "❌ error",
|
|
"error": str(e)
|
|
}
|
|
|
|
# 2. Configuration
|
|
diagnostics["configuration"] = {
|
|
"agent_fallback_enabled": settings.agent_fallback_enabled,
|
|
"memory_tier1_max_turns": settings.memory_tier1_max_turns,
|
|
"cors_origins": settings.cors_origins[:2] if len(settings.cors_origins) > 2 else settings.cors_origins
|
|
}
|
|
|
|
elapsed_ms = int((time.time() - start_time) * 1000)
|
|
diagnostics["response_time_ms"] = elapsed_ms
|
|
|
|
return diagnostics
|
|
|
|
return router
|
|
|
|
|
|
# Create controller instance
|
|
health_controller = HealthController()
|