- config.py 新增 PEAR_DATA_DIR(默认 <项目根>/data),SQLite 库、Flask-Session
统一收进 DATA_DIR;不设时本地开发行为不变
- applications/__init__.py 顶部先 load_dotenv,保证 .env 里的 PEAR_DATA_DIR
在 config 导入时就生效
- extensions/init_sqlalchemy.py 接 SQLite connect 事件:WAL + synchronous=NORMAL
+ busy_timeout=30s(多 worker 写不再撞锁)
- config.py 注入 SQLALCHEMY_ENGINE_OPTIONS(pool_pre_ping + SQLite connect_args
超时;MySQL 路径下自动跳过 connect_args 防止参数错误)
- 新增 alembic 迁移 a47a5d2a3f1b 建 site_nav_click(含 anon_id 字段),
解决之前该表只由 db.create_all 建、不在迁移链里的隐患
- .gitignore / .dockerignore 加 data/ 排除规则(且把 migrations/ 从
ignore 里重新放行 —— 否则新加的迁移进不了库)
- NAS Docker 部署全面重写:
- compose 唯一绑定挂载 ./data:/app/data,宿主 File Station 看得见
- start.sh 自检 data 目录 + 子目录;workers 默认改为 1 threads 8
(SQLite 写串行,单进程最稳)
- 新增 backup.sh:sqlite3 .backup 在线热备(确保 WAL 一致性)+ 压缩
+ 保留 14 天
- Dockerfile 安装 sqlite3 客户端备用,建 /app/data 子目录并 chown
- docs/deployment-nas.md 重写数据持久化章节(路径表 + 热备命令 +
恢复步骤 + 整库迁移 tar/untar 流程)
56 lines
2.2 KiB
Python
56 lines
2.2 KiB
Python
import os
|
||
|
||
from dotenv import load_dotenv
|
||
from flask import Flask
|
||
|
||
# 项目根目录 = 本文件父目录的父目录
|
||
BASE_DIR = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
|
||
|
||
# 必须先于 applications.config 加载:config 在模块级就要读 PEAR_DATA_DIR
|
||
# 等环境变量来拼 data 目录路径,晚一步就会沿用默认值。
|
||
load_dotenv(os.path.join(BASE_DIR, ".env"), override=False)
|
||
|
||
from applications.common.script import init_script
|
||
from applications.config import BaseConfig, get_config_by_name, ensure_data_dir
|
||
from applications.extensions import init_plugs
|
||
from applications.view import init_bps
|
||
|
||
|
||
def create_app():
|
||
# 1) 再加载一次 .env(幂等;override=False 保证环境变量优先级更高)
|
||
load_dotenv(os.path.join(BASE_DIR, ".env"), override=False)
|
||
|
||
app = Flask(BASE_DIR)
|
||
|
||
# 2) 根据 PEAR_ENV / FLASK_ENV 选择配置类,默认 dev
|
||
env_name = os.environ.get("PEAR_ENV") or os.environ.get("FLASK_ENV") or "dev"
|
||
config_cls = get_config_by_name(env_name)
|
||
app.config.from_object(config_cls)
|
||
|
||
# 3) 把 BaseConfig 默认值里没写、但 .env 可能写了的 SQLALCHEMY_DATABASE_URI 等再覆盖一次
|
||
if os.environ.get("SQLALCHEMY_DATABASE_URI"):
|
||
app.config["SQLALCHEMY_DATABASE_URI"] = os.environ["SQLALCHEMY_DATABASE_URI"]
|
||
|
||
# 3.5) 让配置类有机会做运行时校验(例如 ProConfig 强制要求 SECRET_KEY)
|
||
if hasattr(config_cls, "init_app"):
|
||
config_cls.init_app(app)
|
||
|
||
# 3.6) 数据目录兜底:确保 data/ 存在,并以运行时解析结果为准写回路径
|
||
# (.env 里配了 PEAR_DATA_DIR 时,这里保证它一定生效)
|
||
data_dir = ensure_data_dir()
|
||
app.config["DATA_DIR"] = data_dir
|
||
if not os.environ.get("SQLALCHEMY_DATABASE_URI"):
|
||
app.config["SQLALCHEMY_DATABASE_URI"] = (
|
||
"sqlite:///" + os.path.join(data_dir, "pear.db").replace("\\", "/")
|
||
)
|
||
app.config["SESSION_FILE_DIR"] = os.path.join(data_dir, "flask_session")
|
||
|
||
# 4) 注册 flask 组件 / 蓝图 / 命令
|
||
init_plugs(app)
|
||
init_bps(app)
|
||
init_script(app)
|
||
|
||
# 5) 全站访问埋点已迁移到 plugins/siteStats(@app.before_request → record_visit)
|
||
|
||
return app
|