#!/usr/bin/env bash set -euo pipefail ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" source "${ROOT_DIR}/scripts/docker-prod-common.sh" BACKUP_DIR="${1:-}" CONFIRM_FLAG="${2:-}" if [[ -z "${BACKUP_DIR}" ]]; then echo "Usage: bash scripts/docker-prod-restore.sh --yes" >&2 exit 1 fi if [[ "${CONFIRM_FLAG}" != "--yes" ]]; then echo "Restore is destructive and will overwrite the production database and uploaded files." >&2 echo "Re-run with: bash scripts/docker-prod-restore.sh ${BACKUP_DIR} --yes" >&2 exit 1 fi if [[ ! -d "${BACKUP_DIR}" ]]; then echo "Backup directory not found: ${BACKUP_DIR}" >&2 exit 1 fi if [[ ! -f "${BACKUP_DIR}/postgres.dump" ]]; then echo "Missing database backup: ${BACKUP_DIR}/postgres.dump" >&2 exit 1 fi if [[ ! -f "${BACKUP_DIR}/api-uploads.tar.gz" ]]; then echo "Missing uploads backup: ${BACKUP_DIR}/api-uploads.tar.gz" >&2 exit 1 fi ensure_env_file ensure_traefik_network RAW_VOLUME_DIR="${BACKUP_DIR}/volumes" SKIP_RAW_RESTORE_VOLUMES=( "${PROJECT_NAME}_api_uploads" "${PROJECT_NAME}_postgres_prod_data" ) echo "Stopping application services before restore..." prod_compose stop api carplace dashboard admin pgmanage >/dev/null || true prod_compose stop redis >/dev/null || true stop_traefik echo "Ensuring PostgreSQL is running..." prod_compose up -d postgres >/dev/null echo "Waiting for PostgreSQL..." until prod_compose exec -T postgres sh -lc 'pg_isready -U "$POSTGRES_USER" -d "$POSTGRES_DB"' >/dev/null 2>&1; do sleep 2 done echo "Restoring PostgreSQL database..." cat "${BACKUP_DIR}/postgres.dump" | prod_compose exec -T postgres sh -lc ' pg_restore -U "$POSTGRES_USER" -d "$POSTGRES_DB" --clean --if-exists --no-owner --no-privileges ' echo "Restoring uploaded files..." cat "${BACKUP_DIR}/api-uploads.tar.gz" | prod_compose run --rm --no-deps api bash -lc ' mkdir -p /var/lib/rentaldrivego/storage find /var/lib/rentaldrivego/storage -mindepth 1 -delete tar -xzf - -C /var/lib/rentaldrivego/storage ' if [[ -f "${BACKUP_DIR}/traefik-letsencrypt.tar.gz" ]]; then echo "Restoring Traefik certificates..." cat "${BACKUP_DIR}/traefik-letsencrypt.tar.gz" | traefik_compose run --rm traefik sh -lc ' mkdir -p /letsencrypt find /letsencrypt -mindepth 1 -delete tar -xzf - -C /letsencrypt chmod 600 /letsencrypt/acme.json 2>/dev/null || true ' fi if [[ -d "${RAW_VOLUME_DIR}" ]]; then echo "Restoring raw Docker volume archives..." for archive_path in "${RAW_VOLUME_DIR}"/*.tar.gz; do [[ -e "${archive_path}" ]] || continue volume_name="$(basename "${archive_path}" .tar.gz)" skip_volume=false for managed_volume in "${SKIP_RAW_RESTORE_VOLUMES[@]}"; do if [[ "${volume_name}" == "${managed_volume}" ]]; then skip_volume=true break fi done if [[ "${skip_volume}" == true ]]; then echo "Skipping raw restore for ${volume_name}; handled by service-aware restore." continue fi if ! docker_volume_exists "${volume_name}"; then echo "Creating missing volume ${volume_name}" docker volume create "${volume_name}" >/dev/null fi echo " - ${volume_name}" restore_named_volume "${volume_name}" "${archive_path}" done fi echo "Starting production services..." start_traefik start_prod_services echo "Restore complete from ${BACKUP_DIR}"