#!/usr/bin/env bash set -euo pipefail BACKUP_FILE="${1:-${BACKUP_FILE:-}}" if [[ -z "${BACKUP_FILE:-}" ]]; then echo "Usage: $0 " >&2 exit 1 fi if [[ ! -f "$BACKUP_FILE" ]]; then echo "[ERR] backup file not found: $BACKUP_FILE" >&2 exit 1 fi if [[ -z "${DATABASE_URL:-}" ]]; then echo "[ERR] DATABASE_URL is required" >&2 exit 1 fi RESTORE_SCOPE="${RESTORE_SCOPE:-}" RESTORE_CONFIRM_DATABASE="${RESTORE_CONFIRM_DATABASE:-}" DB_TOOL_MODE="${DB_TOOL_MODE:-auto}" if [[ "$DB_TOOL_MODE" != "auto" && "$DB_TOOL_MODE" != "host" && "$DB_TOOL_MODE" != "docker" ]]; then echo "[ERR] DB_TOOL_MODE must be auto, host, or docker" >&2 exit 1 fi if [[ "$RESTORE_SCOPE" != "shadow" ]]; then echo "[ERR] RESTORE_SCOPE=shadow is required; production restore is not available from this command path" >&2 exit 1 fi if [[ -z "$RESTORE_CONFIRM_DATABASE" ]]; then echo "[ERR] RESTORE_CONFIRM_DATABASE is required" >&2 exit 1 fi validate_dump_with_host() { pg_restore --list "$BACKUP_FILE" >/dev/null } validate_shadow_database_name() { local database_name="$1" if [[ ! "$database_name" =~ (^|[_-])(shadow|drill|restore_test)([_-]|$) ]]; then echo "[ERR] restore target name is not an explicit shadow/drill database" >&2 exit 1 fi } restore_with_host() { local actual_database actual_database="$(psql "$DATABASE_URL" -Atqc 'SELECT current_database()')" if [[ -z "$actual_database" || "$actual_database" != "$RESTORE_CONFIRM_DATABASE" ]]; then echo "[ERR] restore target confirmation mismatch: actual=${actual_database:-unknown}" >&2 exit 1 fi validate_shadow_database_name "$actual_database" echo "[INFO] DB restore target confirmed: $actual_database (shadow)" pg_restore --clean --if-exists --no-owner --no-acl -d "$DATABASE_URL" "$BACKUP_FILE" } if [[ "$DB_TOOL_MODE" != "docker" ]] && command -v pg_restore >/dev/null 2>&1 && command -v psql >/dev/null 2>&1; then if validate_dump_with_host; then restore_with_host echo "[OK] DB restore done (pg_restore): $BACKUP_FILE" exit 0 fi if [[ "$DB_TOOL_MODE" == "host" ]]; then echo "[ERR] host pg_restore could not validate the dump" >&2 exit 1 fi echo "[WARN] host pg_restore could not validate the dump; trying docker fallback" >&2 fi if [[ "$DB_TOOL_MODE" == "host" ]]; then echo "[ERR] DB_TOOL_MODE=host requires pg_restore and psql" >&2 exit 1 fi if ! command -v docker >/dev/null 2>&1; then echo "[ERR] pg_restore/psql not found and docker not available" >&2 exit 1 fi DB_CONTAINER="${DB_CONTAINER:-}" POSTGRES_DB="${POSTGRES_DB:-}" POSTGRES_USER="${POSTGRES_USER:-}" POSTGRES_PASSWORD="${POSTGRES_PASSWORD:-}" if [[ -z "$DB_CONTAINER" ]]; then echo "[ERR] Using docker fallback requires explicit DB_CONTAINER" >&2 exit 1 fi if [[ -z "$POSTGRES_DB" || -z "$POSTGRES_USER" || -z "$POSTGRES_PASSWORD" ]]; then echo "[ERR] Using docker fallback requires POSTGRES_DB/POSTGRES_USER/POSTGRES_PASSWORD" >&2 exit 1 fi ACTUAL_DATABASE="$(PGPASSWORD="$POSTGRES_PASSWORD" docker exec -e PGPASSWORD "$DB_CONTAINER" \ psql -U "$POSTGRES_USER" -d "$POSTGRES_DB" -Atqc 'SELECT current_database()')" if [[ -z "$ACTUAL_DATABASE" || "$ACTUAL_DATABASE" != "$RESTORE_CONFIRM_DATABASE" ]]; then echo "[ERR] restore target confirmation mismatch: actual=${ACTUAL_DATABASE:-unknown}" >&2 exit 1 fi validate_shadow_database_name "$ACTUAL_DATABASE" docker exec -i "$DB_CONTAINER" pg_restore --list <"$BACKUP_FILE" >/dev/null echo "[INFO] DB restore target confirmed: $ACTUAL_DATABASE (shadow)" PGPASSWORD="$POSTGRES_PASSWORD" docker exec -i -e PGPASSWORD "$DB_CONTAINER" \ pg_restore --clean --if-exists --no-owner --no-acl -U "$POSTGRES_USER" -d "$POSTGRES_DB" \ <"$BACKUP_FILE" echo "[OK] DB restore done (docker exec): $BACKUP_FILE"