Files
xiaoxia-saas/apps/api/app/api/routes/health.py
T
xiaoxia 4bb1c4205d
Worker Base Image Build / Build Worker Base Images (worker-base-runtime-cache, infra/docker/worker-base-runtime.Dockerfile, worker-base-runtime, runtime) (push) Failing after 1m19s
API Base Image Build / Build API Base Image (push) Successful in 1m40s
CI/CD Pipeline / Validate - Migration (alembic) (push) Successful in 3m5s
CI/CD Pipeline / Validate - Type Check (mypy) (push) Successful in 3m20s
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 5m24s
CI/CD Pipeline / Validate - Code Quality (push) Successful in 8m22s
CI/CD Pipeline / Integration Tests (push) Successful in 3m3s
Worker Base Image Build / Build Worker Base Images (worker-base-builder-cache, infra/docker/worker-base-builder.Dockerfile, worker-base-builder, builder) (push) Failing after 11m48s
CI/CD Pipeline / Build Staging Web Image (push) Failing after 11m49s
CI/CD Pipeline / Unit Tests (push) Successful in 12m22s
CI/CD Pipeline / Build Staging API Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (push) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (push) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (push) Has been cancelled
CI/CD Pipeline / Canary Release to Production (push) Has been cancelled
CI/CD Pipeline / Production Browser E2E (push) Failing after 693h7m59s
CI/CD Pipeline / CI Gate (push) Failing after 693h8m0s
CI/CD Pipeline / Build Production Worker Image (push) Failing after 693h8m0s
CI/CD Pipeline / Build Production Web Image (push) Failing after 693h8m0s
CI/CD Pipeline / PR Build Worker Image (push) Failing after 693h19m9s
CI/CD Pipeline / PR Build Web Image (push) Failing after 693h19m11s
CI/CD Pipeline / PR Build API Image (push) Failing after 693h19m13s
CI/CD Pipeline / Frontend Lint (push) Failing after 693h19m21s
CI/CD Pipeline / Check if frontend-only change (push) Failing after 693h20m26s
CI/CD Pipeline / Deploy Production (push) Failing after 693h41m41s
CI/CD Pipeline / Build Production API Image (push) Failing after 693h41m41s
perf: 删除 psycopg2-binary 依赖 + 预构建 API 基础镜像 (#1422)
Co-authored-by: xiaoxia <dev@xiaoxiajianji.com>
Co-committed-by: xiaoxia <dev@xiaoxiajianji.com>
2026-08-18 18:06:01 +08:00

140 lines
4.4 KiB
Python

from datetime import datetime, timezone
import psycopg
import redis
from app.config import settings
from fastapi import APIRouter, status
from fastapi.responses import JSONResponse
router = APIRouter(tags=["Health"])
@router.get("/health", status_code=status.HTTP_200_OK)
async def health_check():
return {
"status": "healthy",
"timestamp": datetime.now(timezone.utc).isoformat(),
"version": settings.APP_VERSION,
}
@router.get("/ready", status_code=status.HTTP_200_OK)
async def readiness_check():
"""简单的就绪检查,仅返回状态。详细健康检查请使用 /health 端点。"""
return {"status": "ready"}
@router.get("/startup", status_code=status.HTTP_200_OK)
async def startup_check():
checks = {
"database": await _check_database(),
"migrations": await _check_migrations(),
}
all_ready = all(check["status"] == "healthy" for check in checks.values())
response = {
"status": "started" if all_ready else "starting",
"timestamp": datetime.now(timezone.utc).isoformat(),
"checks": checks,
}
if not all_ready:
return JSONResponse(status_code=status.HTTP_503_SERVICE_UNAVAILABLE, content=response)
return response
async def _check_database() -> dict:
if settings.USE_IN_MEMORY_DB:
return {
"status": "healthy",
"type": "in_memory",
"message": "Using in-memory database",
}
try:
conn = psycopg.connect(settings.DATABASE_URL, connect_timeout=3)
with conn.cursor() as cur:
cur.execute("SELECT 1")
cur.fetchone()
conn.close()
return {
"status": "healthy",
"type": "postgresql",
"message": "Database connection successful",
}
except Exception as error:
return {
"status": "unhealthy",
"type": "postgresql",
"message": f"Database connection failed: {error}",
}
async def _check_redis() -> dict:
try:
client = redis.from_url(settings.REDIS_URL, socket_connect_timeout=3)
client.ping()
client.close()
return {
"status": "healthy",
"type": "redis",
"message": "Redis connection successful",
}
except Exception as error:
return {
"status": "unhealthy",
"type": "redis",
"message": f"Redis connection failed: {error}",
}
def _check_oss() -> dict:
try:
from app.core.storage import get_storage_service
svc = get_storage_service()
if not svc.access_key_id or not svc.access_key_secret:
return {
"status": "unhealthy",
"type": "oss",
"message": "OSS credentials not configured (OSS_ACCESS_KEY_ID / OSS_ACCESS_KEY_SECRET missing)",
}
if svc.bucket is None:
return {
"status": "unhealthy",
"type": "oss",
"message": "OSS SDK (oss2) not installed or bucket client init failed",
}
# Try a lightweight OSS API call to verify connectivity & credentials
svc.bucket.get_bucket_info()
return {
"status": "healthy",
"type": "oss",
"message": f"OSS connected: endpoint={svc.endpoint} bucket={svc.bucket_name}",
}
except Exception as error:
return {
"status": "unhealthy",
"type": "oss",
"message": f"OSS check failed: {type(error).__name__}: {error}",
}
async def _check_migrations() -> dict:
if settings.USE_IN_MEMORY_DB:
return {
"status": "healthy",
"message": "Using in-memory database, no migrations needed",
}
try:
conn = psycopg.connect(settings.DATABASE_URL, connect_timeout=3)
with conn.cursor() as cur:
cur.execute("""
SELECT COUNT(*) FROM information_schema.tables
WHERE table_name IN ('projects', 'asset_libraries', 'assets', 'ingest_jobs', 'classification_jobs')
""")
count = cur.fetchone()[0]
conn.close()
if count >= 5:
return {"status": "healthy", "message": "Database migrations applied"}
return {"status": "unhealthy", "message": f"Missing tables, found {count}/5"}
except Exception as error:
return {"status": "unhealthy", "message": f"Migration check failed: {error}"}