""" Backup der „Verfassung" (Shared-Memory-SQLite) + aller Configs. Snapshot in einen Zeitstempel-Ordner; die letzten N bleiben erhalten. """ import shutil import time from pathlib import Path from config import CONFIG_PATH, MEMORY_DB, MODELS_DIR BACKUP_DIR = Path(MODELS_DIR) / "mc2-backups" RETAIN = 7 def _safe_copy(src: Path, dst_dir: Path) -> str | None: try: if src and src.exists(): shutil.copy2(src, dst_dir / src.name) return src.name except Exception: pass return None def backup_now() -> dict: """Erstellt einen Snapshot (Memory-DB inkl. WAL/SHM + Configs). Alte Snapshots über RETAIN hinaus werden entfernt.""" ts = time.strftime("%Y%m%d-%H%M%S") dst = BACKUP_DIR / ts dst.mkdir(parents=True, exist_ok=True) saved = [] for src in (MEMORY_DB, Path(str(MEMORY_DB) + "-wal"), Path(str(MEMORY_DB) + "-shm"), CONFIG_PATH): if (name := _safe_copy(src, dst)): saved.append(name) # Aufräumen: nur die letzten RETAIN Snapshots behalten. snaps = sorted([p for p in BACKUP_DIR.iterdir() if p.is_dir()], reverse=True) for old in snaps[RETAIN:]: shutil.rmtree(old, ignore_errors=True) return {"ok": bool(saved), "snapshot": ts, "files": saved, "dir": str(dst)} def list_backups() -> list[dict]: if not BACKUP_DIR.exists(): return [] out = [] for p in sorted([d for d in BACKUP_DIR.iterdir() if d.is_dir()], reverse=True): files = [f.name for f in p.iterdir() if f.is_file()] out.append({"snapshot": p.name, "files": files}) return out