Dateien nach "/" hochladen

This commit is contained in:
2026-08-14 00:21:23 +02:00
parent 8bb4237793
commit 9d49321abc
4 changed files with 591 additions and 246 deletions
+356 -1
View File
@@ -440,15 +440,60 @@ function Ensure-ScheduledTask {
function Stop-RelayTask {
$task = Get-ScheduledTask -TaskName $TaskName -ErrorAction SilentlyContinue
if ($task -and $task.State -eq "Running") {
if (-not $task -or $task.State -ne "Running") {
return
}
$graceSeconds = 30
try {
$config = Get-RelayConfig -TargetPath $InstallPath
if ($config -and $config.Smtp.PSObject.Properties.Name -contains "GracefulShutdownSeconds") {
$configured = [int]$config.Smtp.GracefulShutdownSeconds
if ($configured -ge 5 -and $configured -le 300) {
$graceSeconds = $configured
}
}
}
catch {}
$signalPath = Join-Path $InstallPath "shutdown.request"
try {
[IO.File]::WriteAllText(
$signalPath,
([DateTime]::UtcNow.ToString("o")),
(New-Object Text.UTF8Encoding($false))
)
Write-Info "Graceful Shutdown angefordert. Warte auf Relay (max. $graceSeconds Sekunden)..."
$deadline = (Get-Date).AddSeconds($graceSeconds + 5)
while ((Get-Date) -lt $deadline) {
Start-Sleep -Milliseconds 250
$current = Get-ScheduledTask -TaskName $TaskName -ErrorAction SilentlyContinue
if (-not $current -or $current.State -ne "Running") {
Write-Ok "Relay sauber beendet."
return
}
}
Write-Warn "Graceful-Shutdown-Timeout erreicht. Task wird hart beendet."
Stop-ScheduledTask -TaskName $TaskName -ErrorAction SilentlyContinue
Start-Sleep -Milliseconds 750
}
finally {
Remove-Item -LiteralPath $signalPath -Force -ErrorAction SilentlyContinue
}
}
function Start-RelayTask {
$task = Get-ScheduledTask -TaskName $TaskName -ErrorAction SilentlyContinue
if ($task) {
Remove-Item -LiteralPath (Join-Path $InstallPath "shutdown.request") -Force -ErrorAction SilentlyContinue
Start-ScheduledTask -TaskName $TaskName
Start-Sleep -Seconds 2
Write-Ok "Scheduled Task gestartet."
@@ -879,6 +924,7 @@ function Install-New {
AuthMaxFailures = 5
AllowUnauthenticatedNetworks = @()
AuthUsers = @()
GracefulShutdownSeconds = 30
}
Graph = [ordered]@{
TenantId = $tenantId
@@ -1483,6 +1529,308 @@ function Save-SmtpAuthConfigAndRestart {
Start-RelayTask
}
function Get-FailedQueuePath {
param([Parameter(Mandatory)]$Config)
$path = [string]$Config.Paths.Failed
if ([IO.Path]::IsPathRooted($path)) {
return $path
}
return (Join-Path $InstallPath $path)
}
function Get-PendingQueuePath {
param([Parameter(Mandatory)]$Config)
$path = [string]$Config.Paths.Queue
if (-not [IO.Path]::IsPathRooted($path)) {
$path = Join-Path $InstallPath $path
}
return (Join-Path $path "pending")
}
function Get-FailedQueueEntries {
param([Parameter(Mandatory)]$Config)
$failedPath = Get-FailedQueuePath -Config $Config
if (-not (Test-Path -LiteralPath $failedPath)) {
return @()
}
$entries = @()
foreach ($file in Get-ChildItem -LiteralPath $failedPath -Filter "*.eml" -File -ErrorAction SilentlyContinue | Sort-Object LastWriteTime) {
$metaPath = "$($file.FullName).json"
$meta = $null
if (Test-Path -LiteralPath $metaPath) {
try {
$meta = Get-Content -LiteralPath $metaPath -Raw -Encoding UTF8 | ConvertFrom-Json
}
catch {}
}
$queueId = [IO.Path]::GetFileNameWithoutExtension($file.Name)
if ($meta -and $meta.PSObject.Properties.Name -contains "QueueId" -and $meta.QueueId) {
$queueId = [string]$meta.QueueId
}
$entries += [pscustomobject]@{
QueueId = $queueId
FileName = $file.Name
Path = $file.FullName
MetaPath = $metaPath
SizeKB = [Math]::Round($file.Length / 1KB, 1)
FailedSince = $file.LastWriteTime
RetryCount = if ($meta -and $meta.RetryCount -ne $null) { [int]$meta.RetryCount } else { $null }
From = if ($meta) { [string]$meta.EnvelopeFrom } else { "" }
Recipients = if ($meta) { (@($meta.EnvelopeRecipients) -join ", ") } else { "" }
LastStatusCode = if ($meta) { [string]$meta.LastStatusCode } else { "" }
LastError = if ($meta) { [string]$meta.LastError } else { "" }
AuthenticatedUser = if ($meta -and $meta.PSObject.Properties.Name -contains "AuthenticatedUser") { [string]$meta.AuthenticatedUser } else { "" }
Meta = $meta
}
}
return @($entries)
}
function Retry-FailedQueueEntry {
param(
[Parameter(Mandatory)]$Config,
[Parameter(Mandatory)]$Entry
)
$pendingPath = Get-PendingQueuePath -Config $Config
New-Item -ItemType Directory -Path $pendingPath -Force | Out-Null
$targetEml = Join-Path $pendingPath $Entry.FileName
$targetMeta = "$targetEml.json"
if (Test-Path -LiteralPath $targetEml) {
throw "Pending enthält bereits '$($Entry.FileName)'."
}
if ($Entry.Meta) {
$meta = $Entry.Meta
if ($meta.PSObject.Properties.Name -contains "RetryCount") {
$meta.RetryCount = 0
} else {
$meta | Add-Member -NotePropertyName RetryCount -NotePropertyValue 0
}
$now = [DateTime]::UtcNow.ToString("o")
if ($meta.PSObject.Properties.Name -contains "NextAttemptUtc") {
$meta.NextAttemptUtc = $now
} else {
$meta | Add-Member -NotePropertyName NextAttemptUtc -NotePropertyValue $now
}
if ($meta.PSObject.Properties.Name -contains "RequeuedUtc") {
$meta.RequeuedUtc = $now
} else {
$meta | Add-Member -NotePropertyName RequeuedUtc -NotePropertyValue $now
}
$meta | ConvertTo-Json -Depth 10 | Set-Content -LiteralPath $Entry.MetaPath -Encoding UTF8
}
if (Test-Path -LiteralPath $Entry.MetaPath) {
Move-Item -LiteralPath $Entry.MetaPath -Destination $targetMeta -Force
}
Move-Item -LiteralPath $Entry.Path -Destination $targetEml -Force
}
function Remove-FailedQueueEntry {
param([Parameter(Mandatory)]$Entry)
Remove-Item -LiteralPath $Entry.Path -Force -ErrorAction Stop
Remove-Item -LiteralPath $Entry.MetaPath -Force -ErrorAction SilentlyContinue
}
function Manage-FailedQueue {
$config = Get-RelayConfig -TargetPath $InstallPath
if (-not $config) {
Write-Fail "config.json nicht gefunden."
return
}
while ($true) {
Clear-Host
Write-Title "SMTPGraphRelay - Failed Queue"
$entries = @(Get-FailedQueueEntries -Config $config)
Write-Host "Failed-Mails: $($entries.Count)"
Write-Host ""
Write-Host " [1] Failed Queue anzeigen"
Write-Host " [2] Details einer Mail anzeigen"
Write-Host " [3] Eine Mail erneut zustellen"
Write-Host " [4] Alle Mails erneut zustellen"
Write-Host " [5] Eine Mail endgültig löschen"
Write-Host " [6] Alle Failed-Mails endgültig löschen"
Write-Host " [0] Zurück"
Write-Host ""
$choice = Read-Host "Auswahl"
switch ($choice) {
"1" {
if ($entries.Count -eq 0) {
Write-Ok "Failed Queue ist leer."
}
else {
$entries |
Select-Object QueueId,FailedSince,RetryCount,From,Recipients,LastStatusCode,SizeKB |
Format-Table -AutoSize
}
Read-Host "Enter"
}
"2" {
$id = Read-Host "Queue-ID"
$entry = $entries |
Where-Object { $_.QueueId -eq $id -or $_.FileName -eq $id -or $_.FileName -eq "$id.eml" } |
Select-Object -First 1
if (-not $entry) {
Write-Fail "Queue-ID '$id' nicht gefunden."
}
else {
Write-Host ""
Write-Host "Queue-ID: $($entry.QueueId)"
Write-Host "Datei: $($entry.FileName)"
Write-Host "Fehlgeschlagen:$($entry.FailedSince)"
Write-Host "RetryCount: $($entry.RetryCount)"
Write-Host "Von: $($entry.From)"
Write-Host "An: $($entry.Recipients)"
Write-Host "Auth-User: $($entry.AuthenticatedUser)"
Write-Host "Status: $($entry.LastStatusCode)"
Write-Host "Größe: $($entry.SizeKB) KB"
Write-Host ""
Write-Host "Letzter Fehler:" -ForegroundColor Yellow
Write-Host $entry.LastError
}
Read-Host "Enter"
}
"3" {
$id = Read-Host "Queue-ID"
$entry = $entries |
Where-Object { $_.QueueId -eq $id -or $_.FileName -eq $id -or $_.FileName -eq "$id.eml" } |
Select-Object -First 1
if (-not $entry) {
Write-Fail "Queue-ID '$id' nicht gefunden."
}
else {
Retry-FailedQueueEntry -Config $config -Entry $entry
Write-Ok "[$($entry.QueueId)] zurück nach pending verschoben."
}
Read-Host "Enter"
}
"4" {
if ($entries.Count -eq 0) {
Write-Ok "Failed Queue ist leer."
}
elseif (Confirm-Yes "Alle $($entries.Count) Failed-Mails erneut zustellen?") {
$ok = 0
$failed = 0
foreach ($entry in $entries) {
try {
Retry-FailedQueueEntry -Config $config -Entry $entry
$ok++
}
catch {
$failed++
Write-Warn "[$($entry.QueueId)] konnte nicht requeued werden: $($_.Exception.Message)"
}
}
Write-Ok "$ok Mail(s) zurück nach pending verschoben."
if ($failed -gt 0) {
Write-Warn "$failed Mail(s) konnten nicht verschoben werden."
}
}
Read-Host "Enter"
}
"5" {
$id = Read-Host "Queue-ID"
$entry = $entries |
Where-Object { $_.QueueId -eq $id -or $_.FileName -eq $id -or $_.FileName -eq "$id.eml" } |
Select-Object -First 1
if (-not $entry) {
Write-Fail "Queue-ID '$id' nicht gefunden."
}
elseif (Confirm-Yes "[$($entry.QueueId)] endgültig aus Failed löschen?") {
Remove-FailedQueueEntry -Entry $entry
Write-Ok "[$($entry.QueueId)] gelöscht."
}
Read-Host "Enter"
}
"6" {
if ($entries.Count -eq 0) {
Write-Ok "Failed Queue ist leer."
}
elseif (Confirm-Yes "WIRKLICH alle $($entries.Count) Failed-Mails endgültig löschen?") {
if (Confirm-Yes "Endgültiges Löschen nochmals bestätigen?") {
$ok = 0
foreach ($entry in $entries) {
try {
Remove-FailedQueueEntry -Entry $entry
$ok++
}
catch {
Write-Warn "[$($entry.QueueId)] konnte nicht gelöscht werden."
}
}
Write-Ok "$ok Failed-Mail(s) endgültig gelöscht."
}
}
Read-Host "Enter"
}
"0" {
return
}
default {
Write-Warn "Ungültige Auswahl."
Start-Sleep -Seconds 1
}
}
$config = Get-RelayConfig -TargetPath $InstallPath
}
}
function Manage-SmtpAuth {
$config = Get-RelayConfig -TargetPath $InstallPath
@@ -1850,6 +2198,11 @@ function Show-Status {
$authUsers = if ($config.Smtp.PSObject.Properties.Name -contains "AuthUsers") { @($config.Smtp.AuthUsers).Count } else { 0 }
Write-Host " AUTH: $authText ($authUsers Benutzer)"
}
try {
$failedCount = @(Get-FailedQueueEntries -Config $config).Count
Write-Host " Failed: $failedCount Mail(s)"
} catch {}
} catch {}
}
else {
@@ -1887,6 +2240,7 @@ function Show-Menu {
Write-Host " [7] Deinstallieren"
Write-Host " [8] Status anzeigen"
Write-Host " [9] SMTP-AUTH verwalten"
Write-Host " [10] Failed Queue verwalten"
Write-Host " [0] Beenden"
Write-Host ""
}
@@ -1908,6 +2262,7 @@ while ($true) {
"7" { Uninstall-Relay }
"8" { Show-Status }
"9" { Manage-SmtpAuth }
"10" { Manage-FailedQueue }
"0" { break }
default { Write-Warn "Ungültige Auswahl." }
}