#!/usr/bin/env python3 """ PostgreSQL backup script for SeaFare Montana. Run via cron or manually: python backup_db.py Backs up the database to a timestamped SQL file. Usage: python backup_db.py # Backup to ./backups/ python backup_db.py /path/to/dir # Backup to specified dir """ import os import sys import subprocess from datetime import datetime from pathlib import Path from dotenv import load_dotenv load_dotenv() DATABASE_URL = os.environ.get('DATABASE_URL') def backup(): if not DATABASE_URL: print("ERROR: DATABASE_URL not set. Cannot backup SQLite — just copy the .db file.") sys.exit(1) backup_dir = Path(sys.argv[1]) if len(sys.argv) > 1 else Path(__file__).parent / 'backups' backup_dir.mkdir(parents=True, exist_ok=True) timestamp = datetime.utcnow().strftime('%Y%m%d_%H%M%S') filename = backup_dir / f'seafare_backup_{timestamp}.sql' print(f"Backing up to {filename}...") # pg_dump using DATABASE_URL result = subprocess.run( ['pg_dump', DATABASE_URL, '--no-owner', '--no-acl', '-f', str(filename)], capture_output=True, text=True ) if result.returncode != 0: print(f"ERROR: pg_dump failed: {result.stderr}") sys.exit(1) size_kb = filename.stat().st_size / 1024 print(f"Backup complete: {filename} ({size_kb:.1f} KB)") # Keep only last 10 backups backups = sorted(backup_dir.glob('seafare_backup_*.sql'), reverse=True) for old in backups[10:]: old.unlink() print(f"Removed old backup: {old.name}") if __name__ == '__main__': backup()