#!/usr/bin/env bash
# Maintenance backup of this Compose project only. Run on Linux, from any cwd.
set -euo pipefail
umask 077
cd "$(dirname "$(realpath "$0")")"
destination=${1:?Usage: backup.sh /absolute/private/backup-root}
mode=${2:-resume}
[[ "$mode" = resume || "$mode" = leave-stopped ]] || { echo 'Unknown maintenance mode' >&2; exit 2; }
[[ "$destination" = /* ]] || { echo 'Absolute private backup directory required' >&2; exit 2; }
mkdir -p -- "$destination"
destination=$(realpath "$destination")
exec 9>"$destination/.ftg-backup.lock"
flock -n 9 || { echo 'Another backup holds this destination lock' >&2; exit 2; }
bundle="$destination/ftg-$(date -u +%Y%m%dT%H%M%SZ)"
mkdir -- "$bundle"
compose=(docker compose --env-file .env.server -f compose.yaml)
running=$("${compose[@]}" ps --services --status running)
paused=()
for service in caddy beat infrastructure-beat web worker match-worker friendly-worker token-bridge-worker; do
  if grep -qx "$service" <<<"$running"; then paused+=("$service"); fi
done
resume() { if [[ "$mode" = resume ]] && ((${#paused[@]})); then "${compose[@]}" up -d --no-deps "${paused[@]}"; fi; }
trap resume EXIT
# Stop incoming writes and drain in-flight worker work before capturing DB/media.
if ((${#paused[@]})); then "${compose[@]}" stop -t 660 "${paused[@]}"; fi
"${compose[@]}" exec -T db sh -ec 'case "$POSTGRES_DB" in ftg2_beta_*) ;; *) echo "Dedicated beta DB required" >&2; exit 2;; esac; pg_dump -U "$POSTGRES_USER" -d "$POSTGRES_DB" -Fc --no-owner --no-privileges' > "$bundle/database.dump.partial"
mv -- "$bundle/database.dump.partial" "$bundle/database.dump"
"${compose[@]}" run --rm --no-deps --entrypoint python web -c 'import sys,tarfile; t=tarfile.open(fileobj=sys.stdout.buffer,mode="w|gz"); t.add("/srv/private-media",arcname="private-media"); t.close()' > "$bundle/private-media.tar.gz.partial"
mv -- "$bundle/private-media.tar.gz.partial" "$bundle/private-media.tar.gz"
# A replication snapshot preserves queued/unacknowledged Redis messages too.
"${compose[@]}" exec -T cache redis-cli --rdb /tmp/ftg-recovery.rdb
"${compose[@]}" cp cache:/tmp/ftg-recovery.rdb "$bundle/redis.rdb"
"${compose[@]}" images -q > "$bundle/image-ids.txt"
"${compose[@]}" exec -T db sh -ec 'printf "%s\n" "$POSTGRES_DB"' > "$bundle/database-name.txt"
"${compose[@]}" run --rm --no-deps --entrypoint python web manage.py showmigrations --plan > "$bundle/migrations.txt"
(cd "$bundle"; sha256sum database.dump private-media.tar.gz redis.rdb image-ids.txt database-name.txt migrations.txt > SHA256SUMS)
printf '%s\n' 'COMPLETE: restricted local backup. Encrypt and copy offsite before treating this as disaster recovery.' > "$bundle/STATUS.txt"
echo "Backup captured: $bundle"
# EXIT trap restores only services that were running before the backup; a
# deliberately stopped world scheduler remains stopped.
