Fix app startup and enable Sonic ESB restart via remote CMD.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-24 08:17:35 +02:00
co-authored by Cursor
parent ed8e2822d1
commit c2268f1908
9 changed files with 446 additions and 207 deletions
@@ -15,93 +15,148 @@ public sealed class SonicConnection
/// <summary>
/// Sonic-Broker-/Management-URL im Sonic-Format, z.B. "tcp://dekun-painwbdet:13070".
/// Der Hostname wird daraus extrahiert (für HTTP-Modus: Basis-URL, für WinRM-Modus: Zielrechner).
/// Der Hostname wird daraus extrahiert (WinRM-Ziel / HTTP-Basis).
/// </summary>
public required string ConnectionUrl { get; init; }
/// <summary>Benutzername für die Management-Konsole.</summary>
/// <summary>Benutzername für WinRM / Management-Konsole.</summary>
public required string Username { get; init; }
/// <summary>Passwort für die Management-Konsole.</summary>
/// <summary>Passwort für WinRM / Management-Konsole.</summary>
public required string Password { get; init; }
/// <summary>
/// Management-Modus: HttpApi (REST) oder WinRm (PowerShell Remoting).
/// Standard: WinRm da Sonic 10.x kein HTTP REST API bereitstellt.
/// Management-Modus:
/// - WinRm: PowerShell Remoting auf dem Sonic-Server (Standard)
/// - LocalCmd: CMD/PowerShell lokal (zum Testen direkt auf dem Sonic-PC)
/// - HttpApi: REST, falls vorhanden
/// </summary>
public SonicManagementMode ManagementMode { get; init; } = SonicManagementMode.WinRm;
/// <summary>
/// Installationsroot von Sonic MQ / Management Console, z.B. "C:\Sonic\MQ10.0".
/// Wird für Default-Scripts (stopcontainer/startcontainer) benötigt.
/// </summary>
public string SonicHome { get; init; } = @"C:\Sonic\MQ10.0";
// ---------------------------------------------------------------
// HTTP REST API (ManagementMode = HttpApi)
// ---------------------------------------------------------------
/// <summary>HTTP-Port der Sonic Management Console REST-API (Standard: 8080).</summary>
public int ManagementHttpPort { get; init; } = 8080;
/// <summary>Präfix für alle REST-API-Pfade (Standard: "/api/v1").</summary>
public string ApiBasePath { get; init; } = "/api/v1";
/// <summary>Konfigurierter Pfad zum Auflisten aller Container. Leer = automatische Erkennung.</summary>
public string ContainerListPath { get; init; } = string.Empty;
/// <summary>Konfigurierter Pfad zum Neustarten. Platzhalter: {domain}, {container}.</summary>
public string ContainerRestartPath { get; init; } = string.Empty;
/// <summary>Konfigurierter Pfad zum Stoppen. Platzhalter: {domain}, {container}.</summary>
public string ContainerStopPath { get; init; } = string.Empty;
/// <summary>Konfigurierter Pfad zum Starten. Platzhalter: {domain}, {container}.</summary>
public string ContainerStartPath { get; init; } = string.Empty;
// ---------------------------------------------------------------
// WinRM / PowerShell Remoting (ManagementMode = WinRm)
// WinRM / LocalCmd
// ---------------------------------------------------------------
/// <summary>
/// WinRM-Port auf dem Zielrechner (Standard: 5985 = HTTP, 5986 = HTTPS).
/// </summary>
public int WinRmPort { get; init; } = 5985;
/// <summary>
/// PowerShell-Scriptblock zum Neustarten eines Containers.
/// Platzhalter: {container} = Container-Name (nicht enkodiert), {domain} = Domain-Name.
/// Beispiel für Windows-Service: "Restart-Service -Name 'CT-ZADBService' -Force"
/// Beispiel für Sonic-Skript: "& 'C:\\Sonic\\bin\\stopContainer.bat' '{container}'; Start-Sleep 5; & 'C:\\Sonic\\bin\\startContainer.bat' '{container}'"
/// PowerShell-Script zum Neustarten. Leer = Default über Sonic bin\stop/startcontainer.bat.
/// Platzhalter: {container}, {domain}, {sonicHome}
/// </summary>
public string WinRmRestartScript { get; init; } = string.Empty;
/// <summary>
/// PowerShell-Scriptblock zum Auflisten aller Container der Domain.
/// Ausgabe: eine Zeile pro Container-Name.
/// Beispiel: "Get-Service -DisplayName 'Sonic*' | Select-Object -ExpandProperty Name"
/// PowerShell-Script zum Auflisten der Container. Leer = Default (Services + SonicHome).
/// </summary>
public string WinRmContainerListScript { get; init; } = string.Empty;
/// <summary>
/// PowerShell-Scriptblock für XApi-Import.
/// Platzhalter: {container}, {xapiPath}.
/// </summary>
public string WinRmXapiImportScript { get; init; } = string.Empty;
// ---------------------------------------------------------------
// Gemeinsame Einstellungen
// ---------------------------------------------------------------
/// <summary>Timeout in Sekunden für einzelne API-/Script-Aufrufe (Standard: 60).</summary>
public int TimeoutSeconds { get; init; } = 60;
/// <summary>Wartezeit in Sekunden nach einem Container-Neustart (Standard: 15).</summary>
public int PostRestartDelaySeconds { get; init; } = 15;
/// <summary>Effektives Restart-Script inkl. Default, wenn leer.</summary>
public string ResolveRestartScript()
=> string.IsNullOrWhiteSpace(WinRmRestartScript)
? DefaultRestartScript
: WinRmRestartScript;
/// <summary>Effektives List-Script inkl. Default, wenn leer.</summary>
public string ResolveContainerListScript()
=> string.IsNullOrWhiteSpace(WinRmContainerListScript)
? DefaultContainerListScript
: WinRmContainerListScript;
/// <summary>
/// Default: Sonic Management Console Tools per CMD auf dem Ziel-PC.
/// stopcontainer.bat / startcontainer.bat unter SonicHome\bin.
/// </summary>
public const string DefaultRestartScript =
"""
$ErrorActionPreference = 'Stop'
$sonicHome = '{sonicHome}'
$domain = '{domain}'
$container = '{container}'
$bin = Join-Path $sonicHome 'bin'
$stop = Join-Path $bin 'stopcontainer.bat'
$start = Join-Path $bin 'startcontainer.bat'
$fullName = if ($container -like '*.*') { $container } else { "$domain.$container" }
if (-not (Test-Path -LiteralPath $stop)) {
throw "Sonic stopcontainer.bat nicht gefunden: $stop (SonicHome prüfen)"
}
if (-not (Test-Path -LiteralPath $start)) {
throw "Sonic startcontainer.bat nicht gefunden: $start (SonicHome prüfen)"
}
Write-Output "STOP $fullName via $stop"
$stopProc = Start-Process -FilePath 'cmd.exe' -ArgumentList @('/c', "`"$stop`" `"$fullName`"") -Wait -PassThru -NoNewWindow
if ($stopProc.ExitCode -ne 0) {
Write-Warning "stopcontainer ExitCode=$($stopProc.ExitCode) starte trotzdem neu"
}
Start-Sleep -Seconds 5
Write-Output "START $fullName via $start"
$startProc = Start-Process -FilePath 'cmd.exe' -ArgumentList @('/c', "`"$start`" `"$fullName`"") -Wait -PassThru -NoNewWindow
if ($startProc.ExitCode -ne 0) {
throw "startcontainer fehlgeschlagen, ExitCode=$($startProc.ExitCode)"
}
Write-Output "Neustart ok: $fullName"
""";
public const string DefaultContainerListScript =
"""
$ErrorActionPreference = 'Continue'
$sonicHome = '{sonicHome}'
$names = @()
# Windows-Dienste mit Sonic/ESB im Namen
$names += @(Get-Service -ErrorAction SilentlyContinue |
Where-Object { $_.DisplayName -match 'Sonic|ESB|MQ' -or $_.Name -match 'Sonic|ESB|MQ' } |
Select-Object -ExpandProperty Name)
# Container-Cache-Ordner unter SonicHome (Domain.Container.cache)
if (Test-Path -LiteralPath $sonicHome) {
$names += @(Get-ChildItem -LiteralPath $sonicHome -Directory -ErrorAction SilentlyContinue |
Where-Object { $_.Name -like '*.cache' } |
ForEach-Object { $_.Name -replace '\.cache$', '' })
}
$names | Where-Object { $_ } | Sort-Object -Unique
""";
}
public enum SonicManagementMode
{
/// <summary>HTTP REST API (wenn vom Sonic-Server bereitgestellt).</summary>
HttpApi,
HttpApi = 0,
/// <summary>
/// PowerShell Remoting (WinRM) Standard für Sonic 10.x auf Windows.
/// Führt konfigurierte Scriptblöcke via Invoke-Command auf dem Sonic-Server aus.
/// PowerShell Remoting (WinRM) führt Scripts auf dem Sonic-Server aus.
/// </summary>
WinRm
WinRm = 1,
/// <summary>
/// CMD/PowerShell lokal auf diesem PC zum Testen direkt auf dem Sonic-Rechner.
/// </summary>
LocalCmd = 2
}