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。
This commit is contained in:
@@ -1,11 +1,16 @@
|
||||
from applications.view.system import register_system_bps
|
||||
from applications.view.public import bp as public_bp # public 包的入口就是 nav_bp
|
||||
from applications.view.public import bp as public_bp, register_public_bp # public 包的入口就是 nav_bp
|
||||
from applications.view.health import bp as health_bp
|
||||
from applications.extensions.init_plugins import broadcast_execute
|
||||
|
||||
|
||||
def init_bps(app):
|
||||
# 健康检查端点(无需登录,应在负载均衡/容器探活里使用)
|
||||
app.register_blueprint(health_bp)
|
||||
|
||||
# 前台公开蓝图(无需登录)
|
||||
app.register_blueprint(public_bp)
|
||||
register_public_bp(app)
|
||||
|
||||
# 后台系统蓝图(需要登录 + 权限码)
|
||||
register_system_bps(app)
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
"""
|
||||
健康检查端点(无需登录)
|
||||
|
||||
- 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)
|
||||
Reference in New Issue
Block a user