<# .SYNOPSIS Stellt GerbilManager aus einem Backup wieder her. .DESCRIPTION WARNUNG: Ueberschreibt alle aktuellen Datenbankdaten und Fotos! 1. Stoppt GerbilManager (falls laufend). 2. Stellt die Datenbank aus dem pg_dump-SQL-File wieder her. 3. Entpackt das Foto-Archiv. 4. Startet GerbilManager neu. .PARAMETER BackupDir Pfad zum Backup-Unterverzeichnis (z.B. C:\gerbil-data\backups\2026-06-06_03-00). Wenn nicht angegeben, wird das neueste verfuegbare Backup verwendet. .PARAMETER PhotosRoot Pfad zum Fotoordner. Standard: C:\gerbil-data\photos .PARAMETER NoRestart GerbilManager nach der Wiederherstellung NICHT neu starten. .EXAMPLE # Neuestes Backup wiederherstellen .\Restore-GerbilManager.ps1 # Bestimmtes Backup wiederherstellen .\Restore-GerbilManager.ps1 -BackupDir "C:\gerbil-data\backups\2026-06-05_03-00" #> param( [string]$BackupDir = "", [string]$PhotosRoot = "C:\gerbil-data\photos", [switch]$NoRestart ) Set-StrictMode -Version Latest $ErrorActionPreference = "Stop" $backupRoot = "C:\gerbil-data\backups" function Log([string]$msg) { Write-Host "[$(Get-Date -Format 'HH:mm:ss')] $msg" } # --- Backup-Verzeichnis bestimmen --- if ($BackupDir -eq "") { $latest = Get-ChildItem -Path $backupRoot -Directory | Sort-Object LastWriteTime -Descending | Select-Object -First 1 if (-not $latest) { Log "FEHLER: Kein Backup gefunden in $backupRoot" exit 1 } $BackupDir = $latest.FullName } if (-not (Test-Path $BackupDir)) { Log "FEHLER: Backup-Verzeichnis nicht gefunden: $BackupDir" exit 1 } # --- Dateien im Backup suchen --- $sqlFile = Get-ChildItem -Path $BackupDir -Filter "*.sql" | Select-Object -First 1 $zipFile = Get-ChildItem -Path $BackupDir -Filter "*.zip" | Select-Object -First 1 if (-not $sqlFile) { Log "FEHLER: Kein SQL-Dump (.sql) in $BackupDir gefunden." exit 1 } Log "Wiederherstellung aus: $BackupDir" Log " Datenbank: $($sqlFile.Name)" if ($zipFile) { Log " Fotos : $($zipFile.Name)" } # --- Bestaetigung --- Write-Host "" Write-Host "WARNUNG: Alle aktuellen Datenbankdaten und Fotos werden UNWIDERRUFLICH ueberschrieben!" -ForegroundColor Red $confirm = Read-Host "Fortfahren? (ja/nein)" if ($confirm -ne "ja") { Log "Abgebrochen." exit 0 } # --- GerbilManager stoppen (falls laufend) --- $stopScript = Join-Path $PSScriptRoot "Stop-GerbilManager.ps1" if (Test-Path $stopScript) { Log "Stoppe GerbilManager..." & $stopScript Start-Sleep -Seconds 3 } # --- Postgres-Container finden --- Log "Suche Postgres-Container..." $containers = docker ps --format "{{.Names}}" 2>&1 $pgContainer = $containers -split "`n" | Where-Object { $_ -match "postgres" } | Select-Object -First 1 if (-not $pgContainer) { # Container koennte gestoppt sein - starten wir ihn kurz Log "Postgres-Container laeuft nicht. Starte ihn zunaechst..." Log "Bitte starten Sie GerbilManager kurz einmal (Start-GerbilManager.ps1) und versuchen Sie es erneut." exit 1 } $pgContainer = $pgContainer.Trim() Log "Container: $pgContainer" # Aspire setzt scram-sha-256 Auth - Passwort aus Container-Env lesen $pgPassword = (docker inspect $pgContainer --format "{{range .Config.Env}}{{println .}}{{end}}" 2>&1) -split "`n" | Where-Object { $_ -match "^POSTGRES_PASSWORD=" } | ForEach-Object { $_ -replace "^POSTGRES_PASSWORD=", "" } | Select-Object -First 1 if (-not $pgPassword) { Log "FEHLER: POSTGRES_PASSWORD nicht im Container gefunden." exit 1 } # --- Datenbankwiederherstellung --- Log "Stelle Datenbank wieder her aus $($sqlFile.Name)..." # Verbindungen trennen docker exec -e "PGPASSWORD=$pgPassword" $pgContainer psql -U postgres -c ` "SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = 'gerbilmanager' AND pid <> pg_backend_pid();" | Out-Null # SQL-Dump einspielen (--clean im Dump macht DROP TABLE IF EXISTS vor CREATE) Get-Content $sqlFile.FullName -Raw | docker exec -i -e "PGPASSWORD=$pgPassword" $pgContainer psql -U postgres gerbilmanager if ($LASTEXITCODE -ne 0) { Log "FEHLER: Datenbankwiederherstellung fehlgeschlagen (exit $LASTEXITCODE)." exit 1 } Log "Datenbankwiederherstellung erfolgreich." # --- Fotos wiederherstellen --- if ($zipFile) { Log "Stelle Fotos wieder her..." if (Test-Path $PhotosRoot) { Remove-Item "$PhotosRoot\*" -Recurse -Force -ErrorAction SilentlyContinue } else { New-Item -ItemType Directory -Force -Path $PhotosRoot | Out-Null } Expand-Archive -Path $zipFile.FullName -DestinationPath $PhotosRoot -Force $count = (Get-ChildItem $PhotosRoot -File).Count Log "Fotos wiederhergestellt: $count Dateien." } # --- GerbilManager neu starten --- if (-not $NoRestart) { $startScript = Join-Path $PSScriptRoot "Start-GerbilManager.ps1" if (Test-Path $startScript) { Log "Starte GerbilManager neu..." & $startScript } } Log "Wiederherstellung abgeschlossen."