Ensure scheduled auto backups explicitly include .env in backup archives.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-07-02 16:38:51 +08:00
parent 8ebe1a3c77
commit b0afff53af
2 changed files with 28 additions and 21 deletions
+27 -20
View File
@@ -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:
+1 -1
View File
@@ -536,7 +536,7 @@
<div class="field">
<label style="display:flex;align-items:center;gap:.45rem;cursor:pointer">
<input type="checkbox" name="backup_auto_enabled" value="1" {% if backup_auto_enabled %}checked{% endif %}>
<span>启用每日自动备份</span>
<span>启用每日自动备份(含 <code>futures.db</code><code>uploads/</code><code>.env</code></span>
</label>
</div>
<div class="field">