#!/usr/bin/env bash
# Dump Postgres and hosted bare repos under APP_DIR/backups.
set -euo pipefail

ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
APP_DIR="${APP_DIR:-/opt/tarakan}"
if [[ -d "$APP_DIR/scripts/deploy" ]]; then
  :
elif [[ -d "$ROOT_DIR/scripts/deploy" ]]; then
  APP_DIR="$ROOT_DIR"
fi

BACKUP_DIR="${BACKUP_DIR:-$APP_DIR/backups}"
RETENTION_DAYS="${RETENTION_DAYS:-7}"
COMPOSE_FILE="${COMPOSE_FILE:-$APP_DIR/deploy/docker/compose.yml}"

cd "$APP_DIR"
# Backups are shared only between the service account and the deployment
# account through the private linuxuser group.
umask 007

set -a
# shellcheck disable=SC1091
. "$APP_DIR/.env"
set +a

mkdir -p "$BACKUP_DIR"
test -f "$COMPOSE_FILE"

# The systemd timer and a deployment can request a backup at the same time.
exec 8> "$BACKUP_DIR/.backup.lock"
flock 8

timestamp="$(date -u +%Y%m%dT%H%M%S%NZ)"
partial="$BACKUP_DIR/.backup-$timestamp.partial"
complete="$BACKUP_DIR/backup-$timestamp"

rm -rf "$partial"
mkdir "$partial"

cleanup() {
  rm -rf "$partial"
}
trap cleanup EXIT HUP INT TERM

compose() {
  docker compose \
    --project-directory "$APP_DIR" \
    -f "$COMPOSE_FILE" \
    --env-file "$APP_DIR/.env" \
    "$@"
}

compose exec -T db pg_dump \
  --username "$POSTGRES_USER" \
  --dbname "${POSTGRES_DB:-tarakan}" \
  --format custom \
  --compress 6 \
  > "$partial/database.dump"

compose exec -T db pg_restore --list \
  < "$partial/database.dump" \
  > /dev/null

compose exec -T app tar \
  --create \
  --gzip \
  --file - \
  --directory /app/storage/hosted \
  . \
  > "$partial/hosted.tar.gz"

tar --list --gzip --file "$partial/hosted.tar.gz" > /dev/null

artifacts=(database.dump hosted.tar.gz)

# The SSH host key is a few kilobytes and cannot be regenerated: a new one is
# indistinguishable from a man-in-the-middle to every client that has already
# connected. The directory is empty until git-over-SSH is first enabled, and
# the guard also covers images built before it existed.
if compose exec -T app test -d /app/storage/ssh; then
  compose exec -T app tar \
    --create \
    --gzip \
    --file - \
    --directory /app/storage/ssh \
    . \
    > "$partial/ssh.tar.gz"

  tar --list --gzip --file "$partial/ssh.tar.gz" > /dev/null
  artifacts+=(ssh.tar.gz)
fi

(
  cd "$partial"
  sha256sum "${artifacts[@]}" > SHA256SUMS
)

mv "$partial" "$complete"
trap - EXIT HUP INT TERM

find "$BACKUP_DIR" \
  -mindepth 1 \
  -maxdepth 1 \
  -type d \
  -name 'backup-*' \
  -mtime "+$RETENTION_DAYS" \
  -exec rm -rf -- {} +

printf 'Created %s\n' "$complete"
