Add edit and delete for systems, containers, and paths in settings.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-31 08:12:15 +02:00
co-authored by Cursor
parent aac71ec2cd
commit 89a694ba21
6 changed files with 979 additions and 98 deletions
@@ -1299,6 +1299,434 @@ public sealed class SonicSetupRepository
return Convert.ToInt32(result);
}
public async Task UpdateSonicConnectionAsync(
int sonicConnectionId,
string connectionName,
string managementHost,
int managementPort,
string domainName,
string connectionProtocol,
CancellationToken cancellationToken = default)
{
if (sonicConnectionId <= 0)
{
throw new ArgumentOutOfRangeException(
nameof(sonicConnectionId));
}
string name = RequireTrimmed(
connectionName,
150,
"ConnectionName");
string host = RequireTrimmed(
managementHost,
255,
"ManagementHost");
string domain = RequireTrimmed(
domainName,
150,
"DomainName");
string protocol = string.IsNullOrWhiteSpace(connectionProtocol)
? "tcp"
: connectionProtocol.Trim().TrimEnd(':', '/');
if (managementPort is < 1 or > 65535)
{
throw new ArgumentOutOfRangeException(
nameof(managementPort),
"Der ManagementPort muss zwischen 1 und 65535 liegen.");
}
const string sql = """
UPDATE [dbo].[SonicConnection]
SET
[ConnectionName] = @ConnectionName,
[ManagementHost] = @ManagementHost,
[ManagementPort] = @ManagementPort,
[DomainName] = @DomainName,
[ConnectionProtocol] = @ConnectionProtocol,
[ModifiedDateTime] = SYSUTCDATETIME(),
[ModifiedBy] = SUSER_SNAME()
WHERE [SonicConnectionId] = @SonicConnectionId
AND [IsActive] = 1;
""";
await using SqlConnection connection =
new(_connectionString);
await connection.OpenAsync(cancellationToken);
await using SqlCommand command =
new(sql, connection);
command.Parameters.Add(
new SqlParameter("@SonicConnectionId", SqlDbType.Int)
{
Value = sonicConnectionId
});
command.Parameters.Add(
new SqlParameter(
"@ConnectionName",
SqlDbType.NVarChar,
150)
{
Value = name
});
command.Parameters.Add(
new SqlParameter(
"@ManagementHost",
SqlDbType.NVarChar,
255)
{
Value = host
});
command.Parameters.Add(
new SqlParameter("@ManagementPort", SqlDbType.Int)
{
Value = managementPort
});
command.Parameters.Add(
new SqlParameter(
"@DomainName",
SqlDbType.NVarChar,
150)
{
Value = domain
});
command.Parameters.Add(
new SqlParameter(
"@ConnectionProtocol",
SqlDbType.NVarChar,
20)
{
Value = protocol
});
int affected =
await command.ExecuteNonQueryAsync(cancellationToken);
if (affected == 0)
{
throw new InvalidOperationException(
$"Sonic-System Id {sonicConnectionId} "
+ "wurde nicht gefunden oder ist bereits inaktiv.");
}
}
public async Task DeactivateSonicConnectionAsync(
int sonicConnectionId,
CancellationToken cancellationToken = default)
{
if (sonicConnectionId <= 0)
{
throw new ArgumentOutOfRangeException(
nameof(sonicConnectionId));
}
const string sql = """
UPDATE [dbo].[SonicConnection]
SET
[IsActive] = 0,
[ModifiedDateTime] = SYSUTCDATETIME(),
[ModifiedBy] = SUSER_SNAME()
WHERE [SonicConnectionId] = @SonicConnectionId
AND [IsActive] = 1;
""";
await ExecuteSoftDeleteAsync(
sql,
"@SonicConnectionId",
sonicConnectionId,
$"Sonic-System Id {sonicConnectionId} "
+ "wurde nicht gefunden oder ist bereits inaktiv.",
cancellationToken);
}
public async Task UpdateSonicContainerAsync(
int sonicContainerId,
int companyId,
int sonicConnectionId,
string containerName,
string? containerDisplayName,
int restartTimeoutSeconds,
CancellationToken cancellationToken = default)
{
if (sonicContainerId <= 0)
{
throw new ArgumentOutOfRangeException(
nameof(sonicContainerId));
}
if (companyId <= 0)
{
throw new ArgumentOutOfRangeException(nameof(companyId));
}
if (sonicConnectionId <= 0)
{
throw new ArgumentOutOfRangeException(nameof(sonicConnectionId));
}
string name = RequireTrimmed(
containerName,
250,
"ContainerName");
string? display =
string.IsNullOrWhiteSpace(containerDisplayName)
? null
: containerDisplayName.Trim();
if (display is { Length: > 250 })
{
throw new ArgumentException(
"Der Anzeigename darf maximal 250 Zeichen enthalten.",
nameof(containerDisplayName));
}
if (restartTimeoutSeconds is < 10 or > 3600)
{
throw new ArgumentOutOfRangeException(
nameof(restartTimeoutSeconds),
"Timeout muss zwischen 10 und 3600 Sekunden liegen.");
}
const string sql = """
UPDATE [dbo].[SonicContainer]
SET
[CompanyId] = @CompanyId,
[SonicConnectionId] = @SonicConnectionId,
[ContainerName] = @ContainerName,
[ContainerDisplayName] = @ContainerDisplayName,
[RestartTimeoutSeconds] = @RestartTimeoutSeconds,
[ModifiedDateTime] = SYSUTCDATETIME(),
[ModifiedBy] = SUSER_SNAME()
WHERE [SonicContainerId] = @SonicContainerId
AND [IsActive] = 1;
""";
await using SqlConnection connection =
new(_connectionString);
await connection.OpenAsync(cancellationToken);
await using SqlCommand command =
new(sql, connection);
command.Parameters.Add(
new SqlParameter("@SonicContainerId", SqlDbType.Int)
{
Value = sonicContainerId
});
command.Parameters.Add(
new SqlParameter("@CompanyId", SqlDbType.Int)
{
Value = companyId
});
command.Parameters.Add(
new SqlParameter("@SonicConnectionId", SqlDbType.Int)
{
Value = sonicConnectionId
});
command.Parameters.Add(
new SqlParameter(
"@ContainerName",
SqlDbType.NVarChar,
250)
{
Value = name
});
command.Parameters.Add(
new SqlParameter(
"@ContainerDisplayName",
SqlDbType.NVarChar,
250)
{
Value = display is null
? DBNull.Value
: display
});
command.Parameters.Add(
new SqlParameter(
"@RestartTimeoutSeconds",
SqlDbType.Int)
{
Value = restartTimeoutSeconds
});
int affected =
await command.ExecuteNonQueryAsync(cancellationToken);
if (affected == 0)
{
throw new InvalidOperationException(
$"Container Id {sonicContainerId} "
+ "wurde nicht gefunden oder ist bereits inaktiv.");
}
}
public async Task DeactivateSonicContainerAsync(
int sonicContainerId,
CancellationToken cancellationToken = default)
{
if (sonicContainerId <= 0)
{
throw new ArgumentOutOfRangeException(
nameof(sonicContainerId));
}
const string sql = """
UPDATE [dbo].[SonicContainer]
SET
[IsActive] = 0,
[ModifiedDateTime] = SYSUTCDATETIME(),
[ModifiedBy] = SUSER_SNAME()
WHERE [SonicContainerId] = @SonicContainerId
AND [IsActive] = 1;
""";
await ExecuteSoftDeleteAsync(
sql,
"@SonicContainerId",
sonicContainerId,
$"Container Id {sonicContainerId} "
+ "wurde nicht gefunden oder ist bereits inaktiv.",
cancellationToken);
}
public async Task UpdateCertificateTargetAsync(
int certificateTargetId,
int sonicContainerId,
string targetDirectory,
string targetFileName,
bool backupEnabled,
string backupDirectoryName,
CancellationToken cancellationToken = default)
{
if (certificateTargetId <= 0)
{
throw new ArgumentOutOfRangeException(
nameof(certificateTargetId));
}
if (sonicContainerId <= 0)
{
throw new ArgumentOutOfRangeException(
nameof(sonicContainerId));
}
string directory = RequireTrimmed(
targetDirectory,
500,
"TargetDirectory");
string fileName = RequireTrimmed(
targetFileName,
260,
"TargetFileName");
string backupName = string.IsNullOrWhiteSpace(backupDirectoryName)
? "Backup"
: backupDirectoryName.Trim();
if (backupName.Length > 100)
{
throw new ArgumentException(
"Der Backup-Ordnername darf maximal 100 Zeichen enthalten.",
nameof(backupDirectoryName));
}
const string sql = """
UPDATE [dbo].[CertificateTarget]
SET
[SonicContainerId] = @SonicContainerId,
[TargetDirectory] = @TargetDirectory,
[TargetFileName] = @TargetFileName,
[BackupEnabled] = @BackupEnabled,
[BackupDirectoryName] = @BackupDirectoryName,
[ModifiedDateTime] = SYSUTCDATETIME(),
[ModifiedBy] = SUSER_SNAME()
WHERE [CertificateTargetId] = @CertificateTargetId
AND [IsActive] = 1;
""";
await using SqlConnection connection =
new(_connectionString);
await connection.OpenAsync(cancellationToken);
await using SqlCommand command =
new(sql, connection);
command.Parameters.Add(
new SqlParameter("@CertificateTargetId", SqlDbType.Int)
{
Value = certificateTargetId
});
command.Parameters.Add(
new SqlParameter("@SonicContainerId", SqlDbType.Int)
{
Value = sonicContainerId
});
command.Parameters.Add(
new SqlParameter(
"@TargetDirectory",
SqlDbType.NVarChar,
500)
{
Value = directory
});
command.Parameters.Add(
new SqlParameter(
"@TargetFileName",
SqlDbType.NVarChar,
260)
{
Value = fileName
});
command.Parameters.Add(
new SqlParameter("@BackupEnabled", SqlDbType.Bit)
{
Value = backupEnabled
});
command.Parameters.Add(
new SqlParameter(
"@BackupDirectoryName",
SqlDbType.NVarChar,
100)
{
Value = backupName
});
int affected =
await command.ExecuteNonQueryAsync(cancellationToken);
if (affected == 0)
{
throw new InvalidOperationException(
$"Zertifikat-Pfad Id {certificateTargetId} "
+ "wurde nicht gefunden oder ist bereits inaktiv.");
}
}
public async Task DeactivateCertificateTargetAsync(
int certificateTargetId,
CancellationToken cancellationToken = default)
@@ -1319,6 +1747,22 @@ public sealed class SonicSetupRepository
AND [IsActive] = 1;
""";
await ExecuteSoftDeleteAsync(
sql,
"@CertificateTargetId",
certificateTargetId,
$"Zertifikat-Pfad Id {certificateTargetId} "
+ "wurde nicht gefunden oder ist bereits inaktiv.",
cancellationToken);
}
private async Task ExecuteSoftDeleteAsync(
string sql,
string idParameterName,
int id,
string notFoundMessage,
CancellationToken cancellationToken)
{
await using SqlConnection connection =
new(_connectionString);
@@ -1328,11 +1772,9 @@ public sealed class SonicSetupRepository
new(sql, connection);
command.Parameters.Add(
new SqlParameter(
"@CertificateTargetId",
SqlDbType.Int)
new SqlParameter(idParameterName, SqlDbType.Int)
{
Value = certificateTargetId
Value = id
});
int affected =
@@ -1340,9 +1782,7 @@ public sealed class SonicSetupRepository
if (affected == 0)
{
throw new InvalidOperationException(
$"Zertifikat-Pfad Id {certificateTargetId} "
+ "wurde nicht gefunden oder ist bereits inaktiv.");
throw new InvalidOperationException(notFoundMessage);
}
}
@@ -5,6 +5,8 @@ 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;
@@ -15,13 +17,20 @@ public sealed class AddContainerForm : Form
public int? CreatedSonicContainerId { get; private set; }
public bool IsEditMode => _existing is not null;
public AddContainerForm(
SetupCoordinator coordinator,
int? preferredSonicConnectionId = null)
int? preferredSonicConnectionId = null,
ContainerOption? existing = null)
{
_coordinator = coordinator;
_preferredSonicConnectionId = preferredSonicConnectionId;
_existing = existing;
Text = "Container hinzufügen";
Text = IsEditMode
? "Container bearbeiten"
: "Container hinzufügen";
StartPosition = FormStartPosition.CenterParent;
FormBorderStyle = FormBorderStyle.FixedDialog;
MaximizeBox = false;
@@ -34,7 +43,9 @@ public sealed class AddContainerForm : Form
Label title = new()
{
Text = "Neuer Container",
Text = IsEditMode
? "Container bearbeiten"
: "Neuer Container",
Location = new Point(28, 22),
AutoSize = true,
Font = new Font("Segoe UI Semibold", 18f, FontStyle.Bold),
@@ -62,13 +73,21 @@ public sealed class AddContainerForm : Form
_txtTimeout = SettingsUi.CreateTextBox();
_txtTimeout.Text = "180";
if (_existing is not null)
{
_txtName.Text = _existing.ContainerName;
_txtDisplay.Text = _existing.ContainerDisplayName ?? 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);
_btnSave = SettingsUi.CreatePrimaryButton("Container anlegen");
_btnSave = SettingsUi.CreatePrimaryButton(
IsEditMode ? "Änderungen speichern" : "Container anlegen");
_btnSave.Location = new Point(292, 405);
_btnSave.Size = new Size(220, 40);
_btnSave.Click += async (_, _) => await SaveAsync();
@@ -88,11 +107,10 @@ public sealed class AddContainerForm : Form
Controls.Add(cancel);
Controls.Add(_btnSave);
Shown += async (_, _) =>
await LoadLookupsAsync(preferredSonicConnectionId);
Shown += async (_, _) => await LoadLookupsAsync();
}
private async Task LoadLookupsAsync(int? preferredSonicConnectionId)
private async Task LoadLookupsAsync()
{
try
{
@@ -103,33 +121,43 @@ public sealed class AddContainerForm : Form
await _coordinator.LoadSystemsAsync();
_cmbCompany.Items.Clear();
foreach (CompanyOption company in companies)
int companySelected = 0;
for (int index = 0; index < companies.Count; index++)
{
_cmbCompany.Items.Add(company);
_cmbCompany.Items.Add(companies[index]);
if (_existing is not null
&& companies[index].CompanyId == _existing.CompanyId)
{
companySelected = index;
}
}
if (_cmbCompany.Items.Count > 0)
{
_cmbCompany.SelectedIndex = 0;
_cmbCompany.SelectedIndex = companySelected;
}
_cmbSystem.Items.Clear();
int selected = 0;
int systemSelected = 0;
int? preferredSystemId =
_existing?.SonicConnectionId ?? _preferredSonicConnectionId;
for (int index = 0; index < systems.Count; index++)
{
_cmbSystem.Items.Add(systems[index]);
if (preferredSonicConnectionId is int id
if (preferredSystemId is int id
&& systems[index].SonicConnectionId == id)
{
selected = index;
systemSelected = index;
}
}
if (_cmbSystem.Items.Count > 0)
{
_cmbSystem.SelectedIndex = selected;
_cmbSystem.SelectedIndex = systemSelected;
}
}
catch (Exception ex)
@@ -188,6 +216,20 @@ public sealed class AddContainerForm : Form
Cursor = Cursors.WaitCursor;
try
{
if (_existing is not null)
{
await _coordinator.UpdateSonicContainerAsync(
_existing.SonicContainerId,
company.CompanyId,
system.SonicConnectionId,
_txtName.Text,
_txtDisplay.Text,
timeout);
CreatedSonicContainerId = _existing.SonicContainerId;
}
else
{
CreatedSonicContainerId =
await _coordinator.AddSonicContainerAsync(
@@ -196,6 +238,7 @@ public sealed class AddContainerForm : Form
_txtName.Text,
_txtDisplay.Text,
timeout);
}
DialogResult = DialogResult.OK;
Close();
@@ -1,8 +1,11 @@
using ZA.CoreService.ESBCertificateManager.Models;
namespace ZA.CoreService.ESBCertificateManager.Setup;
public sealed class AddSonicSystemForm : Form
{
private readonly SetupCoordinator _coordinator;
private readonly SonicSystemOption? _existing;
private readonly TextBox _txtName;
private readonly TextBox _txtHost;
private readonly TextBox _txtPort;
@@ -13,11 +16,18 @@ public sealed class AddSonicSystemForm : Form
public int? CreatedSonicConnectionId { get; private set; }
public AddSonicSystemForm(SetupCoordinator coordinator)
public bool IsEditMode => _existing is not null;
public AddSonicSystemForm(
SetupCoordinator coordinator,
SonicSystemOption? existing = null)
{
_coordinator = coordinator;
_existing = existing;
Text = "Sonic-System hinzufügen";
Text = IsEditMode
? "Sonic-System bearbeiten"
: "Sonic-System hinzufügen";
StartPosition = FormStartPosition.CenterParent;
FormBorderStyle = FormBorderStyle.FixedDialog;
MaximizeBox = false;
@@ -30,7 +40,9 @@ public sealed class AddSonicSystemForm : Form
Label title = new()
{
Text = "Neues Sonic-System",
Text = IsEditMode
? "Sonic-System bearbeiten"
: "Neues Sonic-System",
Location = new Point(28, 22),
AutoSize = true,
Font = new Font("Segoe UI Semibold", 18f, FontStyle.Bold),
@@ -61,13 +73,30 @@ public sealed class AddSonicSystemForm : Form
_cmbProtocol.SelectedIndex = 0;
_txtPort.Text = "13070";
if (_existing is not null)
{
_txtName.Text = _existing.ConnectionName;
_txtHost.Text = _existing.ManagementHost;
_txtPort.Text = _existing.ManagementPort.ToString();
_txtDomain.Text = _existing.DomainName;
string protocol = _existing.ConnectionProtocol
.Trim()
.TrimEnd(':', '/')
.ToLowerInvariant();
int protocolIndex = _cmbProtocol.Items.IndexOf(protocol);
_cmbProtocol.SelectedIndex = protocolIndex >= 0 ? protocolIndex : 0;
}
AddField(card, "Verbindungsname", _txtName, 18, 18, 420);
AddField(card, "Management-Host", _txtHost, 18, 78, 280);
AddField(card, "Port", _txtPort, 316, 78, 122);
AddField(card, "Domain", _txtDomain, 18, 138, 420);
AddField(card, "Protokoll", _cmbProtocol, 18, 198, 160);
_btnSave = SettingsUi.CreatePrimaryButton("System anlegen");
_btnSave = SettingsUi.CreatePrimaryButton(
IsEditMode ? "Änderungen speichern" : "System anlegen");
_btnSave.Location = new Point(292, 365);
_btnSave.Size = new Size(200, 40);
_btnSave.Click += async (_, _) => await SaveAsync();
@@ -113,6 +142,23 @@ public sealed class AddSonicSystemForm : Form
Cursor = Cursors.WaitCursor;
try
{
string protocol =
_cmbProtocol.SelectedItem?.ToString() ?? "tcp";
if (_existing is not null)
{
await _coordinator.UpdateSonicConnectionAsync(
_existing.SonicConnectionId,
_txtName.Text,
_txtHost.Text,
port,
_txtDomain.Text,
protocol);
CreatedSonicConnectionId = _existing.SonicConnectionId;
}
else
{
CreatedSonicConnectionId =
await _coordinator.AddSonicConnectionAsync(
@@ -120,7 +166,8 @@ public sealed class AddSonicSystemForm : Form
_txtHost.Text,
port,
_txtDomain.Text,
_cmbProtocol.SelectedItem?.ToString() ?? "tcp");
protocol);
}
DialogResult = DialogResult.OK;
Close();
@@ -7,6 +7,8 @@ public sealed class AddTargetPathForm : Form
{
private readonly SetupCoordinator _coordinator;
private readonly CertificateProbeService _probeService = new();
private readonly CertificateTargetOption? _existing;
private readonly int? _preferredSonicContainerId;
private readonly ComboBox _cmbContainer;
private readonly TextBox _txtFullPath;
@@ -21,13 +23,20 @@ public sealed class AddTargetPathForm : Form
public int? CreatedCertificateTargetId { get; private set; }
public bool IsEditMode => _existing is not null;
public AddTargetPathForm(
SetupCoordinator coordinator,
int? preferredSonicContainerId = null)
int? preferredSonicContainerId = null,
CertificateTargetOption? existing = null)
{
_coordinator = coordinator;
_preferredSonicContainerId = preferredSonicContainerId;
_existing = existing;
Text = "Zertifikat-Pfad hinzufügen";
Text = IsEditMode
? "Zertifikat-Pfad bearbeiten"
: "Zertifikat-Pfad hinzufügen";
StartPosition = FormStartPosition.CenterParent;
FormBorderStyle = FormBorderStyle.FixedDialog;
MaximizeBox = false;
@@ -40,7 +49,9 @@ public sealed class AddTargetPathForm : Form
Label title = new()
{
Text = "Zertifikat-Datei-Pfad",
Text = IsEditMode
? "Zertifikat-Pfad bearbeiten"
: "Zertifikat-Datei-Pfad",
Location = new Point(28, 18),
AutoSize = true,
Font = new Font("Segoe UI Semibold", 18f, FontStyle.Bold),
@@ -64,7 +75,8 @@ public sealed class AddTargetPathForm : Form
card.Paint += SettingsUi.PaintCardBorder;
_txtFullPath = SettingsUi.CreateTextBox();
_txtFullPath.Text = DemoCertificatePath.FullPath;
_txtFullPath.Text = _existing?.FullTargetPath
?? DemoCertificatePath.FullPath;
_txtFullPath.PlaceholderText = DemoCertificatePath.FullPath;
Button browse = SettingsUi.CreateGhostButton("…");
@@ -88,13 +100,13 @@ public sealed class AddTargetPathForm : Form
_cmbContainer = SettingsUi.CreateComboBox();
_txtBackupFolder = SettingsUi.CreateTextBox();
_txtBackupFolder.Text = "Backup";
_txtBackupFolder.Text = _existing?.BackupDirectoryName ?? "Backup";
_chkBackup = new CheckBox
{
Text = "Backup vor dem Überschreiben",
AutoSize = true,
Checked = true,
Checked = _existing?.BackupEnabled ?? true,
ForeColor = SettingsUi.Text,
Font = new Font("Segoe UI", 9.5f)
};
@@ -140,7 +152,8 @@ public sealed class AddTargetPathForm : Form
card.Controls.Add(_cmbContainer);
card.Controls.Add(hint);
_btnSave = SettingsUi.CreatePrimaryButton("Pfad anlegen");
_btnSave = SettingsUi.CreatePrimaryButton(
IsEditMode ? "Änderungen speichern" : "Pfad anlegen");
_btnSave.Location = new Point(392, 490);
_btnSave.Size = new Size(220, 40);
_btnSave.Click += async (_, _) => await SaveAsync();
@@ -160,8 +173,7 @@ public sealed class AddTargetPathForm : Form
Controls.Add(cancel);
Controls.Add(_btnSave);
Shown += async (_, _) =>
await LoadContainersAsync(preferredSonicContainerId);
Shown += async (_, _) => await LoadContainersAsync();
}
private void BrowseForFile()
@@ -222,7 +234,7 @@ public sealed class AddTargetPathForm : Form
+ $"Ablauf: {expiry} · Fingerprint: {cert.FingerprintSha256}";
}
private async Task LoadContainersAsync(int? preferredSonicContainerId)
private async Task LoadContainersAsync()
{
try
{
@@ -231,12 +243,14 @@ public sealed class AddTargetPathForm : Form
_cmbContainer.Items.Clear();
int selected = 0;
int? preferredId =
_existing?.SonicContainerId ?? _preferredSonicContainerId;
for (int index = 0; index < containers.Count; index++)
{
_cmbContainer.Items.Add(containers[index]);
if (preferredSonicContainerId is int id
if (preferredId is int id
&& containers[index].SonicContainerId == id)
{
selected = index;
@@ -328,6 +342,20 @@ public sealed class AddTargetPathForm : Form
Cursor = Cursors.WaitCursor;
try
{
if (_existing is not null)
{
await _coordinator.UpdateCertificateTargetAsync(
_existing.CertificateTargetId,
container.SonicContainerId,
directory,
fileName,
_chkBackup.Checked,
_txtBackupFolder.Text);
CreatedCertificateTargetId = _existing.CertificateTargetId;
}
else
{
CreatedCertificateTargetId =
await _coordinator.AddCertificateTargetAsync(
@@ -336,6 +364,7 @@ public sealed class AddTargetPathForm : Form
fileName,
_chkBackup.Checked,
_txtBackupFolder.Text);
}
DialogResult = DialogResult.OK;
Close();
@@ -466,6 +466,126 @@ public sealed class SetupCoordinator
return id;
}
public async Task UpdateSonicConnectionAsync(
int sonicConnectionId,
string connectionName,
string managementHost,
int managementPort,
string domainName,
string connectionProtocol,
CancellationToken cancellationToken = default)
{
await _setupRepository.UpdateSonicConnectionAsync(
sonicConnectionId,
connectionName,
managementHost,
managementPort,
domainName,
connectionProtocol,
cancellationToken);
await EnsureExistsAsync(
() => _setupRepository.ExistsSonicConnectionAsync(
sonicConnectionId,
cancellationToken),
$"Sonic-System Id {sonicConnectionId} "
+ "wurde aktualisiert, ist danach aber nicht auffindbar.");
}
public async Task DeactivateSonicConnectionAsync(
int sonicConnectionId,
CancellationToken cancellationToken = default)
{
await _setupRepository.DeactivateSonicConnectionAsync(
sonicConnectionId,
cancellationToken);
bool stillActive =
await _setupRepository.ExistsSonicConnectionAsync(
sonicConnectionId,
cancellationToken);
if (stillActive)
{
throw new InvalidOperationException(
$"Sonic-System Id {sonicConnectionId} "
+ "konnte nicht deaktiviert werden.");
}
}
public async Task UpdateSonicContainerAsync(
int sonicContainerId,
int companyId,
int sonicConnectionId,
string containerName,
string? containerDisplayName,
int restartTimeoutSeconds,
CancellationToken cancellationToken = default)
{
await _setupRepository.UpdateSonicContainerAsync(
sonicContainerId,
companyId,
sonicConnectionId,
containerName,
containerDisplayName,
restartTimeoutSeconds,
cancellationToken);
await EnsureExistsAsync(
() => _setupRepository.ExistsSonicContainerAsync(
sonicContainerId,
cancellationToken),
$"Container Id {sonicContainerId} "
+ "wurde aktualisiert, ist danach aber nicht auffindbar.");
}
public async Task DeactivateSonicContainerAsync(
int sonicContainerId,
CancellationToken cancellationToken = default)
{
await _setupRepository.DeactivateSonicContainerAsync(
sonicContainerId,
cancellationToken);
bool stillActive =
await _setupRepository.ExistsSonicContainerAsync(
sonicContainerId,
cancellationToken);
if (stillActive)
{
throw new InvalidOperationException(
$"Container Id {sonicContainerId} "
+ "konnte nicht deaktiviert werden.");
}
}
public async Task UpdateCertificateTargetAsync(
int certificateTargetId,
int sonicContainerId,
string targetDirectory,
string targetFileName,
bool backupEnabled,
string backupDirectoryName,
CancellationToken cancellationToken = default)
{
await _setupRepository.UpdateCertificateTargetAsync(
certificateTargetId,
sonicContainerId,
targetDirectory,
targetFileName,
backupEnabled,
backupDirectoryName,
cancellationToken);
await EnsureExistsAsync(
() => _setupRepository.ExistsCertificateTargetAsync(
certificateTargetId,
cancellationToken),
$"Zertifikat-Pfad Id {certificateTargetId} "
+ "wurde aktualisiert, ist danach aber nicht auffindbar.");
}
public async Task DeactivateCertificateTargetAsync(
int certificateTargetId,
CancellationToken cancellationToken = default)
@@ -368,8 +368,9 @@ public sealed class SetupWizardForm : Form
"Management-Verbindungen der aktuellen Umgebung.");
Panel toolbar = CreateToolbar(
"+ System hinzufügen",
async () => await AddSystemAsync());
("+ System hinzufügen", async () => await AddSystemAsync(), true, 210),
("Bearbeiten", async () => await EditSelectedSystemAsync(), false, 130),
("Löschen", async () => await DeleteSelectedSystemAsync(), false, 120));
_lvSystems = SettingsUi.CreateListView();
_lvSystems.Dock = DockStyle.Fill;
@@ -379,6 +380,7 @@ public sealed class SetupWizardForm : Form
_lvSystems.Columns.Add("Domain", 180);
_lvSystems.Columns.Add("Protokoll", 80);
_lvSystems.Columns.Add("URL", 180);
_lvSystems.DoubleClick += async (_, _) => await EditSelectedSystemAsync();
FinishPanelLayout(_panelSystems, _lvSystems, toolbar);
}
@@ -390,8 +392,9 @@ public sealed class SetupWizardForm : Form
"Sonic-Container je Company und System.");
Panel toolbar = CreateToolbar(
"+ Container hinzufügen",
async () => await AddContainerAsync());
("+ Container hinzufügen", async () => await AddContainerAsync(), true, 220),
("Bearbeiten", async () => await EditSelectedContainerAsync(), false, 130),
("Löschen", async () => await DeleteSelectedContainerAsync(), false, 120));
_lvContainers = SettingsUi.CreateListView();
_lvContainers.Dock = DockStyle.Fill;
@@ -400,6 +403,7 @@ public sealed class SetupWizardForm : Form
_lvContainers.Columns.Add("Anzeige", 160);
_lvContainers.Columns.Add("System", 160);
_lvContainers.Columns.Add("Timeout", 90);
_lvContainers.DoubleClick += async (_, _) => await EditSelectedContainerAsync();
FinishPanelLayout(_panelContainers, _lvContainers, toolbar);
}
@@ -411,12 +415,10 @@ public sealed class SetupWizardForm : Form
"Dateipfade (UNC/lokal) zum Austausch. Container = nur Neustart-Ziel.");
Panel toolbar = CreateToolbar(
"+ Pfad hinzufügen",
async () => await AddPathAsync(),
"Pfad prüfen",
async () => await ProbeSelectedPathAsync(),
"Pfad löschen",
async () => await DeleteSelectedPathAsync());
("+ Pfad hinzufügen", async () => await AddPathAsync(), true, 190),
("Bearbeiten", async () => await EditSelectedPathAsync(), false, 120),
("Pfad prüfen", async () => await ProbeSelectedPathAsync(), false, 130),
("Löschen", async () => await DeleteSelectedPathAsync(), false, 110));
_lvPaths = SettingsUi.CreateListView();
_lvPaths.Dock = DockStyle.Fill;
@@ -426,17 +428,13 @@ public sealed class SetupWizardForm : Form
_lvPaths.Columns.Add("Ablauf", 140);
_lvPaths.Columns.Add("Backup", 80);
_lvPaths.Columns.Add("System", 120);
_lvPaths.DoubleClick += async (_, _) => await EditSelectedPathAsync();
FinishPanelLayout(_panelPaths, _lvPaths, toolbar);
}
private Panel CreateToolbar(
string addButtonText,
Func<Task> onAdd,
string? secondaryText = null,
Func<Task>? onSecondary = null,
string? tertiaryText = null,
Func<Task>? onTertiary = null)
private static Panel CreateToolbar(
params (string Text, Func<Task> Action, bool Primary, int Width)[] buttons)
{
Panel toolbar = new()
{
@@ -446,32 +444,23 @@ public sealed class SetupWizardForm : Form
Padding = new Padding(8, 8, 8, 8)
};
Button add = SettingsUi.CreatePrimaryButton(addButtonText);
add.Dock = DockStyle.Left;
add.Width = 230;
add.Click += async (_, _) => await onAdd();
if (tertiaryText is not null && onTertiary is not null)
// Dock Left: zuletzt hinzugefügt erscheint ganz links
for (int index = buttons.Length - 1; index >= 0; index--)
{
Button tertiary = SettingsUi.CreateGhostButton(tertiaryText);
tertiary.Dock = DockStyle.Left;
tertiary.Width = 140;
tertiary.Margin = new Padding(8, 0, 0, 0);
tertiary.Click += async (_, _) => await onTertiary();
toolbar.Controls.Add(tertiary);
(string text, Func<Task> action, bool primary, int width) =
buttons[index];
Button button = primary
? SettingsUi.CreatePrimaryButton(text)
: SettingsUi.CreateGhostButton(text);
button.Dock = DockStyle.Left;
button.Width = width;
button.Margin = new Padding(8, 0, 0, 0);
button.Click += async (_, _) => await action();
toolbar.Controls.Add(button);
}
if (secondaryText is not null && onSecondary is not null)
{
Button secondary = SettingsUi.CreateGhostButton(secondaryText);
secondary.Dock = DockStyle.Left;
secondary.Width = 180;
secondary.Margin = new Padding(8, 0, 0, 0);
secondary.Click += async (_, _) => await onSecondary();
toolbar.Controls.Add(secondary);
}
toolbar.Controls.Add(add);
return toolbar;
}
@@ -810,6 +799,90 @@ public sealed class SetupWizardForm : Form
return;
}
await AfterSystemSavedAsync(createdId, created: true);
}
private async Task EditSelectedSystemAsync()
{
if (_lvSystems.SelectedItems.Count == 0
|| _lvSystems.SelectedItems[0].Tag is not SonicSystemOption system)
{
MessageBox.Show(
this,
"Bitte zuerst ein Sonic-System in der Liste wählen.",
"System bearbeiten",
MessageBoxButtons.OK,
MessageBoxIcon.Information);
return;
}
using AddSonicSystemForm dialog = new(_coordinator, system);
if (dialog.ShowDialog(this) != DialogResult.OK
|| dialog.CreatedSonicConnectionId is not int savedId)
{
return;
}
await AfterSystemSavedAsync(savedId, created: false);
}
private async Task DeleteSelectedSystemAsync()
{
if (_lvSystems.SelectedItems.Count == 0
|| _lvSystems.SelectedItems[0].Tag is not SonicSystemOption system)
{
MessageBox.Show(
this,
"Bitte zuerst ein Sonic-System in der Liste wählen.",
"System löschen",
MessageBoxButtons.OK,
MessageBoxIcon.Information);
return;
}
DialogResult confirm = MessageBox.Show(
this,
"Sonic-System wirklich aus der Konfiguration entfernen?\n\n"
+ $"{system.ConnectionName}\n"
+ $"{system.ConnectionUrl}\n\n"
+ "Zugehörige Container und Pfade verschwinden aus den Listen. "
+ "Der Eintrag wird in SQL nur deaktiviert (IsActive=0).",
"System löschen",
MessageBoxButtons.YesNo,
MessageBoxIcon.Warning);
if (confirm != DialogResult.Yes)
{
return;
}
try
{
await _coordinator.DeactivateSonicConnectionAsync(
system.SonicConnectionId);
await LoadSystemsListAsync();
await LoadContainersListAsync();
await LoadPathsListAsync();
await LoadSystemsAsync();
await RefreshAllAsync();
MessageBox.Show(
this,
$"System Id {system.SonicConnectionId} wurde deaktiviert.",
"System gelöscht",
MessageBoxButtons.OK,
MessageBoxIcon.Information);
}
catch (Exception ex)
{
ShowError(ex.Message);
}
}
private async Task AfterSystemSavedAsync(int systemId, bool created)
{
await LoadSystemsListAsync();
await LoadSystemsAsync();
await RefreshAllAsync();
@@ -818,13 +891,13 @@ public sealed class SetupWizardForm : Form
.Cast<ListViewItem>()
.Any(item =>
item.Tag is SonicSystemOption system
&& system.SonicConnectionId == createdId);
&& system.SonicConnectionId == systemId);
if (!visible)
{
ShowError(
$"Anlage-Prüfung fehlgeschlagen: System-Id {createdId} " +
"ist nach dem Speichern nicht in der Liste.");
$"{(created ? "Anlage" : "Update")}-Prüfung fehlgeschlagen: "
+ $"System-Id {systemId} ist nach dem Speichern nicht in der Liste.");
return;
}
@@ -832,11 +905,13 @@ public sealed class SetupWizardForm : Form
_lvSystems,
item =>
item.Tag is SonicSystemOption system
&& system.SonicConnectionId == createdId);
&& system.SonicConnectionId == systemId);
MessageBox.Show(
this,
$"System wurde gespeichert und geprüft (Id {createdId}).",
created
? $"System wurde gespeichert und geprüft (Id {systemId})."
: $"System wurde aktualisiert und geprüft (Id {systemId}).",
"Prüfung OK",
MessageBoxButtons.OK,
MessageBoxIcon.Information);
@@ -860,6 +935,96 @@ public sealed class SetupWizardForm : Form
return;
}
await AfterContainerSavedAsync(createdId, created: true);
}
private async Task EditSelectedContainerAsync()
{
if (_lvContainers.SelectedItems.Count == 0
|| _lvContainers.SelectedItems[0].Tag is not ContainerOption container)
{
MessageBox.Show(
this,
"Bitte zuerst einen Container in der Liste wählen.",
"Container bearbeiten",
MessageBoxButtons.OK,
MessageBoxIcon.Information);
return;
}
using AddContainerForm dialog = new(
_coordinator,
container.SonicConnectionId,
container);
if (dialog.ShowDialog(this) != DialogResult.OK
|| dialog.CreatedSonicContainerId is not int savedId)
{
return;
}
await AfterContainerSavedAsync(savedId, created: false);
}
private async Task DeleteSelectedContainerAsync()
{
if (_lvContainers.SelectedItems.Count == 0
|| _lvContainers.SelectedItems[0].Tag is not ContainerOption container)
{
MessageBox.Show(
this,
"Bitte zuerst einen Container in der Liste wählen.",
"Container löschen",
MessageBoxButtons.OK,
MessageBoxIcon.Information);
return;
}
string display =
string.IsNullOrWhiteSpace(container.ContainerDisplayName)
? container.ContainerName
: container.ContainerDisplayName;
DialogResult confirm = MessageBox.Show(
this,
"Container wirklich aus der Konfiguration entfernen?\n\n"
+ $"{container.CompanyCode} / {display}\n"
+ $"System: {container.ConnectionName}\n\n"
+ "Zugehörige Zertifikat-Pfade verschwinden aus der Liste. "
+ "Der Eintrag wird in SQL nur deaktiviert (IsActive=0).",
"Container löschen",
MessageBoxButtons.YesNo,
MessageBoxIcon.Warning);
if (confirm != DialogResult.Yes)
{
return;
}
try
{
await _coordinator.DeactivateSonicContainerAsync(
container.SonicContainerId);
await LoadContainersListAsync();
await LoadPathsListAsync();
await RefreshAllAsync();
MessageBox.Show(
this,
$"Container Id {container.SonicContainerId} wurde deaktiviert.",
"Container gelöscht",
MessageBoxButtons.OK,
MessageBoxIcon.Information);
}
catch (Exception ex)
{
ShowError(ex.Message);
}
}
private async Task AfterContainerSavedAsync(int containerId, bool created)
{
await LoadContainersListAsync();
await LoadSystemsListAsync();
await RefreshAllAsync();
@@ -868,13 +1033,13 @@ public sealed class SetupWizardForm : Form
.Cast<ListViewItem>()
.Any(item =>
item.Tag is ContainerOption container
&& container.SonicContainerId == createdId);
&& container.SonicContainerId == containerId);
if (!visible)
{
ShowError(
$"Anlage-Prüfung fehlgeschlagen: Container-Id {createdId} " +
"ist nach dem Speichern nicht in der Liste.");
$"{(created ? "Anlage" : "Update")}-Prüfung fehlgeschlagen: "
+ $"Container-Id {containerId} ist nach dem Speichern nicht in der Liste.");
return;
}
@@ -882,11 +1047,13 @@ public sealed class SetupWizardForm : Form
_lvContainers,
item =>
item.Tag is ContainerOption container
&& container.SonicContainerId == createdId);
&& container.SonicContainerId == containerId);
MessageBox.Show(
this,
$"Container wurde gespeichert und geprüft (Id {createdId}).",
created
? $"Container wurde gespeichert und geprüft (Id {containerId})."
: $"Container wurde aktualisiert und geprüft (Id {containerId}).",
"Prüfung OK",
MessageBoxButtons.OK,
MessageBoxIcon.Information);
@@ -910,6 +1077,39 @@ public sealed class SetupWizardForm : Form
return;
}
await AfterPathSavedAsync(createdId, created: true);
}
private async Task EditSelectedPathAsync()
{
if (_lvPaths.SelectedItems.Count == 0
|| _lvPaths.SelectedItems[0].Tag is not CertificateTargetOption target)
{
MessageBox.Show(
this,
"Bitte zuerst einen Zertifikat-Pfad in der Liste wählen.",
"Pfad bearbeiten",
MessageBoxButtons.OK,
MessageBoxIcon.Information);
return;
}
using AddTargetPathForm dialog = new(
_coordinator,
target.SonicContainerId,
target);
if (dialog.ShowDialog(this) != DialogResult.OK
|| dialog.CreatedCertificateTargetId is not int savedId)
{
return;
}
await AfterPathSavedAsync(savedId, created: false);
}
private async Task AfterPathSavedAsync(int pathId, bool created)
{
await LoadPathsListAsync();
await RefreshAllAsync();
@@ -917,13 +1117,13 @@ public sealed class SetupWizardForm : Form
.Cast<ListViewItem>()
.Any(item =>
item.Tag is CertificateTargetOption target
&& target.CertificateTargetId == createdId);
&& target.CertificateTargetId == pathId);
if (!visible)
{
ShowError(
$"Anlage-Prüfung fehlgeschlagen: Pfad-Id {createdId} " +
"ist nach dem Speichern nicht in der Liste.");
$"{(created ? "Anlage" : "Update")}-Prüfung fehlgeschlagen: "
+ $"Pfad-Id {pathId} ist nach dem Speichern nicht in der Liste.");
return;
}
@@ -931,11 +1131,13 @@ public sealed class SetupWizardForm : Form
_lvPaths,
item =>
item.Tag is CertificateTargetOption target
&& target.CertificateTargetId == createdId);
&& target.CertificateTargetId == pathId);
MessageBox.Show(
this,
$"Zertifikat-Pfad wurde gespeichert und geprüft (Id {createdId}).",
created
? $"Zertifikat-Pfad wurde gespeichert und geprüft (Id {pathId})."
: $"Zertifikat-Pfad wurde aktualisiert und geprüft (Id {pathId}).",
"Prüfung OK",
MessageBoxButtons.OK,
MessageBoxIcon.Information);