diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..efdba87 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +* text=auto +*.sh text eol=lf diff --git a/.gitignore b/.gitignore index a2422fd..e65d728 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,7 @@ build/ caddy_data/ caddy_config/ pgdata/ +backups/ frontend/dist/ *.tsbuildinfo .DS_Store diff --git a/scripts/backup-db.sh b/scripts/backup-db.sh new file mode 100644 index 0000000..b0e842f --- /dev/null +++ b/scripts/backup-db.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash +# Dumps the Postgres database from the running "postgres" container into ./backups/, +# in pg_dump's custom format (compressed, restorable with pg_restore — see restore-db.sh). +# +# Usage: scripts/backup-db.sh +set -euo pipefail + +cd "$(dirname "$0")/.." + +mkdir -p backups +timestamp="$(date +%Y%m%d_%H%M%S)" +out="backups/dnd_backup_${timestamp}.dump" +trap 'rm -f "${out}"' ERR + +echo "Dumping database into ${out} ..." +docker compose exec -T postgres sh -c 'pg_dump -U "$POSTGRES_USER" -d "$POSTGRES_DB" -F c' > "${out}" + +size="$(du -h "${out}" | cut -f1)" +echo "Done: ${out} (${size})" diff --git a/scripts/restore-db.sh b/scripts/restore-db.sh new file mode 100644 index 0000000..c37f377 --- /dev/null +++ b/scripts/restore-db.sh @@ -0,0 +1,43 @@ +#!/usr/bin/env bash +# Restores a dump produced by backup-db.sh into the running "postgres" container. +# Destructive: drops and recreates every object in the target database before restoring. +# +# Usage: scripts/restore-db.sh path/to/dnd_backup_20260101_120000.dump [-y] +# -y skip the confirmation prompt (for non-interactive/scripted use) +set -euo pipefail + +cd "$(dirname "$0")/.." + +skip_confirm=false +file="" +for arg in "$@"; do + if [ "$arg" = "-y" ]; then + skip_confirm=true + else + file="$arg" + fi +done + +if [ -z "$file" ]; then + echo "Usage: $0 path/to/backup.dump [-y]" >&2 + exit 1 +fi +if [ ! -f "$file" ]; then + echo "Error: file not found: $file" >&2 + exit 1 +fi + +if [ "$skip_confirm" = false ]; then + read -r -p "This will DROP and recreate every table in the target database. Continue? [y/N] " reply + case "$reply" in + [yY]|[yY][eE][sS]) ;; + *) echo "Aborted."; exit 1 ;; + esac +fi + +echo "Restoring ${file} ..." +docker compose exec -T postgres sh -c \ + 'pg_restore -U "$POSTGRES_USER" -d "$POSTGRES_DB" --clean --if-exists --no-owner --no-privileges --single-transaction' \ + < "$file" + +echo "Done."