#!/usr/bin/env bash
# self-hosted-supabase-backup.sh: back up a self-hosted Supabase stack (Docker Compose)
# and copy it, encrypted, to an S3-compatible bucket.
# From https://backupdrill.com/guides/self-hosted-supabase-backup (MIT licensed).
#
# Run it from the directory with docker-compose.yml. Needs rclone with a remote
# named "offsite": a crypt remote over your bucket, set up as the guide shows.
set -euo pipefail
umask 077  # the output holds password hashes, the Vault key and .env

STAMP=$(date -u +%Y-%m-%dT%H%M%SZ)
OUT="$HOME/supabase-backups/$STAMP"
mkdir -p "$HOME/supabase-backups"
mkdir "$OUT"  # fails instead of mixing two runs in one directory

# 1. Roles and their password hashes. They belong to the server, not to a database.
docker compose exec -T db pg_dumpall -U supabase_admin --globals-only > "$OUT/globals.sql"

# 2. Every database. --create keeps each database's owner, grants and settings.
DATABASE_NAMES=$(docker compose exec -T db psql -U supabase_admin -d postgres -Atc \
  "select datname from pg_database where not datistemplate")
mapfile -t DATABASES <<< "$DATABASE_NAMES"
for db in "${DATABASES[@]}"; do
  # the name becomes a file name; anything unusual stops the backup instead
  if [[ ! "$db" =~ ^[A-Za-z0-9_-]+$ ]]; then echo "unexpected database name: $db" >&2; exit 1; fi
  docker compose exec -T db pg_dump -U supabase_admin -Fc --create -d "$db" > "$OUT/$db.dump"
done

# 3. From the db-config volume: the root key Vault encrypts secrets with, and conf.d,
#    where custom Postgres settings go.
docker compose exec -T db tar -C /etc/postgresql-custom -cf - pgsodium_root.key conf.d > "$OUT/db-config.tar"

# 4. Storage files. Each file's content type and cache-control are stored in xattrs.
#    storage-path.txt records where in the archive Storage keeps them, as Storage sees it.
tar --xattrs --xattrs-include='user.*' -czf "$OUT/storage.tar.gz" -C volumes storage
docker compose exec -T storage sh -c 'echo "$GLOBAL_S3_BUCKET/$TENANT_ID"' > "$OUT/storage-path.txt"

# 5. Everything else: .env, compose files, function code, gateway config.
tar --exclude=./volumes/db/data --exclude=./volumes/storage -czf "$OUT/config.tar.gz" .

(cd "$OUT" && sha256sum -- *.sql *.dump *.tar *.txt *.tar.gz > SHA256SUMS)

# 6. Encrypted copy off the server; keep a week of local copies for quick restores.
rclone copy "$OUT" "offsite:$STAMP"
find "$HOME/supabase-backups" -mindepth 1 -maxdepth 1 -type d -mtime +7 -exec rm -rf {} +

echo "backup $STAMP done: $(du -sh "$OUT" | cut -f1)"
