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
Co-authored-by: xiaoxia <dev@xiaoxiajianji.com> Co-committed-by: xiaoxia <dev@xiaoxiajianji.com>
140 lines
4.4 KiB
Python
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}"}
|