"""Consistent SQLite backup plus attachment files; only IKAR paths are used.""" from pathlib import Path import datetime, os, sqlite3, tarfile, tempfile root = Path('/mnt/data/ikar_server') if not os.path.ismount('/mnt/data'): raise SystemExit('External disk is not mounted') source = root / 'shared/data' stamp = datetime.datetime.now(datetime.timezone.utc).strftime('%Y%m%dT%H%M%SZ') destination = root / f'backups/ikar-{stamp}.tar.gz' with tempfile.TemporaryDirectory(prefix='backup-', dir=root / 'backups') as temp: database = Path(temp) / 'ikar.db' with sqlite3.connect(f'file:{source / "ikar.db"}?mode=ro', uri=True) as live, sqlite3.connect(database) as backup: live.backup(backup) if backup.execute('pragma quick_check').fetchone()[0] != 'ok': raise SystemExit('Database backup failed integrity check') with tarfile.open(destination, 'w:gz') as bundle: bundle.add(database, arcname='data/ikar.db') for directory in source.iterdir(): if directory.is_dir() and directory.name not in ('QlyraUploads', 'AppUpdates'): bundle.add(directory, arcname=f'data/{directory.name}') os.chmod(destination, 0o600) cutoff = datetime.datetime.now(datetime.timezone.utc).timestamp() - 14 * 86400 for previous in (root / 'backups').glob('ikar-*.tar.gz'): if previous != destination and previous.is_file() and previous.stat().st_mtime < cutoff: previous.unlink() print(destination)