param( [string]$SourceRoot = "\\192.168.178.58\backups", [string]$DestinationRoot = "H:\kallilab-nearline-backups", [switch]$WhatIf ) $ErrorActionPreference = "Stop" $Jobs = @( @{ Name = "borg-dumps-latest" Source = Join-Path $SourceRoot "borg\dumps\latest" Destination = Join-Path $DestinationRoot "borg-dumps\latest" Purpose = "Latest database/application dumps (Unraid-Flash-Artefakte bewusst ausgeschlossen, weil 0600 root:root - dafuer bleibt die Hetzner-Borg-Kette die Restore-Quelle)" # /XF schliesst bewusst die unraid-flash-config-Artefakte aus, # weil sie hostseitig 0600 root:root sind und der SMB-Share das # nicht ueberbruecken kann. Restore-Quelle dafuer bleibt das # Hetzner-Borg-Repo (siehe docs/RESTORE_MATRIX.md Tier 1 Unraid OS Flash). ExcludeFiles = @("unraid-flash-config.tar.gz", "unraid-flash-config.tar.gz.sha256", "unraid-flash-config.manifest.txt") }, @{ Name = "gitea-bundles" Source = Join-Path $SourceRoot "git-bundles\gitea" Destination = Join-Path $DestinationRoot "git-bundles\gitea" Purpose = "Verified bare-repository bundles for Gitea bootstrap" ExcludeFiles = @() } ) function Assert-PathExists { param( [string]$Path, [string]$Label ) if (-not (Test-Path -LiteralPath $Path)) { throw "$Label not found: $Path" } } function Invoke-RobocopyJob { param( [hashtable]$Job, [string]$LogRoot ) $logPath = Join-Path $LogRoot ("{0}-{1}.log" -f (Get-Date -Format "yyyyMMdd-HHmmss"), $Job.Name) New-Item -ItemType Directory -Force -Path $Job.Destination | Out-Null $args = @( $Job.Source, $Job.Destination, "/E", "/COPY:DAT", "/DCOPY:DAT", "/R:2", "/W:5", "/FFT", "/XJ", "/XD", ".tmp", "/NP", "/TEE", "/LOG:$logPath" ) if ($Job.ContainsKey("ExcludeFiles") -and $Job.ExcludeFiles.Count -gt 0) { $args += "/XF" $args += $Job.ExcludeFiles } Write-Host "Running robocopy job: $($Job.Name)" Write-Host " Source: $($Job.Source)" Write-Host " Destination: $($Job.Destination)" & robocopy @args $code = $LASTEXITCODE if ($code -gt 7) { throw "Robocopy job '$($Job.Name)' failed with exit code $code. See log: $logPath" } [pscustomobject]@{ Name = $Job.Name Source = $Job.Source Destination = $Job.Destination ExitCode = $code Log = $logPath } } Assert-PathExists -Path $SourceRoot -Label "Source root" foreach ($job in $Jobs) { Assert-PathExists -Path $job.Source -Label "Source for job '$($job.Name)'" } if ($WhatIf) { Write-Host "H:/ nearline pull plan only. No files will be copied." Write-Host "SourceRoot: $SourceRoot" Write-Host "DestinationRoot: $DestinationRoot" Write-Host "" foreach ($job in $Jobs) { Write-Host "- $($job.Name)" Write-Host " Purpose: $($job.Purpose)" Write-Host " Source: $($job.Source)" Write-Host " Destination: $($job.Destination)" } exit 0 } $destinationDrive = Split-Path -Qualifier $DestinationRoot Assert-PathExists -Path $destinationDrive -Label "Destination drive" $logRoot = Join-Path $DestinationRoot "_logs" $reportRoot = Join-Path $DestinationRoot "_reports" New-Item -ItemType Directory -Force -Path $DestinationRoot, $logRoot, $reportRoot | Out-Null $results = foreach ($job in $Jobs) { Invoke-RobocopyJob -Job $job -LogRoot $logRoot } $reportPath = Join-Path $reportRoot ("nearline-pull-{0}.md" -f (Get-Date -Format "yyyy-MM-dd-HHmmss")) $lines = @() $lines += "# H:/ Nearline Pull Report - $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')" $lines += "" $lines += "- Source root: ``$SourceRoot``" $lines += "- Destination root: ``$DestinationRoot``" $lines += "- Mode: non-destructive copy, no ``/MIR``, no purge" $lines += "" $lines += "| Job | Exit code | Source | Destination | Log |" $lines += "|---|---:|---|---|---|" foreach ($result in $results) { $lines += "| $($result.Name) | $($result.ExitCode) | ``$($result.Source)`` | ``$($result.Destination)`` | ``$($result.Log)`` |" } $lines += "" $lines += "Expected critical artifacts after run:" $lines += "" $lines += "- ``borg-dumps/latest/immich.dump``" $lines += "- ``borg-dumps/latest/komodo-mongo.archive.gz``" $lines += "- ``git-bundles/gitea/latest-report.md``" $lines += "- ``git-bundles/gitea/micha/*.bundle``" $lines += "" $lines += "Bewusst NICHT in Nearline-Scope:" $lines += "" $lines += "- ``unraid-flash-config.tar.gz`` (hostseitig 0600 root:root; Restore aus Hetzner-Borg)" $lines | Set-Content -LiteralPath $reportPath -Encoding UTF8 Write-Host "H:/ nearline pull completed." Write-Host "Report: $reportPath"