Initial: Holzleitner Mail-Client (Rust, Windows-Service)

Polling-Client, der beim Backend die noch nicht versendeten ausgelieferten
Belege abfragt (GET /admin/delivered-belegnummern), ERPframe per CLI zum
Mailversand anstößt (_SV_MAIL_VERSAND) und die Belege anschließend als
versendet markiert (POST /admin/mark-mail-sent). Authentifizierung gegen
das Backend per X-Admin-Api-Key.

Enthält: Config-Laden (config.json, Vorlage config.example.json), Logging,
Windows-Service-Wrapper (install-/uninstall-service.ps1).

Nicht im Repo (.gitignore): config.json (Secrets: Admin-Key + ERPframe-
Passwort), target/, logs/. config.example.json trägt nur Platzhalter.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Dennis Nemec
2026-06-01 18:02:07 +02:00
commit 3774a378f3
11 changed files with 2399 additions and 0 deletions

80
install-service.ps1 Normal file
View File

@ -0,0 +1,80 @@
#Requires -RunAsAdministrator
<#
.SYNOPSIS
Registriert den Holzleitner-Mailclient als Windows-Dienst "Holzleitner App Mails".
.DESCRIPTION
Muss als Administrator ausgeführt werden. Erwartet die kompilierte EXE
(Default: neben diesem Skript) und eine config.json IM SELBEN Verzeichnis
wie die EXE (der Dienst startet mit Arbeitsverzeichnis C:\Windows\System32,
daher werden config.json/logs relativ zur EXE aufgelöst).
.PARAMETER ExePath
Pfad zu holzleitner-mailclient.exe. Default: neben diesem Skript.
.PARAMETER Credential
Optionales Dienstkonto (z. B. der ERPframe-/Domänen-Benutzer). Ohne Angabe
läuft der Dienst als LocalSystem. Für ERPframe ist oft ein echtes
Benutzerkonto nötig (DB-/Netzwerkzugriff). Das Konto braucht das Recht
"Als Dienst anmelden".
.EXAMPLE
.\install-service.ps1
.EXAMPLE
.\install-service.ps1 -ExePath "C:\HolzleitnerMail\holzleitner-mailclient.exe" -Credential (Get-Credential)
#>
[CmdletBinding()]
param(
[string]$ExePath,
[string]$ServiceName = "HolzleitnerAppMails",
[string]$DisplayName = "Holzleitner App Mails",
[pscredential]$Credential
)
$ErrorActionPreference = 'Stop'
$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Definition
if (-not $ExePath) { $ExePath = Join-Path $ScriptDir 'holzleitner-mailclient.exe' }
if (-not (Test-Path $ExePath)) { throw "EXE nicht gefunden: $ExePath" }
$ExePath = (Resolve-Path $ExePath).Path
$ExeDir = Split-Path $ExePath -Parent
if (-not (Test-Path (Join-Path $ExeDir 'config.json'))) {
Write-Warning "config.json fehlt neben der EXE ($ExeDir) - der Dienst startet sonst nicht korrekt."
}
# Vorhandenen Dienst stoppen & entfernen (Neuinstallation)
$existing = Get-Service -Name $ServiceName -ErrorAction SilentlyContinue
if ($existing) {
Write-Host "Dienst '$ServiceName' existiert bereits - stoppe & entferne..."
if ($existing.Status -ne 'Stopped') { Stop-Service -Name $ServiceName -Force }
& sc.exe delete $ServiceName | Out-Null
Start-Sleep -Seconds 2
}
# binPath in doppelten Quotes (Pfad kann Leerzeichen enthalten)
$bin = "`"$ExePath`""
$params = @{
Name = $ServiceName
DisplayName = $DisplayName
BinaryPathName = $bin
StartupType = 'Automatic'
Description = 'Pollt offene Belegnummern vom Holzleitner-Backend und stoesst ERPframe zum Mailversand an (alle 5 Min).'
}
if ($Credential) { $params['Credential'] = $Credential }
Write-Host "Registriere Dienst '$DisplayName' ($ServiceName) -> $ExePath"
New-Service @params | Out-Null
# Auto-Restart bei Absturz: 3x mit 60s Abstand, Fehlerzaehler nach 1 Tag zuruecksetzen
& sc.exe failure $ServiceName reset= 86400 actions= restart/60000/restart/60000/restart/60000 | Out-Null
# Verzoegerter Autostart (startet nach den System-Diensten)
& sc.exe config $ServiceName start= delayed-auto | Out-Null
Write-Host "Starte Dienst..."
Start-Service -Name $ServiceName
Start-Sleep -Seconds 1
Get-Service -Name $ServiceName | Format-List Name, DisplayName, Status, StartType
Write-Host "Logs: $(Join-Path $ExeDir 'logs')"
Write-Host "Fertig."