"""Consistent SQLite + asset backup. Run: docker compose exec siteforge python -m app.backup""" import os import sqlite3 import tempfile import zipfile from pathlib import Path from datetime import datetime, timezone from . import db def backup(): db.init() storage=Path(db.DATA).resolve() path=storage/'backups' path.mkdir(exist_ok=True,parents=True) name='siteforge-backup-'+datetime.now(timezone.utc).strftime('%Y%m%d-%H%M%S')+'.zip' dest=path/name # SQLite online backup is consistent even when the DB uses WAL mode. with tempfile.TemporaryDirectory() as tmp: snapshot=Path(tmp)/'siteforge.sqlite3' with sqlite3.connect(db.DB_FILE) as source, sqlite3.connect(snapshot) as target: source.backup(target) with zipfile.ZipFile(dest,'w',compression=zipfile.ZIP_DEFLATED,compresslevel=5) as output: output.write(snapshot,'siteforge.sqlite3') for subdir in ('media','legacy','builds','archives'): root=storage/subdir if not root.is_dir():continue for file in root.rglob('*'): if file.is_file() and not file.is_symlink(): output.write(file, file.relative_to(storage).as_posix()) print(f'备份成功: {dest}') print('注意:备份文件仍在 Docker 数据卷内。请使用 docker compose cp 或其他方式再复制到外部设备。') return dest if __name__=='__main__':backup()