Repository Public
1#!/bin/bash2# Encrypted PostgreSQL backup of SP Calendar. Run daily (PM2 cron). Keeps 30 days.3# pg_dump (custom format) | AES-256 (openssl, PBKDF2) → $BACKUP_DIR/sp-calendar-YYYYmmdd-HHMMSS.dump.enc4# Restore: scripts/restore.sh <file> <target database>5set -euo pipefail6cd "$(dirname "$0")/.."7# Read only the variables this script needs from .env (the file is not shell syntax: values may contain spaces).8if [ -f .env ]; then9 while IFS='=' read -r k v; do10 case "$k" in DATABASE_URL|BACKUP_DIR|BACKUP_PASSPHRASE_FILE)11 v="${v%\"}"; v="${v#\"}"; [ -n "${!k:-}" ] || export "$k=$v" ;;12 esac13 done < .env14fi15: "${DATABASE_URL:?DATABASE_URL missing}"; : "${BACKUP_DIR:?BACKUP_DIR missing}"; : "${BACKUP_PASSPHRASE_FILE:?BACKUP_PASSPHRASE_FILE missing}"16export PATH="/opt/homebrew/opt/postgresql@17/bin:/opt/homebrew/bin:$PATH"17[ -s "$BACKUP_PASSPHRASE_FILE" ] || { echo "passphrase file missing or empty" >&2; exit 1; }18mkdir -p "$BACKUP_DIR"; chmod 700 "$BACKUP_DIR"19out="$BACKUP_DIR/sp-calendar-$(date +%Y%m%d-%H%M%S).dump.enc"20umask 07721pg_dump --format=custom --no-owner --no-privileges "$DATABASE_URL" | openssl enc -aes-256-cbc -pbkdf2 -iter 200000 -salt -pass "file:$BACKUP_PASSPHRASE_FILE" -out "$out.tmp"22mv "$out.tmp" "$out"23# The archive must decrypt and be readable by pg_restore, otherwise the run fails loudly.24openssl enc -d -aes-256-cbc -pbkdf2 -iter 200000 -pass "file:$BACKUP_PASSPHRASE_FILE" -in "$out" | pg_restore --list >/dev/null25find "$BACKUP_DIR" -name 'sp-calendar-*.dump.enc' -mtime +30 -delete26echo "backup ok: $out ($(du -h "$out" | cut -f1))"27