refactor: 备份/恢复改为「SQLite 归档 + 单事务整库替换」——不再换文件,也不再依赖 SQLite 运行时
SQLite 从"运行时数据库"降级为"备份交换格式":导出把当前库(MySQL 或 SQLite)
整库写成一份 SQLite 归档,恢复则是启动时在单个事务里 DELETE + INSERT。
前端三个接口的契约一行未改。
- core/system_backup.py 重写:
* dump_to_sqlite()(旧名 snapshot_db 保留为别名)取代在线备份 API:MySQL 下把
这条连接提到 REPEATABLE READ,保证 12 张表读的是同一时刻
* zip 结构不变(users.db + manifest.json + apks/),既有老备份继续可导入;不依赖
任何外部二进制(sqlite3 是标准库,python:slim 容器里现成)
* manifest 增加 db_backend / deployment_env / source_db_id,用于识别备份来源环境
* apply_restore:安全网从裸 pre_restore_*.db 升级为 pre_restore_*.zip(可直接再导入
回滚);跨环境导入默认硬停(需 force_env_mismatch=true)
* consume_pending_restore:启动时单事务整库替换,任何一步失败 rollback,当前数据
完好(比换文件更安全);坏归档挪 restore_failed_* 且不阻塞启动
* 归档库收尾时 checkpoint 回单文件模式并清掉 -wal/-shm(zip 只打包主文件)
- web_server.py:consume 从 init_db 之前移到之后(现在是事务替换,需要表与 app context)
- config.py:BACKUP_DIR / RESTORE_STAGING_DIR / RESTORE_PENDING_DIR 支持环境变量覆盖
—— 自动化测试必须把恢复目录指到临时位置,否则测试造的"待生效恢复任务"会在服务
下次重启时被当成用户的操作消费掉
- core/db_config.py:SQLite 回退模式也写库环境标签(备份要能标出来源环境)
- web/system_api.py + static/admin/system.js + templates/admin/monitor.html:
预览显示来源/当前环境,跨环境时出现「允许跨环境导入」勾选项;文案同步
验证(隔离副本,绝不碰真实库):导出→zip 结构与 manifest 正确;预览带来源环境与
告警;应用→生成 pre_restore zip + 归档就位;跨环境 apply 被拒;模拟重启→数据回到
导出时刻、经验库/动作库完好;塞入坏归档→挪 restore_failed_*、服务照常启动、数据未变。
This commit is contained in:
+9
-3
@@ -66,10 +66,16 @@ def api_system_backup_preview():
|
||||
@bp.route("/api/system/backup/apply", methods=["POST"])
|
||||
@admin_required
|
||||
def api_system_backup_apply():
|
||||
"""确认应用导入:自动备份当前库 → 落待生效恢复任务(重启后生效)。"""
|
||||
token = (request.json or {}).get("token", "")
|
||||
"""确认应用导入:自动备份当前库 → 落待生效恢复任务(重启后生效)。
|
||||
|
||||
force_env_mismatch 默认 False:备份来源环境与当前库环境不符时拒绝导入,
|
||||
要跨环境(如把生产数据导进 dev 库)必须显式传 true。
|
||||
"""
|
||||
body = request.json or {}
|
||||
token = body.get("token", "")
|
||||
force = bool(body.get("force_env_mismatch", False))
|
||||
try:
|
||||
res = sb.apply_restore(str(token).strip())
|
||||
res = sb.apply_restore(str(token).strip(), force_env_mismatch=force)
|
||||
except sb.BackupError as e:
|
||||
return jsonify({"ok": False, "error": str(e)}), 400
|
||||
except Exception as e:
|
||||
|
||||
Reference in New Issue
Block a user