#!/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