#!/usr/bin/env bash # # Daily DB backup. Cron-friendly. Detects SQLite vs Postgres from DATABASE_URL. # # Example crontab: # 15 3 * * * /path/to/scripts/backup_db.sh >> /var/log/trumpsignal-backup.log 2>&1 # # Retention: keeps the last RETAIN_DAYS daily backups (default 14). # Storage: writes to $BACKUP_DIR (default ./backups/). # # DATABASE_URL must be available in env. Loads .env if present. set -euo pipefail cd "$(dirname "$0")/.." # Load .env if it exists (so cron jobs without inherited env still work) if [[ -f .env ]]; then set -a # shellcheck disable=SC1091 source .env set +a fi : "${DATABASE_URL:?DATABASE_URL not set — refusing to back up nothing}" BACKUP_DIR="${BACKUP_DIR:-./backups}" RETAIN_DAYS="${RETAIN_DAYS:-14}" TS="$(date -u +%Y%m%d_%H%M%S)" mkdir -p "$BACKUP_DIR" if [[ "$DATABASE_URL" == sqlite* ]]; then # Extract path from sqlite+aiosqlite:///./trumpsignal.db DB_PATH="$(echo "$DATABASE_URL" | sed -E 's#^sqlite(\+[^:]+)?:///+##')" if [[ ! -f "$DB_PATH" ]]; then echo "[backup] SQLite file not found: $DB_PATH" >&2 exit 1 fi OUT="$BACKUP_DIR/sqlite_${TS}.db" # sqlite3 .backup is online-safe (uses backup API, not a raw copy). # Fall back to cp if sqlite3 CLI isn't installed. if command -v sqlite3 >/dev/null 2>&1; then sqlite3 "$DB_PATH" ".backup '$OUT'" else cp "$DB_PATH" "$OUT" fi gzip -f "$OUT" echo "[backup] wrote $OUT.gz ($(du -h "$OUT.gz" | cut -f1))" elif [[ "$DATABASE_URL" == postgres* ]] || [[ "$DATABASE_URL" == postgresql* ]]; then OUT="$BACKUP_DIR/pg_${TS}.sql.gz" # pg_dump reads DATABASE_URL natively if we strip the driver prefix. PG_URL="${DATABASE_URL/+asyncpg/}" PG_URL="${PG_URL/+psycopg2/}" pg_dump "$PG_URL" --no-owner --no-privileges | gzip > "$OUT" echo "[backup] wrote $OUT ($(du -h "$OUT" | cut -f1))" else echo "[backup] unsupported DATABASE_URL scheme: $DATABASE_URL" >&2 exit 1 fi # Prune backups older than RETAIN_DAYS days find "$BACKUP_DIR" -name "*.gz" -mtime "+$RETAIN_DAYS" -print -delete