From b0afff53afed68c1f0401fa73ef7482095a0c04d Mon Sep 17 00:00:00 2001 From: dekun Date: Thu, 2 Jul 2026 16:38:51 +0800 Subject: [PATCH] Ensure scheduled auto backups explicitly include .env in backup archives. Co-authored-by: Cursor --- modules/backup/db_backup.py | 47 +++++++++++++++++------------ modules/web/templates/settings.html | 2 +- 2 files changed, 28 insertions(+), 21 deletions(-) diff --git a/modules/backup/db_backup.py b/modules/backup/db_backup.py index 406b99f..27cd4a5 100644 --- a/modules/backup/db_backup.py +++ b/modules/backup/db_backup.py @@ -516,20 +516,21 @@ def _backup_sqlite(src_path: str, dst_path: str) -> None: def _env_backup_info() -> tuple[Optional[Path], str]: """返回 (源 .env 路径, 恢复到应用目录的相对路径)。""" - from modules.core.paths import CONFIG_DIR, LEGACY_ENV_FILE, ROOT, resolve_env_file + from modules.core.paths import CONFIG_DIR, ENV_FILE, LEGACY_ENV_FILE - src = Path(resolve_env_file()) - if not src.is_file(): - return None, "config/.env" - try: - if src.resolve().parent == CONFIG_DIR.resolve(): - return src, "config/.env" - if src.resolve().parent == ROOT.resolve() and src.name == ".env": - if LEGACY_ENV_FILE.is_file() and not (CONFIG_DIR / ".env").is_file(): - return src, ".env" - except Exception: - pass - return src, "config/.env" + if ENV_FILE.is_file(): + return ENV_FILE, "config/.env" + if LEGACY_ENV_FILE.is_file(): + return LEGACY_ENV_FILE, ".env" + return None, "config/.env" + + +def _copy_env_into_backup(work: Path) -> tuple[bool, str]: + env_src, env_restore_path = _env_backup_info() + if env_src and env_src.is_file(): + shutil.copy2(env_src, work / ".env") + return True, env_restore_path + return False, env_restore_path def _write_restore_script(dest: Path, *, env_restore_path: str = "") -> None: @@ -565,7 +566,7 @@ echo "详见 RESTORE.md 与 docs/BACKUP.md" dest.write_text(script, encoding="utf-8") -def create_backup(*, include_uploads: bool = True) -> tuple[str, str]: +def create_backup(*, include_uploads: bool = True, include_env: bool = True) -> tuple[str, str]: """创建 tar.gz 备份,返回 (文件名, 说明)。""" if not os.path.isfile(DB_PATH): raise FileNotFoundError(f"数据库不存在: {DB_PATH}") @@ -586,11 +587,12 @@ def create_backup(*, include_uploads: bool = True) -> tuple[str, str]: if include_uploads and upload_src.is_dir(): shutil.copytree(upload_src, work / "uploads", dirs_exist_ok=True) - env_src, env_restore_path = _env_backup_info() includes_env = False - if env_src and env_src.is_file(): - shutil.copy2(env_src, work / ".env") - includes_env = True + env_restore_path = "config/.env" + if include_env: + includes_env, env_restore_path = _copy_env_into_backup(work) + if not includes_env: + logger.warning("backup: .env not found (checked config/.env and root .env)") manifest = { "app": "qihuo", @@ -617,7 +619,8 @@ def create_backup(*, include_uploads: bool = True) -> tuple[str, str]: tar.add(work, arcname=folder_name) size_mb = out_path.stat().st_size / (1024 * 1024) - return filename, f"备份已生成 {filename}({size_mb:.2f} MB)" + env_note = "含 .env" if includes_env else "未含 .env" + return filename, f"备份已生成 {filename}({size_mb:.2f} MB,{env_note})" def list_backups(*, with_manifest: bool = True) -> list[dict]: @@ -678,13 +681,14 @@ def run_backup_job( get_setting: Callable[[str, str], str], set_setting: Callable[[str, str], None], include_uploads: bool = True, + include_env: bool = True, ) -> tuple[str, str]: keep = DEFAULT_KEEP_COUNT try: keep = max(5, min(200, int(get_setting(BACKUP_KEEP_KEY, str(DEFAULT_KEEP_COUNT)) or DEFAULT_KEEP_COUNT))) except ValueError: pass - filename, msg = create_backup(include_uploads=include_uploads) + filename, msg = create_backup(include_uploads=include_uploads, include_env=include_env) set_setting(BACKUP_LAST_KEY, datetime.now(TZ).isoformat(timespec="seconds")) removed = prune_old_backups(keep) if removed: @@ -697,6 +701,7 @@ def schedule_backup( get_setting: Callable[[str, str], str], set_setting: Callable[[str, str], None], include_uploads: bool = True, + include_env: bool = True, ) -> tuple[bool, str]: if _backup_lock.locked(): return False, "备份进行中,请稍后再试" @@ -709,6 +714,7 @@ def schedule_backup( get_setting=get_setting, set_setting=set_setting, include_uploads=include_uploads, + include_env=include_env, ) except Exception as exc: logger.exception("backup failed: %s", exc) @@ -751,6 +757,7 @@ def start_backup_worker( get_setting=get_setting_fn, set_setting=set_setting_fn, include_uploads=True, + include_env=True, ) logger.info("auto backup: %s — %s", filename, msg) except Exception as exc: diff --git a/modules/web/templates/settings.html b/modules/web/templates/settings.html index 6d7c674..6138175 100644 --- a/modules/web/templates/settings.html +++ b/modules/web/templates/settings.html @@ -536,7 +536,7 @@