fix(avatar): prevent stale deployment images
This commit is contained in:
@@ -1,5 +1,12 @@
|
||||
FROM python:3.10-slim
|
||||
|
||||
ARG APP_GIT_SHA=unknown
|
||||
ARG APP_BUILD_TIME=unknown
|
||||
ENV APP_GIT_SHA=${APP_GIT_SHA} \
|
||||
APP_BUILD_TIME=${APP_BUILD_TIME}
|
||||
LABEL org.opencontainers.image.revision=${APP_GIT_SHA} \
|
||||
org.opencontainers.image.created=${APP_BUILD_TIME}
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# 后端依赖(fastapi/uvicorn/sqlalchemy/pypdf/python-docx/openpyxl 等)均为纯 Python wheel,
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
from fastapi import FastAPI
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
|
||||
import os
|
||||
import importlib.util
|
||||
import logging
|
||||
import os
|
||||
|
||||
from apscheduler.schedulers.asyncio import AsyncIOScheduler
|
||||
from apscheduler.triggers.interval import IntervalTrigger
|
||||
|
||||
from database import init_db, SessionLocal
|
||||
from database import engine, init_db, SessionLocal
|
||||
from models import Avatar, Authorization, Organization, TokenAccount, TokenPlan, User
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
import routers.avatars
|
||||
@@ -54,7 +55,31 @@ app.mount("/api/files", StaticFiles(directory=UPLOAD_DIR), name="knowledge-files
|
||||
|
||||
@app.get("/api/health")
|
||||
def health():
|
||||
return ok({"status": "ok"})
|
||||
checks = _runtime_checks()
|
||||
return ok({
|
||||
"status": "ok" if all(checks.values()) else "degraded",
|
||||
"gitSha": os.getenv("APP_GIT_SHA", "unknown"),
|
||||
"buildTime": os.getenv("APP_BUILD_TIME", "unknown"),
|
||||
"checks": checks,
|
||||
})
|
||||
|
||||
|
||||
def _runtime_checks():
|
||||
return {
|
||||
"database": _database_is_ready(),
|
||||
"uploads": os.path.isdir(UPLOAD_DIR) and os.access(UPLOAD_DIR, os.W_OK),
|
||||
"pdfOcr": importlib.util.find_spec("pymupdf") is not None,
|
||||
}
|
||||
|
||||
|
||||
def _database_is_ready():
|
||||
try:
|
||||
with engine.connect() as connection:
|
||||
connection.exec_driver_sql("SELECT 1")
|
||||
return True
|
||||
except Exception:
|
||||
logger.exception("Database readiness check failed")
|
||||
return False
|
||||
|
||||
|
||||
def seed():
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
import main
|
||||
|
||||
|
||||
def test_health_reports_release_and_runtime_capabilities(monkeypatch):
|
||||
monkeypatch.setenv("APP_GIT_SHA", "test-sha")
|
||||
monkeypatch.setenv("APP_BUILD_TIME", "2026-09-09T00:00:00Z")
|
||||
monkeypatch.setattr(main, "_runtime_checks", lambda: {
|
||||
"database": True,
|
||||
"uploads": True,
|
||||
"pdfOcr": True,
|
||||
})
|
||||
|
||||
response = main.health()
|
||||
|
||||
assert response["code"] == 200
|
||||
assert response["data"]["status"] == "ok"
|
||||
assert response["data"]["gitSha"] == "test-sha"
|
||||
assert response["data"]["buildTime"] == "2026-09-09T00:00:00Z"
|
||||
assert response["data"]["checks"] == {
|
||||
"database": True,
|
||||
"uploads": True,
|
||||
"pdfOcr": True,
|
||||
}
|
||||
|
||||
|
||||
def test_health_is_degraded_when_a_required_capability_is_missing(monkeypatch):
|
||||
monkeypatch.setattr(main, "_runtime_checks", lambda: {
|
||||
"database": True,
|
||||
"uploads": True,
|
||||
"pdfOcr": False,
|
||||
})
|
||||
|
||||
assert main.health()["data"]["status"] == "degraded"
|
||||
Reference in New Issue
Block a user