klg-asutk-app/backend/app/api/routes/health.py
Yuriy a7da43be0e apply recommendations: security, get_db, exceptions, eslint, api-client
- session: set_tenant use bound param (SQL injection fix)
- health: text('SELECT 1'), REDIS_URL from config
- deps: re-export get_db from session, use settings.ENABLE_DEV_AUTH (default False)
- routes: all get_db from app.api.deps; conftest overrides deps.get_db
- main: register exception handlers from app.api.exceptions
- next.config: enable ESLint and TypeScript checks
- .eslintrc: drop @typescript-eslint/recommended; fix no-console (logger, ws-client, regulations)
- backend/.env.example added
- frontend: export apiFetch; dashboard, profile, settings, risks use api-client
- docs/ANALYSIS_AND_RECOMMENDATIONS.md

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-14 21:48:58 +03:00

128 lines
4.0 KiB
Python

"""Health check with dependency status."""
from datetime import datetime, timezone
from fastapi import APIRouter, Depends
from sqlalchemy.orm import Session
from sqlalchemy import text
from app.api.deps import get_db
router = APIRouter(prefix="/health", tags=["health"])
@router.get("")
def health_check(db: Session = Depends(get_db)):
"""Comprehensive health check with DB, Redis, and service status."""
checks = {"status": "healthy", "timestamp": datetime.now(timezone.utc).isoformat()}
# Database
try:
db.execute(text("SELECT 1"))
checks["database"] = {"status": "up", "type": "postgresql"}
except Exception as e:
checks["database"] = {"status": "down", "error": str(e)[:100]}
checks["status"] = "degraded"
# Redis
try:
import redis
import os
r = redis.from_url(os.getenv("REDIS_URL", "redis://localhost:6379"), socket_timeout=2)
r.ping()
checks["redis"] = {"status": "up"}
except Exception:
checks["redis"] = {"status": "down"}
# Redis is optional — don't degrade status
# Risk scheduler
try:
from app.services.risk_scheduler import get_last_scan_time
last_scan = get_last_scan_time()
checks["risk_scanner"] = {
"status": "running",
"last_scan": last_scan.isoformat() if last_scan else "never",
}
except Exception:
checks["risk_scanner"] = {"status": "not_configured"}
# Version info
checks["version"] = "2.2.0"
checks["environment"] = "production" if not __import__("os").getenv("ENABLE_DEV_AUTH", "true").lower() == "true" else "development"
return checks
@router.get("/openapi.json", tags=["health"])
async def export_openapi():
"""Export OpenAPI specification."""
from app.main import app
return app.openapi()
@router.get("/health/detailed")
def detailed_health():
"""Расширенная проверка всех компонентов системы."""
import time
from app.db.session import SessionLocal
from app.core.config import settings
checks = {}
# Database
try:
db = SessionLocal()
db.execute(text("SELECT 1"))
db.close()
checks["database"] = {"status": "ok", "type": "PostgreSQL"}
except Exception as e:
checks["database"] = {"status": "error", "error": str(e)[:100]}
# Redis (from config)
try:
import redis
r = redis.from_url(settings.REDIS_URL, socket_timeout=2)
r.ping()
checks["redis"] = {"status": "ok"}
except Exception:
checks["redis"] = {"status": "unavailable", "note": "Optional component"}
# Disk space
import shutil
usage = shutil.disk_usage("/")
checks["disk"] = {
"status": "ok" if usage.free > 1_000_000_000 else "warning",
"free_gb": round(usage.free / 1_000_000_000, 1),
"total_gb": round(usage.total / 1_000_000_000, 1),
}
# Memory
try:
import psutil
mem = psutil.virtual_memory()
checks["memory"] = {
"status": "ok" if mem.percent < 90 else "warning",
"used_percent": mem.percent,
}
except ImportError:
checks["memory"] = {"status": "unknown", "note": "psutil not installed"}
# Module counts
from app.api.routes import personnel_plg, airworthiness_core, work_orders, defects
checks["data"] = {
"specialists": len(personnel_plg._specialists),
"directives": len(airworthiness_core._directives),
"bulletins": len(airworthiness_core._bulletins),
"work_orders": len(work_orders._work_orders),
"defects": len(defects._defects),
"components": len(airworthiness_core._components),
}
overall = "ok" if all(c.get("status") in ("ok", "unavailable", "unknown") for c in checks.values() if isinstance(c, dict) and "status" in c) else "degraded"
return {
"status": overall,
"timestamp": time.time(),
"version": "v26",
"checks": checks,
}