Files
pear-admin-flask/applications/view/health.py
T
bwstudio 1214bfdf4a feat(ops): 环境变量化配置 + 健康检查 + 启动脚本 + 部署/安全文档
主要变更:

config
- 引入 python-dotenv,从 .env 加载 SECRET_KEY / MAIL_* / SQLALCHEMY_DATABASE_URI
- 拆分 BaseConfig / DevConfig / ProConfig,PEAR_ENV 控制切换
- ProConfig 在 init_app() 校验 SECRET_KEY,禁止使用默认值

ops
- 新增 /healthz 端点:DB 探活 + 环境名 + 时间戳,失败返回 503
- 新增 start.bat (Windows) / Makefile (跨平台) 一键启动 / 初始化
- 新增 .env.example 模板;.gitignore 排除 .env / logs / vscode 等
- 新增 docs/DEPLOY.md(部署指南)和 docs/SECURITY.md(安全清单)
- app.py 不再硬编码 debug=False,交给配置类决定

本提交不包含上一次会话的其它工作树修改(模板/导航相关),后续单独 PR。
2026-09-05 20:46:12 +08:00

72 lines
1.9 KiB
Python

"""
健康检查端点(无需登录)
- GET /healthz
用于探活/负载均衡/容器 readinessProbe。
返回:
{
"status": "ok" | "degraded",
"db_ok": true | false,
"env": "dev" | "production",
"system": "Pear Admin",
"timestamp": "2026-09-05T17:00:00+08:00"
}
HTTP 状态码:
200 - 全部正常
503 - DB 不可达(用于触发 k8s readinessProbe 失败)
"""
from datetime import datetime, timedelta, timezone
import os
from flask import Blueprint, current_app, jsonify
from sqlalchemy import text
from applications.extensions import db
bp = Blueprint("health", __name__)
def _beijing_now() -> str:
"""返回 +08:00 时区 ISO8601 时间字符串。"""
tz = timezone(timedelta(hours=8))
return datetime.now(tz).isoformat()
@bp.get("/healthz")
def healthz():
# 优先按 PEAR_ENV/FLASK_ENV 取环境名;兜底按 DEBUG 反推
env = (
os.environ.get("PEAR_ENV")
or os.environ.get("FLASK_ENV")
or current_app.config.get("PEAR_ENV")
or ("dev" if current_app.config.get("DEBUG") else "production")
)
system_name = current_app.config.get("SYSTEM_NAME", "Pear Admin")
# DB 探活:跑一个最轻量的 SELECT 1
db_ok = True
db_error = None
try:
db.session.execute(text("SELECT 1"))
except Exception as e: # noqa: BLE001 - 这里就是要把异常吃掉,转换成 False
db_ok = False
db_error = str(e)
finally:
try:
db.session.rollback()
except Exception:
pass
payload = {
"status": "ok" if db_ok else "degraded",
"db_ok": db_ok,
"env": env,
"system": system_name,
"timestamp": _beijing_now(),
}
if db_error:
payload["db_error"] = db_error
return jsonify(payload), (200 if db_ok else 503)