Add live TLS certificate probing and improve restart error handling.

Configure CertificateCheckUrl per container for curl-like TLS checks, classify Sonic permission errors, and extend setup wizard for container management.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-08-03 13:16:21 +02:00
co-authored by Cursor
parent d20e0df618
commit 58b7826162
1430 changed files with 358394 additions and 71 deletions
@@ -1,4 +1,5 @@
using ZA.CoreService.ESBCertificateManager.Models;
using ZA.CoreService.ESBCertificateManager.Services;
namespace ZA.CoreService.ESBCertificateManager.Setup;
@@ -11,8 +12,10 @@ public sealed class AddContainerForm : Form
private readonly ComboBox _cmbSystem;
private readonly TextBox _txtName;
private readonly TextBox _txtDisplay;
private readonly TextBox _txtCheckUrl;
private readonly TextBox _txtTimeout;
private readonly Button _btnSave;
private readonly Button _btnProbeUrl;
private bool _running;
public int? CreatedSonicContainerId { get; private set; }
@@ -36,7 +39,7 @@ public sealed class AddContainerForm : Form
MaximizeBox = false;
MinimizeBox = false;
ShowInTaskbar = false;
ClientSize = new Size(540, 470);
ClientSize = new Size(540, 560);
BackColor = SettingsUi.Background;
ForeColor = SettingsUi.Text;
Font = new Font("Segoe UI", 10f);
@@ -55,7 +58,7 @@ public sealed class AddContainerForm : Form
Label subtitle = new()
{
Text =
"Container einer Company und einem Sonic-System zuordnen.",
"Container zuordnen und optional TLS-Prüf-URL hinterlegen.",
Location = new Point(30, 58),
AutoSize = true,
ForeColor = SettingsUi.Muted
@@ -63,13 +66,14 @@ public sealed class AddContainerForm : Form
Panel card = SettingsUi.CreateCard();
card.Location = new Point(28, 95);
card.Size = new Size(484, 290);
card.Size = new Size(484, 370);
card.Paint += SettingsUi.PaintCardBorder;
_cmbCompany = SettingsUi.CreateComboBox();
_cmbSystem = SettingsUi.CreateComboBox();
_txtName = SettingsUi.CreateTextBox();
_txtDisplay = SettingsUi.CreateTextBox();
_txtCheckUrl = SettingsUi.CreateTextBox();
_txtTimeout = SettingsUi.CreateTextBox();
_txtTimeout.Text = "180";
@@ -77,6 +81,7 @@ public sealed class AddContainerForm : Form
{
_txtName.Text = _existing.ContainerName;
_txtDisplay.Text = _existing.ContainerDisplayName ?? string.Empty;
_txtCheckUrl.Text = _existing.CertificateCheckUrl ?? string.Empty;
_txtTimeout.Text = _existing.RestartTimeoutSeconds.ToString();
}
@@ -85,15 +90,39 @@ public sealed class AddContainerForm : Form
AddField(card, "Container-Name", _txtName, 18, 138, 440);
AddField(card, "Anzeigename (optional)", _txtDisplay, 18, 198, 280);
AddField(card, "Timeout (s)", _txtTimeout, 318, 198, 140);
AddField(
card,
"Zertifikat prüfen (URL / Host:Port)",
_txtCheckUrl,
18,
258,
440);
Label urlHint = new()
{
Text = "z. B. https://server:8443 oder server:8443",
Location = new Point(18, 312),
AutoSize = true,
ForeColor = SettingsUi.Muted,
Font = new Font("Segoe UI", 8.5f)
};
_btnProbeUrl = SettingsUi.CreateGhostButton("URL prüfen");
_btnProbeUrl.Location = new Point(318, 318);
_btnProbeUrl.Size = new Size(140, 30);
_btnProbeUrl.Click += (_, _) => ProbeUrl();
card.Controls.Add(urlHint);
card.Controls.Add(_btnProbeUrl);
_btnSave = SettingsUi.CreatePrimaryButton(
IsEditMode ? "Änderungen speichern" : "Container anlegen");
_btnSave.Location = new Point(292, 405);
_btnSave.Location = new Point(292, 490);
_btnSave.Size = new Size(220, 40);
_btnSave.Click += async (_, _) => await SaveAsync();
Button cancel = SettingsUi.CreateGhostButton("Abbrechen");
cancel.Location = new Point(170, 405);
cancel.Location = new Point(170, 490);
cancel.Size = new Size(110, 40);
cancel.Click += (_, _) =>
{
@@ -110,6 +139,53 @@ public sealed class AddContainerForm : Form
Shown += async (_, _) => await LoadLookupsAsync();
}
private void ProbeUrl()
{
string url = _txtCheckUrl.Text.Trim();
if (string.IsNullOrWhiteSpace(url))
{
MessageBox.Show(
this,
"Bitte zuerst eine Prüf-URL eintragen.",
"URL prüfen",
MessageBoxButtons.OK,
MessageBoxIcon.Information);
return;
}
TlsEndpointProbeService probe = new();
CertificateProbeResult result = probe.Probe(url);
if (result.Success && result.Certificate is not null)
{
string expiry = result.Certificate.ValidUntil
.ToLocalTime()
.ToString("dd.MM.yyyy HH:mm");
MessageBox.Show(
this,
$"TLS-Zertifikat erreichbar.\n\n"
+ $"Subject: {result.Certificate.Subject}\n"
+ $"Aussteller: {result.Certificate.Issuer}\n"
+ $"Gültig bis: {expiry}\n"
+ $"Status: {(result.Certificate.IsCurrentlyValid ? "gültig" : "abgelaufen")}",
"URL prüfen",
MessageBoxButtons.OK,
result.Certificate.IsCurrentlyValid
? MessageBoxIcon.Information
: MessageBoxIcon.Warning);
return;
}
MessageBox.Show(
this,
result.Message,
"URL prüfen",
MessageBoxButtons.OK,
MessageBoxIcon.Warning);
}
private async Task LoadLookupsAsync()
{
try
@@ -211,6 +287,29 @@ public sealed class AddContainerForm : Form
return;
}
string? checkUrl = _txtCheckUrl.Text.Trim();
if (string.IsNullOrWhiteSpace(checkUrl))
{
checkUrl = null;
}
else if (!TlsEndpointProbeService.TryParseEndpoint(
checkUrl,
out _,
out _,
out _,
out string? parseError))
{
MessageBox.Show(
this,
parseError ?? "Ungültige Prüf-URL.",
"Container",
MessageBoxButtons.OK,
MessageBoxIcon.Warning);
_txtCheckUrl.Focus();
return;
}
_running = true;
_btnSave.Enabled = false;
Cursor = Cursors.WaitCursor;
@@ -225,7 +324,8 @@ public sealed class AddContainerForm : Form
system.SonicConnectionId,
_txtName.Text,
_txtDisplay.Text,
timeout);
timeout,
checkUrl);
CreatedSonicContainerId = _existing.SonicContainerId;
}
@@ -237,7 +337,8 @@ public sealed class AddContainerForm : Form
system.SonicConnectionId,
_txtName.Text,
_txtDisplay.Text,
timeout);
timeout,
checkUrl);
}
DialogResult = DialogResult.OK;