<# .SYNOPSIS Sichert die GerbilManager-Datenbank (pg_dump) und den Fotoordner. .DESCRIPTION 1. Findet den laufenden Aspire-Postgres-Docker-Container. 2. Fuehrt pg_dump aus und speichert das SQL-Dump in C:\gerbil-data\backups\. 3. Kopiert den Fotoordner als ZIP ins Backup-Verzeichnis. 4. Rotiert alte Backups: behaelt die letzten $KeepDays Tage. .PARAMETER BackupRoot Pfad zum Backup-Verzeichnis. Standard: C:\gerbil-data\backups .PARAMETER PhotosRoot Pfad zum Fotoordner. Standard: C:\gerbil-data\photos .PARAMETER KeepDays Anzahl der Tage, die Backups aufbewahrt werden. Standard: 7 .EXAMPLE .\Backup-GerbilManager.ps1 .\Backup-GerbilManager.ps1 -KeepDays 14 -BackupRoot D:\Sicherungen #> param( [string]$BackupRoot = "C:\gerbil-data\backups", [string]$PhotosRoot = "C:\gerbil-data\photos", [int]$KeepDays = 7 ) Set-StrictMode -Version Latest $ErrorActionPreference = "Stop" $timestamp = Get-Date -Format "yyyy-MM-dd_HH-mm" $backupDir = Join-Path $BackupRoot $timestamp $logFile = Join-Path $BackupRoot "backup.log" $dbDumpFile = Join-Path $backupDir "gerbilmanager_$timestamp.sql" $photoZip = Join-Path $backupDir "photos_$timestamp.zip" function Log([string]$msg) { $line = "[$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')] $msg" Write-Host $line Add-Content -Path $logFile -Value $line -Encoding UTF8 } # --- Voraussetzungen pruefen --- if (-not (Get-Command "docker" -ErrorAction SilentlyContinue)) { Log "FEHLER: Docker nicht gefunden. Backup abgebrochen." exit 1 } # --- Backup-Verzeichnis anlegen --- New-Item -ItemType Directory -Force -Path $backupDir | Out-Null Log "Backup-Verzeichnis: $backupDir" # --- 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) { Log "FEHLER: Kein laufender Postgres-Container gefunden. Ist GerbilManager gestartet?" exit 1 } $pgContainer = $pgContainer.Trim() Log "Gefundener 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 } # --- Datenbank-Dump --- Log "Starte pg_dump fuer Datenbank 'gerbilmanager'..." try { docker exec -e "PGPASSWORD=$pgPassword" $pgContainer ` pg_dump --clean --if-exists --format=plain --username=postgres gerbilmanager ` | Out-File -FilePath $dbDumpFile -Encoding UTF8 $dumpSize = [math]::Round((Get-Item $dbDumpFile).Length / 1KB, 1) Log "Datenbank-Dump erstellt: $dbDumpFile ($dumpSize KB)" } catch { Log "FEHLER beim Datenbank-Dump: $_" exit 1 } # Dump-Validierung: muss mindestens 'CREATE TABLE' enthalten $dumpContent = Get-Content $dbDumpFile -Raw -ErrorAction SilentlyContinue if ($dumpContent -notlike "*CREATE TABLE*" -and $dumpContent -notlike "*PostgreSQL*") { Log "WARNUNG: Dump-Datei sieht ungueltig aus - pruefen Sie $dbDumpFile manuell." } # --- Fotos sichern --- if (Test-Path $PhotosRoot) { $photoCount = (Get-ChildItem $PhotosRoot -File -ErrorAction SilentlyContinue).Count if ($photoCount -gt 0) { Log "Komprimiere $photoCount Fotos nach $photoZip..." try { Compress-Archive -Path "$PhotosRoot\*" -DestinationPath $photoZip -Force $zipSize = [math]::Round((Get-Item $photoZip).Length / 1MB, 1) Log "Foto-Archiv erstellt: $photoZip ($zipSize MB)" } catch { Log "WARNUNG: Foto-Backup fehlgeschlagen: $_" } } else { Log "Kein Fotoordner oder keine Fotos vorhanden - Foto-Backup uebersprungen." } } else { Log "Fotoordner nicht gefunden ($PhotosRoot) - Foto-Backup uebersprungen." } # --- Rotation: alte Backups loeschen --- Log "Rotiere Backups (behalte letzte $KeepDays Tage)..." $cutoff = (Get-Date).AddDays(-$KeepDays) $oldBackups = Get-ChildItem -Path $BackupRoot -Directory | Where-Object { $_.LastWriteTime -lt $cutoff } foreach ($old in $oldBackups) { Log "Loesche altes Backup: $($old.FullName)" Remove-Item $old.FullName -Recurse -Force } $remaining = @(Get-ChildItem -Path $BackupRoot -Directory).Count Log "Backup abgeschlossen. Vorhandene Backups: $remaining"