From cc07c03d82f488f479d9b84fe38dbf58607a1da4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Gruszczy=C5=84ski?= Date: Sun, 26 Oct 2025 21:36:37 +0100 Subject: [PATCH] backup before update --- npm_install.py | 55 ++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 47 insertions(+), 8 deletions(-) diff --git a/npm_install.py b/npm_install.py index 83de805..50c3aa4 100644 --- a/npm_install.py +++ b/npm_install.py @@ -2015,7 +2015,6 @@ def print_summary(info, ipv6_enabled, dark_enabled, tp_theme, update_mode): # ========== UPDATE-ONLY ========== - def update_only( node_pkg: str, node_version: str | None, @@ -2041,6 +2040,49 @@ def update_only( txt = re.sub(r'"version":\s*"0\.0\.0"', f'"version": "{version}"', txt) pj.write_text(txt, encoding="utf-8") + # ========== BACKUP BEFORE UPDATE ========== + with step("Creating full backup before update"): + timestamp = time.strftime("%Y%m%d-%H%M%S") + backup_dir = Path(f"/data/backups/npm-backup-{timestamp}") + backup_dir.parent.mkdir(parents=True, exist_ok=True) + + try: + if Path("/opt/npm").exists(): + shutil.copytree("/opt/npm", backup_dir / "opt_npm", dirs_exist_ok=True) + + if Path("/data/database.sqlite").exists(): + shutil.copy2("/data/database.sqlite", backup_dir / "database.sqlite") + if Path("/data/letsencrypt").exists(): + shutil.copytree("/data/letsencrypt", backup_dir / "letsencrypt", dirs_exist_ok=True) + if Path("/data/nginx").exists(): + shutil.copytree("/data/nginx", backup_dir / "nginx", dirs_exist_ok=True) + + # Save backup info + backup_info = { + "backup_date": timestamp, + "npm_version": "current", + "update_to_version": version, + "backup_path": str(backup_dir) + } + (backup_dir / "backup_info.json").write_text(json.dumps(backup_info, indent=2)) + + print(f" Backup saved to: {backup_dir}") + + backups = sorted(backup_dir.parent.glob("npm-backup-*")) + if len(backups) > 3: + for old_backup in backups[:-3]: + shutil.rmtree(old_backup, ignore_errors=True) + print(f" Removed old backup: {old_backup.name}") + + except Exception as e: + print(f"⚠ Warning: Backup failed: {e}") + print(" Continue update anyway? [y/N]: ", end="", flush=True) + response = input().strip().lower() + if response not in ["y", "yes"]: + print("Update cancelled.") + sys.exit(1) + # ========== END BACKUP ========== + _build_frontend(src / "frontend", Path("/opt/npm/frontend")) with step("Updating backend without overwriting config/"): @@ -2057,6 +2099,7 @@ def update_only( else: item.unlink() shutil.copytree(src / "backend", "/opt/npm", dirs_exist_ok=True) + shutil.copytree(src / "global", "/opt/npm/global", dirs_exist_ok=True) Path("/opt/npm/config").mkdir(parents=True, exist_ok=True) if backup_cfg.exists(): shutil.copytree(backup_cfg, "/opt/npm/config", dirs_exist_ok=True) @@ -2075,10 +2118,8 @@ def update_only( if apply_dark: apply_dark_mode(**dark_env) - save_installer_config({ "ipv6_enabled": ipv6_enabled, - "dark_mode_enabled": apply_dark, "tp_theme": dark_env.get("TP_THEME") if apply_dark else None, "tp_domain": dark_env.get("TP_DOMAIN", TP_DOMAIN), "tp_scheme": dark_env.get("TP_SCHEME", TP_SCHEME), @@ -2087,7 +2128,6 @@ def update_only( "npm_version": version, }) - with step("Restarting services after update"): run(["systemctl", "restart", "angie.service"], check=False) run(["systemctl", "restart", "npm.service"], check=False) @@ -2095,7 +2135,7 @@ def update_only( return version -# ========== DARK MODE ========== +# ========== CUSTOM THEME ========== def apply_dark_mode( @@ -2261,9 +2301,9 @@ def main(): selected_theme = installer_config["tp_theme"] print(f"✓ Using stored theme: {selected_theme}") - if not args.dark_mode and installer_config.get("dark_mode_enabled"): + if not args.dark_mode and not args.tp_theme and installer_config.get("tp_theme"): args.dark_mode = True - print(f"✓ Using stored dark mode setting: enabled") + print(f"✓ Using stored Theme-Park setting: enabled") stored_ipv6 = installer_config.get("ipv6_enabled", args.enable_ipv6) @@ -2341,7 +2381,6 @@ def main(): # Save installation configuration for future updates save_installer_config({ "ipv6_enabled": args.enable_ipv6, - "dark_mode_enabled": dark_mode_enabled, "tp_theme": selected_theme, "tp_domain": TP_DOMAIN, "tp_scheme": TP_SCHEME,