Files
mission-control-v2/backend/services/backup.py
T
Hitonabi 1f4c987652 feat(2.0): Phase 5 — Betrieb, Observability + Politur
Backup: services/backup.py (Memory-SQLite inkl. WAL/SHM + Configs, retain 7)
+ deploy/backup.sh + POST /api/system/backup + GET /api/system/backups.
Services-Health: GET /api/system/services (aggregierte Reachability +
Observability-Links). Frontend: SystemView mit Dienste-Health + Backup-
Button + Links; Theme-Toggle (Hell/Dunkel, persistent).

Lokal verifiziert: Backup-Snapshot + services-Aggregat (TestClient),
Frontend-Build + Browser (Light-Mode bestaetigt via computed styles).
Damit Phasen 0-5 lokal fertig. Docs aktualisiert.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 08:29:27 +02:00

52 lines
1.6 KiB
Python

"""
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, GATEWAY_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, GATEWAY_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