diff --git a/scripts/restore-postgres.sh b/scripts/restore-postgres.sh new file mode 100644 index 0000000..8de2bf3 --- /dev/null +++ b/scripts/restore-postgres.sh @@ -0,0 +1,122 @@ +#!/usr/bin/env bash +# ============================================================================= +# restore-postgres.sh – Wiederherstellung einer PostgreSQL-Datenbank aus Borg +# Läuft auf dem Raspberry Pi +# ============================================================================= + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +CONFIG_FILE="${SCRIPT_DIR}/../config/backup.conf" +source "$CONFIG_FILE" + +RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; BLUE='\033[0;34m'; NC='\033[0m' +log() { echo -e "${BLUE}[RESTORE]${NC} $*"; } +log_ok() { echo -e "${GREEN}[RESTORE] ✓${NC} $*"; } +log_warn(){ echo -e "${YELLOW}[RESTORE] ⚠${NC} $*"; } +log_err() { echo -e "${RED}[RESTORE] ✗${NC} $*" >&2; } + +export BORG_PASSPHRASE +export BORG_REPO + +usage() { + echo "" + echo "Verwendung: $0 [OPTIONEN]" + echo "" + echo "Optionen:" + echo " -l, --list Verfügbare Archive auflisten" + echo " -a, --archive ARCHIVE Archiv-Name (z.B. mydb-2025-01-15T02:30)" + echo " -d, --database DB Ziel-Datenbank für Wiederherstellung" + echo " -t, --target-host HOST Ziel-Host (Standard: ${PG_HOST})" + echo " -o, --output-dir DIR Nur als Datei extrahieren (kein DB-Import)" + echo " -h, --help Diese Hilfe anzeigen" + echo "" + echo "Beispiele:" + echo " $0 --list" + echo " $0 --archive mydb-2025-01-15T02:30 --database mydb_restored" + echo " $0 --archive mydb-2025-01-15T02:30 --output-dir /tmp/restore" + echo "" + exit 0 +} + +list_archives() { + log "Verfügbare Archive in ${BORG_REPO}:" + echo "" + borg list --short "${BORG_REPO}" | sort -r | while read -r archive; do + local info + info=$(borg info "${BORG_REPO}::${archive}" 2>/dev/null | grep -E "Time|Duration|Deduplicated" || echo "") + echo " 📦 ${archive}" + echo "$info" | sed 's/^/ /' + echo "" + done +} + +restore_to_db() { + local archive="$1" + local target_db="$2" + local target_host="${3:-$PG_HOST}" + + log_warn "ACHTUNG: Datenbank '${target_db}' auf '${target_host}' wird überschrieben!" + read -rp "Fortfahren? (ja/NEIN): " confirm + [[ "$confirm" != "ja" ]] && { log "Abgebrochen."; exit 0; } + + log "Stelle '${archive}' → '${target_db}' auf '${target_host}' wieder her..." + + # Dump aus Borg extrahieren und direkt per SSH zu pg_restore pipen + borg extract --stdout "${BORG_REPO}::${archive}" \ + | ssh -i "${SSH_KEY_PATH}" \ + -o StrictHostKeyChecking=accept-new \ + "${PG_SSH_USER}@${target_host}" \ + "sudo -u ${PG_DB_USER} pg_restore \ + --dbname=${target_db} \ + --no-owner \ + --no-privileges \ + --clean \ + --if-exists \ + --format=custom" + + log_ok "Wiederherstellung abgeschlossen!" +} + +extract_to_dir() { + local archive="$1" + local output_dir="$2" + + mkdir -p "$output_dir" + log "Extrahiere '${archive}' nach '${output_dir}'..." + borg extract --destination "$output_dir" "${BORG_REPO}::${archive}" + log_ok "Datei(en) in: ${output_dir}" + ls -lh "$output_dir" +} + +# --- Argumente parsen -------------------------------------------------------- +LIST=false +ARCHIVE="" +DATABASE="" +TARGET_HOST="${PG_HOST}" +OUTPUT_DIR="" + +while [[ $# -gt 0 ]]; do + case "$1" in + -l|--list) LIST=true ;; + -a|--archive) ARCHIVE="$2"; shift ;; + -d|--database) DATABASE="$2"; shift ;; + -t|--target-host) TARGET_HOST="$2"; shift ;; + -o|--output-dir) OUTPUT_DIR="$2"; shift ;; + -h|--help) usage ;; + *) log_err "Unbekannte Option: $1"; usage ;; + esac + shift +done + +# --- Hauptlogik -------------------------------------------------------------- +if $LIST; then + list_archives +elif [[ -n "$ARCHIVE" && -n "$OUTPUT_DIR" ]]; then + extract_to_dir "$ARCHIVE" "$OUTPUT_DIR" +elif [[ -n "$ARCHIVE" && -n "$DATABASE" ]]; then + restore_to_db "$ARCHIVE" "$DATABASE" "$TARGET_HOST" +else + log_err "Keine gültige Aktion angegeben." + usage +fi