#!/usr/bin/env python3 """Backup online SQLite (T7) — copie consistenta a bazei pe volum persistent. SQLite in WAL NU se copiaza sigur cu `cp` (WAL-ul poate avea tranzactii necheckpoint-ate). Folosim API-ul `Connection.backup` (online, consistent, fara oprirea worker-ului). Utilizare: python -m tools.backup # -> /backups/autopass-YYYYMMDD-HHMMSS.db python -m tools.backup --out /path/snap.db # destinatie explicita python -m tools.backup --keep 14 # pastreaza ultimele 14, sterge restul Recomandat: rulat dintr-un cron pe gazda (ex. zilnic), tinta pe volum/montaj separat. """ from __future__ import annotations import argparse import sqlite3 import sys from datetime import datetime, timezone from pathlib import Path from app.config import get_settings def backup_db(src: Path, dest: Path) -> Path: """Backup online (consistent) din `src` in `dest`. Intoarce `dest`.""" dest.parent.mkdir(parents=True, exist_ok=True) src_conn = sqlite3.connect(src) try: dst_conn = sqlite3.connect(dest) try: src_conn.backup(dst_conn) finally: dst_conn.close() finally: src_conn.close() return dest def prune(backup_dir: Path, keep: int) -> list[Path]: """Pastreaza cele mai noi `keep` snapshot-uri (dupa nume = timestamp), sterge restul.""" snaps = sorted(backup_dir.glob("autopass-*.db"), reverse=True) removed = [] for old in snaps[keep:]: old.unlink(missing_ok=True) removed.append(old) return removed def main(argv: list[str] | None = None) -> int: parser = argparse.ArgumentParser(description="Backup online SQLite gateway RAR AUTOPASS") parser.add_argument("--out", type=Path, default=None, help="destinatie explicita (default: /backups/)") parser.add_argument("--keep", type=int, default=0, help="pastreaza ultimele N snapshot-uri (0 = nu sterge)") args = parser.parse_args(argv) src = get_settings().db_path if not src.exists(): print(f"eroare: baza {src} nu exista", file=sys.stderr) return 1 if args.out is not None: dest = args.out else: stamp = datetime.now(timezone.utc).strftime("%Y%m%d-%H%M%S") dest = src.parent / "backups" / f"autopass-{stamp}.db" backup_db(src, dest) print(f"backup -> {dest} ({dest.stat().st_size} bytes)") if args.keep > 0: removed = prune(dest.parent, args.keep) if removed: print(f"sterse {len(removed)} snapshot-uri vechi (pastrez {args.keep})") return 0 if __name__ == "__main__": sys.exit(main())