#!/usr/bin/env bash
set -euo pipefail
umask 077
cd "$(dirname "$0")"
mode=${1:-s3}
if [[ "$mode" != s3 && "$mode" != --local ]]; then
  echo 'Usage: backup.sh [--local DIRECTORY]' >&2
  exit 2
fi
exec 9>.backup.lock
flock -n 9 || { echo 'Another backup is running.' >&2; exit 1; }
work=$(mktemp -d)
trap 'rm -rf "$work"' EXIT
docker compose exec -T database pg_dump -U calcom --no-owner -Fc calcom_oss > "$work/database.dump"
test -s "$work/database.dump"
cp .env compose.yaml setup.yaml Caddyfile cron.cjs source.json "$work/"
files=(database.dump .env compose.yaml setup.yaml Caddyfile cron.cjs source.json images.txt)
# Public deployments select this overlay through COMPOSE_FILE in the saved .env.
# Preserve its bind-mounted files too, including the matching source offer.
if [[ -f public.yaml ]]; then
  test -d public || { echo 'Public overlay requires the public directory.' >&2; exit 1; }
  cp public.yaml "$work/"
  cp -a public "$work/"
  files+=(public.yaml public)
fi
docker compose config --images > "$work/images.txt"
# Include actual IDs for images currently running as well as the configured tags.
docker compose images --format json >> "$work/images.txt"
archive="calcom-oss-$(date -u +%Y%m%dT%H%M%SZ)-$$.tar.gz"
tar -C "$work" -czf "$work/$archive" "${files[@]}"
if [[ "$mode" == --local ]]; then
  destination=${2:?Local destination directory required}
  mkdir -p "$destination"
  chmod 700 "$destination"
  mv "$work/$archive" "$destination/$archive"
  echo "Local backup saved: $destination/$archive"
else
  bucket=$(cat .backup-bucket)
  [[ "$bucket" =~ ^[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]$ ]] || { echo 'Invalid backup bucket.' >&2; exit 1; }
  aws s3 cp "$work/$archive" "s3://$bucket/backups/$archive" --sse AES256 --only-show-errors
  date -u +%FT%TZ > backup.last-success
  echo "Off-instance backup saved: s3://$bucket/backups/$archive"
fi
