1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
|
$ErrorActionPreference = "Stop"
# -------------------- Konfiguration --------------------
$LogDir = "C:\Scripts\Logs"
$LogFile = Join-Path $LogDir "backup_ntfy.log"
$LogRotateMaxBytes = 5MB # bei Überschreiten wird rotiert (alte Datei weggesichert)
$Robo = "C:\Windows\System32\Robocopy.exe"
$RoboArgsCommon = @("/MIR", "/MT", "/R:1", "/W:5")
$Jobs = @(
@{ Name="Job1"; Src="Source1"; Dst="Destination1" },
@{ Name="Job2"; Src="Source2"; Dst="Destination2" },
@{ Name="Job3"; Src="Source3"; Dst="Destination3" },
@{ Name="Job4"; Src="Source4"; Dst="Destination4" }
)
# ntfy CLI
$NtfyExe = "C:\Scripts\ntfy.exe"
$NtfyToken = "TOKEN"
$NtfyUrl = "https://your.ntfy.server.domain/topic"
# -------------------- Helpers --------------------
function Write-Log([string]$Msg, [string]$Level = "INFO") {
$ts = Get-Date -Format "yyyy-MM-dd HH:mm:ss,fff"
"$ts [$Level] $Msg" | Out-File -FilePath $LogFile -Append -Encoding utf8
}
function Rotate-Log {
if (Test-Path $LogFile) {
$size = (Get-Item $LogFile).Length
if ($size -gt $LogRotateMaxBytes) {
$ts = Get-Date -Format "yyyyMMdd_HHmmss"
$dest = Join-Path $LogDir "backup_ntfy_$ts.log"
Copy-Item $LogFile $dest -Force
Clear-Content $LogFile
}
}
}
# Neue Funktion: Statuscode in lesbare Flags dekodieren
function Decode-RoboCode([int]$Code) {
$flags = @()
if ($Code -band 1) { $flags += "Copied/Updated" }
if ($Code -band 2) { $flags += "Extras/Deleted" }
if ($Code -band 4) { $flags += "Mismatched" }
if ($Code -band 8) { $flags += "CopyErrors" }
if ($Code -band 16) { $flags += "SeriousError" }
if (-not $flags) { $flags = @("NoChanges") }
return ($flags -join ", ")
}
# Schweregrad (nur zur Gesamtauswertung)
function Get-RoboSeverity([int]$Code) {
if ($Code -ge 16) { return "ERROR" }
elseif ($Code -ge 8) { return "ERROR" }
else { return "OK" }
}
function Invoke-RoboJob($Job) {
$name = $Job.Name; $src = $Job.Src; $dst = $Job.Dst
Write-Log "Starte Robocopy [$name] : `"$src`" -> `"$dst`""
$sw = [System.Diagnostics.Stopwatch]::StartNew()
& $Robo $src $dst @RoboArgsCommon | Out-Null
$sw.Stop()
$code = $LASTEXITCODE
$decoded = Decode-RoboCode $code
$sev = Get-RoboSeverity $code
Write-Log "Robocopy [$name] beendet. Code=$code ($decoded), Severity=$sev, Dauer=$([math]::Round($sw.Elapsed.TotalSeconds,1))s"
return @{
Name = $name
Code = $code
Flags = $decoded
Severity = $sev
DurationSec = $sw.Elapsed.TotalSeconds
}
}
# -------------------- Hauptlogik --------------------
try {
New-Item -ItemType Directory -Path $LogDir -Force | Out-Null
Rotate-Log
New-Item -ItemType File -Path $LogFile -Force | Out-Null
Write-Log "Backup gestartet (User=$(whoami))"
$swTotal = [System.Diagnostics.Stopwatch]::StartNew()
if (-not (Test-Path $Robo -PathType Leaf)) { Write-Log "Robocopy fehlt: $Robo" "ERROR"; throw "Robocopy fehlt" }
if (-not (Test-Path $NtfyExe -PathType Leaf)) { Write-Log "ntfy.exe fehlt: $NtfyExe" "ERROR"; throw "ntfy fehlt" }
$results = @()
foreach ($job in $Jobs) { $results += Invoke-RoboJob $job }
# Neues Summary mit Flags
$lines = $results | ForEach-Object {
"{0}: {1} (Code {2}, {3}s)" -f $_.Name, $_.Flags, $_.Code, [math]::Round($_.DurationSec,1)
}
$summary = ($lines -join "`n")
# Gesamtstatus: nur Fehler bei >=8
$overall = "OK"
if ($results.Severity -contains "ERROR") { $overall = "ERROR" }
$swTotal.Stop()
Write-Log "Gesamtstatus: $overall"
Write-Log "Gesamtdauer: $([math]::Round($swTotal.Elapsed.TotalSeconds,1))s"
Write-Log "Zusammenfassung:`n$summary"
# ---------- ntfy senden ----------
$title = "Backup Sync: $overall"
$msg = "Dauer: $([math]::Round($swTotal.Elapsed.TotalSeconds,1))s`n$summary"
$args = @("publish","--token",$NtfyToken,"--title",$title,$NtfyUrl,$msg)
$argsForLog = $args | ForEach-Object { if ($_ -eq $NtfyToken) { "***" } else { $_ } }
Write-Log ("Rufe ntfy.exe auf: " + $NtfyExe + " " + ($argsForLog -join " "))
$output = & $NtfyExe @args 2>&1
$code = $LASTEXITCODE
if ($output) { Write-Log ("ntfy.exe output:`n" + $output.ToString().Trim()) }
Write-Log "ntfy.exe ExitCode=$code"
Write-Log "Backup fertig"
}
catch {
Write-Log ("FEHLER: " + $_.Exception.Message) "ERROR"
if ($_.ScriptStackTrace) { Write-Log ("Stack: " + $_.ScriptStackTrace) "ERROR" }
}
finally {
exit 0
}
|