using ZA.CoreService.ESBCertificateManager.Models; using ZA.CoreService.ESBCertificateManager.Services; namespace ZA.CoreService.ESBCertificateManager.Setup; public sealed class AddContainerForm : Form { private readonly SetupCoordinator _coordinator; private readonly ContainerOption? _existing; private readonly int? _preferredSonicConnectionId; private readonly ComboBox _cmbCompany; 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; } public bool IsEditMode => _existing is not null; public AddContainerForm( SetupCoordinator coordinator, int? preferredSonicConnectionId = null, ContainerOption? existing = null) { _coordinator = coordinator; _preferredSonicConnectionId = preferredSonicConnectionId; _existing = existing; Text = IsEditMode ? "Container bearbeiten" : "Container hinzufügen"; StartPosition = FormStartPosition.CenterParent; FormBorderStyle = FormBorderStyle.FixedDialog; MaximizeBox = false; MinimizeBox = false; ShowInTaskbar = false; ClientSize = new Size(540, 560); BackColor = SettingsUi.Background; ForeColor = SettingsUi.Text; Font = new Font("Segoe UI", 10f); Label title = new() { Text = IsEditMode ? "Container bearbeiten" : "Neuer Container", Location = new Point(28, 22), AutoSize = true, Font = new Font("Segoe UI Semibold", 18f, FontStyle.Bold), ForeColor = SettingsUi.Text }; Label subtitle = new() { Text = "Container zuordnen und optional TLS-Prüf-URL hinterlegen.", Location = new Point(30, 58), AutoSize = true, ForeColor = SettingsUi.Muted }; Panel card = SettingsUi.CreateCard(); card.Location = new Point(28, 95); 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"; if (_existing is not null) { _txtName.Text = _existing.ContainerName; _txtDisplay.Text = _existing.ContainerDisplayName ?? string.Empty; _txtCheckUrl.Text = _existing.CertificateCheckUrl ?? string.Empty; _txtTimeout.Text = _existing.RestartTimeoutSeconds.ToString(); } AddField(card, "Company", _cmbCompany, 18, 18, 440); AddField(card, "Sonic-System", _cmbSystem, 18, 78, 440); 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, 490); _btnSave.Size = new Size(220, 40); _btnSave.Click += async (_, _) => await SaveAsync(); Button cancel = SettingsUi.CreateGhostButton("Abbrechen"); cancel.Location = new Point(170, 490); cancel.Size = new Size(110, 40); cancel.Click += (_, _) => { DialogResult = DialogResult.Cancel; Close(); }; Controls.Add(title); Controls.Add(subtitle); Controls.Add(card); Controls.Add(cancel); Controls.Add(_btnSave); 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 { IReadOnlyList companies = await _coordinator.LoadCompaniesAsync(); IReadOnlyList systems = await _coordinator.LoadSystemsAsync(); _cmbCompany.Items.Clear(); int companySelected = 0; for (int index = 0; index < companies.Count; index++) { _cmbCompany.Items.Add(companies[index]); if (_existing is not null && companies[index].CompanyId == _existing.CompanyId) { companySelected = index; } } if (_cmbCompany.Items.Count > 0) { _cmbCompany.SelectedIndex = companySelected; } _cmbSystem.Items.Clear(); int systemSelected = 0; int? preferredSystemId = _existing?.SonicConnectionId ?? _preferredSonicConnectionId; for (int index = 0; index < systems.Count; index++) { _cmbSystem.Items.Add(systems[index]); if (preferredSystemId is int id && systems[index].SonicConnectionId == id) { systemSelected = index; } } if (_cmbSystem.Items.Count > 0) { _cmbSystem.SelectedIndex = systemSelected; } } catch (Exception ex) { MessageBox.Show( this, ex.Message, "Container", MessageBoxButtons.OK, MessageBoxIcon.Error); } } private async Task SaveAsync() { if (_running) { return; } if (_cmbCompany.SelectedItem is not CompanyOption company) { MessageBox.Show( this, "Bitte Company wählen.", "Container", MessageBoxButtons.OK, MessageBoxIcon.Warning); return; } if (_cmbSystem.SelectedItem is not SonicSystemOption system) { MessageBox.Show( this, "Bitte System wählen.", "Container", MessageBoxButtons.OK, MessageBoxIcon.Warning); return; } if (!int.TryParse(_txtTimeout.Text.Trim(), out int timeout)) { MessageBox.Show( this, "Ungültiger Timeout.", "Container", MessageBoxButtons.OK, MessageBoxIcon.Warning); 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; try { if (_existing is not null) { await _coordinator.UpdateSonicContainerAsync( _existing.SonicContainerId, company.CompanyId, system.SonicConnectionId, _txtName.Text, _txtDisplay.Text, timeout, checkUrl); CreatedSonicContainerId = _existing.SonicContainerId; } else { CreatedSonicContainerId = await _coordinator.AddSonicContainerAsync( company.CompanyId, system.SonicConnectionId, _txtName.Text, _txtDisplay.Text, timeout, checkUrl); } DialogResult = DialogResult.OK; Close(); } catch (Exception ex) { MessageBox.Show( this, ex.Message, "Container", MessageBoxButtons.OK, MessageBoxIcon.Error); } finally { _running = false; _btnSave.Enabled = true; Cursor = Cursors.Default; } } private static void AddField( Control parent, string label, Control input, int x, int y, int width) { Label fieldLabel = SettingsUi.CreateFieldLabel(label); fieldLabel.Location = new Point(x, y); input.Location = new Point(x, y + 22); input.Width = width; parent.Controls.Add(fieldLabel); parent.Controls.Add(input); } }