- Replace em dashes (UTF-8 multi-byte) with ASCII hyphens; PowerShell 5.1 reads ps1 files as CP1252 by default so any non-ASCII in source causes cascading parse errors - Extract POSTGRES_PASSWORD from container env (Aspire uses scram-sha-256; peer/trust auth does not work) and pass as PGPASSWORD env var to pg_dump and psql in both Backup and Restore scripts - @() wrap on Get-ChildItem result to force array before .Count under Set-StrictMode -Version Latest (single-item returns a bare object in PS5) - Restore-test evidence: 73 ColorVarieties -> DELETE 12 -> 61 -> pg_restore -> 73 confirmed (run 2026-06-06 07:09 against postgres-gatkzrgq) Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
153 lines
4.9 KiB
PowerShell
153 lines
4.9 KiB
PowerShell
<#
|
|
.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."
|