- 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 流程)
55 lines
1.8 KiB
Bash
55 lines
1.8 KiB
Bash
#!/bin/bash
|
||
# Pear Admin Flask - SQLite 在线热备脚本
|
||
#
|
||
# 用法: docker exec pear-admin-nas /app/deploy/nas/backup.sh
|
||
# (也支持本地直接 ./backup.sh)
|
||
#
|
||
# 关键:不能用 cp 复制 pear.db —— WAL 模式下 .db 文件单独 cp 不含未刷盘的页。
|
||
# 改用 SQLite 自带的 `sqlite3 pear.db ".backup target"` 或
|
||
# `VACUUM INTO 'target'`,两者都在 SQLite 内部完成拷贝,含 WAL 的一致性快照。
|
||
#
|
||
# 产物:${DATA_DIR}/backup/pear-YYYYMMDD_HHMMSS.db
|
||
# 保留:最近 14 份(可通过 BACKUP_KEEP 覆盖)
|
||
|
||
set -e
|
||
|
||
DATA_DIR=${PEAR_DATA_DIR:-/app/data}
|
||
DB_PATH="${DATA_DIR}/pear.db"
|
||
BACKUP_DIR="${DATA_DIR}/backup"
|
||
KEEP=${BACKUP_KEEP:-14}
|
||
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
|
||
TARGET="${BACKUP_DIR}/pear-${TIMESTAMP}.db"
|
||
|
||
if [[ ! -f "${DB_PATH}" ]]; then
|
||
echo "FATAL: 数据库文件不存在: ${DB_PATH}" >&2
|
||
exit 1
|
||
fi
|
||
|
||
mkdir -p "${BACKUP_DIR}"
|
||
|
||
echo "[backup] db: ${DB_PATH}"
|
||
echo "[backup] -> : ${TARGET}"
|
||
# sqlite3 二进制在 Dockerfile 里 apt install 了;镜像外本机有的话也用得上
|
||
sqlite3 "${DB_PATH}" ".backup '${TARGET}'"
|
||
|
||
# 压缩一份小的;同时保留原文件方便快速恢复
|
||
gzip -f "${TARGET}"
|
||
|
||
# 清理旧备份:保留文件名最近 KEEP 个(含 .gz 后缀)
|
||
KEPT=0
|
||
for f in $(ls -1t "${BACKUP_DIR}"/pear-*.db.gz 2>/dev/null); do
|
||
KEPT=$((KEPT + 1))
|
||
if [[ "${KEPT}" -gt "${KEEP}" ]]; then
|
||
rm -f "${f}"
|
||
echo "[backup] removed old: ${f}"
|
||
fi
|
||
done
|
||
|
||
# 健康检查:备份文件必须 > 0 字节且能正常打开
|
||
LATEST="${BACKUP_DIR}/pear-${TIMESTAMP}.db.gz"
|
||
if [[ ! -s "${LATEST}" ]]; then
|
||
echo "FATAL: 备份文件为空: ${LATEST}" >&2
|
||
exit 1
|
||
fi
|
||
echo "[backup] done. latest = ${LATEST}"
|
||
echo "[backup] current keep: $(ls -1 "${BACKUP_DIR}"/pear-*.db.gz 2>/dev/null | wc -l) of ${KEEP}" |