Files
pear-admin-flask/applications/config.py
T
bwstudio 1ea69c2c34 refactor(plugins): 将导航/友链/关于/统计 4 模块迁出 framework 至 plugins
- 在 applications/config.py 中通过 PLUGIN_ENABLE_FOLDERS 启用 4 个业务插件
- 删除 framework 内 applications/view/system/nav.py 与 templates/system/nav/* 模板
- applications/extensions/init_plugs 移除重复的 broadcast_execute('event_init'/'event_finish')
  (由 init_bps / init_script 触发,避免 Blueprint 注册冲突)
- applications/view/system/index.py:未登录访问 / 直接渲染前台公开聚合页
- templates/system/index.html:移动端首屏即折叠侧边栏
- 新增 5 个 model(Nav 字段 status→enable 兼容、NavCategory、About、Friend、VisitLog/PageStat、NavClick)
- 新增 2 个 schema(NavCategorySchema、FriendOutSchema/ManageSchema)
- admin_about/admin_friend/admin_nav_category 在 init.json 中以「网站管理」分组挂载
- .gitignore 补充一次性备份、调试截图、探针截图不入库
2026-09-06 18:17:52 +08:00

132 lines
4.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import logging
import os
import secrets
from datetime import timedelta
def _env_bool(key: str, default: bool = False) -> bool:
"""读取布尔环境变量,兼容 true/1/yes/on(大小写不敏感)。"""
val = os.environ.get(key)
if val is None:
return default
return val.strip().lower() in {"1", "true", "yes", "on"}
def _env_str(key: str, default: str = "") -> str:
val = os.environ.get(key)
return val if val is not None and val != "" else default
class BaseConfig:
"""通用配置:所有环境共有的项。子类按需覆盖。"""
# ---- 应用基础 ----
SUPERADMIN = 'admin'
SYSTEM_NAME = 'Pear Admin'
SYSTEM_PANEL_LINKS = [
{"icon": "layui-icon layui-icon-auz", "title": "官方网站", "href": "http://www.pearadmin.com"},
{"icon": "layui-icon layui-icon-auz", "title": "开发文档", "href": "http://www.pearadmin.com"},
{"icon": "layui-icon layui-icon-auz", "title": "开源地址", "href": "https://gitee.com/Jmysy/Pear-Admin-Layui"},
]
# ---- 上传 ----
UPLOADED_PHOTOS_DEST = 'static/upload'
UPLOADED_FILES_ALLOW = ['gif', 'jpg', 'jpeg', 'png', 'webp']
UPLOADS_AUTOSERVE = True
# ---- JSON ----
JSON_AS_ASCII = False
# ---- 数据库 ----
# 默认走项目根目录下的 pear.db(与 instance/ 同一级,Flask 2.x 行为)。
# 生产环境推荐通过 SQLALCHEMY_DATABASE_URI 环境变量切换到 MySQL。
SQLALCHEMY_DATABASE_URI = 'sqlite:///../pear.db'
SQLALCHEMY_TRACK_MODIFICATIONS = False
# ---- 日志 ----
LOG_LEVEL = logging.WARN
# ---- Session ----
PERMANENT_SESSION_LIFETIME = timedelta(days=7)
SESSION_TYPE = "filesystem"
SESSION_PERMANENT = False
SESSION_USE_SIGNER = True
# ---- 安全 / 密钥 ----
# SECRET_KEY 的优先级:
# 1. 环境变量 SECRET_KEY
# 2. .env 中的 SECRET_KEY
# 3. 自动生成一个临时随机值(仅用于开发启动;生产模式强制要求外部传入)
SECRET_KEY = os.environ.get("SECRET_KEY") or "pear-system-flask"
# ---- Mail(默认占位;环境变量可覆盖)----
MAIL_SERVER = _env_str("MAIL_SERVER", "smtp.qq.com")
MAIL_USE_TLS = _env_bool("MAIL_USE_TLS", False)
MAIL_USE_SSL = _env_bool("MAIL_USE_SSL", True)
MAIL_PORT = int(_env_str("MAIL_PORT", "465"))
MAIL_USERNAME = _env_str("MAIL_USERNAME", "123@qq.com")
MAIL_PASSWORD = _env_str("MAIL_PASSWORD", "XXXXX") # QQ 邮箱授权码
MAIL_DEFAULT_SENDER = _env_str("MAIL_DEFAULT_SENDER", MAIL_USERNAME)
# ---- 插件 ----
# 站点业务模块(导航 / 友链 / 关于 / 访问统计)均通过 plugins 方式开发,启用即可挂载路由
PLUGIN_ENABLE_FOLDERS = ["navManager", "friendManager", "aboutManager", "siteStats"]
class DevConfig(BaseConfig):
"""开发环境配置:DEBUG=True,详细日志。"""
DEBUG = True
TESTING = False
LOG_LEVEL = logging.INFO
# 开发环境下 SECRET_KEY 自动生成(每次启动都不同,仅用于本地调试)。
SECRET_KEY = os.environ.get("SECRET_KEY") or secrets.token_urlsafe(48)
class ProConfig(BaseConfig):
"""生产环境配置:DEBUG=False,强制要求外部传入 SECRET_KEY。"""
DEBUG = False
TESTING = False
LOG_LEVEL = logging.WARN
# 生产推荐使用 MySQL,例如:
# SQLALCHEMY_DATABASE_URI=mysql+pymysql://user:pass@host:3306/pear?charset=utf8mb4
SQLALCHEMY_DATABASE_URI = os.environ.get(
"SQLALCHEMY_DATABASE_URI",
BaseConfig.SQLALCHEMY_DATABASE_URI,
)
@classmethod
def init_app(cls, app):
"""Flask 在 app.config.from_object 之后调用此钩子。
生产模式要求:必须从环境变量传入强随机 SECRET_KEY
否则启动失败 —— 防止线上仍用默认密钥。
"""
secret = os.environ.get("SECRET_KEY")
if not secret or secret == "pear-system-flask":
raise RuntimeError(
"[ProConfig] 生产环境必须通过环境变量 SECRET_KEY 传入强随机密钥,"
"禁止使用默认值。可用以下命令生成:\n"
" python -c \"import secrets; print(secrets.token_urlsafe(48))\""
)
app.config["SECRET_KEY"] = secret
# 暴露给外部按名字取
config_map = {
"dev": DevConfig,
"development": DevConfig,
"pro": ProConfig,
"production": ProConfig,
}
def get_config_by_name(name: str):
"""根据名字取配置类,未知名字退回 DevConfig。"""
if not name:
return DevConfig
return config_map.get(name.strip().lower(), DevConfig)