"""Database backup/check/list and explicitly offline restore; no app initialization.""" import argparse import json from pathlib import Path import sqlite3 import sys ROOT = Path(__file__).resolve().parents[1] if str(ROOT) not in sys.path: sys.path.insert(0, str(ROOT)) from logistics_chat.database_backup import (check_database, daily_backup, restore_database, snapshot_database, unique_backup_path) def main(argv=None): parser = argparse.ArgumentParser(description=__doc__) parser.add_argument('--db', type=Path, default=ROOT/'history.db') parser.add_argument('--backup-dir', type=Path, default=None) parser.add_argument('command', choices=('backup', 'daily', 'list', 'check', 'restore')) parser.add_argument('--input', type=Path) parser.add_argument('--service-stopped', action='store_true', help='Confirm all database users are stopped before restore') args = parser.parse_args(argv) database = args.db.resolve() directory = (args.backup_dir or database.parent/'backups').resolve() try: if args.command == 'backup': path = snapshot_database(database, unique_backup_path(directory, 'manual')) print('Backup: ' + str(path)) elif args.command == 'daily': print('Backup: ' + str(daily_backup(database, directory))) elif args.command == 'list': for path in sorted(directory.glob('*.db'), reverse=True): print(json.dumps({'file':str(path), 'bytes':path.stat().st_size}, ensure_ascii=False)) elif args.command == 'check': print(check_database(args.input or database)) elif args.command == 'restore': if not args.service_stopped: parser.error('Restore requires --service-stopped; stop the app and all database users first') if args.input is None: parser.error('Restore requires --input') safety = restore_database(database, args.input, directory) print('Restored: ' + str(database)) if safety: print('Safety backup: ' + str(safety)) except (OSError, ValueError, RuntimeError, sqlite3.Error) as error: print('Database operation failed: ' + str(error), file=sys.stderr) return 1 return 0 if __name__ == '__main__': raise SystemExit(main())