Allow soft-deleting certificate paths from settings.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-29 09:55:29 +02:00
co-authored by Cursor
parent b1e1789fd1
commit aac71ec2cd
3 changed files with 134 additions and 2 deletions
@@ -1299,6 +1299,53 @@ public sealed class SonicSetupRepository
return Convert.ToInt32(result);
}
public async Task DeactivateCertificateTargetAsync(
int certificateTargetId,
CancellationToken cancellationToken = default)
{
if (certificateTargetId <= 0)
{
throw new ArgumentOutOfRangeException(
nameof(certificateTargetId));
}
const string sql = """
UPDATE [dbo].[CertificateTarget]
SET
[IsActive] = 0,
[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
});
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<bool> ExistsSonicConnectionAsync(
int sonicConnectionId,
CancellationToken cancellationToken = default)
@@ -466,6 +466,27 @@ public sealed class SetupCoordinator
return id;
}
public async Task DeactivateCertificateTargetAsync(
int certificateTargetId,
CancellationToken cancellationToken = default)
{
await _setupRepository.DeactivateCertificateTargetAsync(
certificateTargetId,
cancellationToken);
bool stillActive =
await _setupRepository.ExistsCertificateTargetAsync(
certificateTargetId,
cancellationToken);
if (stillActive)
{
throw new InvalidOperationException(
$"Zertifikat-Pfad Id {certificateTargetId} "
+ "konnte nicht deaktiviert werden.");
}
}
private static async Task EnsureExistsAsync(
Func<Task<bool>> existsCheck,
string errorMessage)
@@ -414,7 +414,9 @@ public sealed class SetupWizardForm : Form
"+ Pfad hinzufügen",
async () => await AddPathAsync(),
"Pfad prüfen",
async () => await ProbeSelectedPathAsync());
async () => await ProbeSelectedPathAsync(),
"Pfad löschen",
async () => await DeleteSelectedPathAsync());
_lvPaths = SettingsUi.CreateListView();
_lvPaths.Dock = DockStyle.Fill;
@@ -432,7 +434,9 @@ public sealed class SetupWizardForm : Form
string addButtonText,
Func<Task> onAdd,
string? secondaryText = null,
Func<Task>? onSecondary = null)
Func<Task>? onSecondary = null,
string? tertiaryText = null,
Func<Task>? onTertiary = null)
{
Panel toolbar = new()
{
@@ -447,6 +451,16 @@ public sealed class SetupWizardForm : Form
add.Width = 230;
add.Click += async (_, _) => await onAdd();
if (tertiaryText is not null && onTertiary is not null)
{
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);
}
if (secondaryText is not null && onSecondary is not null)
{
Button secondary = SettingsUi.CreateGhostButton(secondaryText);
@@ -927,6 +941,56 @@ public sealed class SetupWizardForm : Form
MessageBoxIcon.Information);
}
private async Task DeleteSelectedPathAsync()
{
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 löschen",
MessageBoxButtons.OK,
MessageBoxIcon.Information);
return;
}
DialogResult confirm = MessageBox.Show(
this,
"Zertifikat-Pfad wirklich aus der Konfiguration entfernen?\n\n"
+ $"{target.FullTargetPath}\n\n"
+ "Die Datei auf dem Server bleibt unverändert. "
+ "Der Eintrag wird in SQL nur deaktiviert (IsActive=0).",
"Pfad löschen",
MessageBoxButtons.YesNo,
MessageBoxIcon.Warning);
if (confirm != DialogResult.Yes)
{
return;
}
try
{
await _coordinator.DeactivateCertificateTargetAsync(
target.CertificateTargetId);
await LoadPathsListAsync();
await RefreshAllAsync();
MessageBox.Show(
this,
$"Pfad Id {target.CertificateTargetId} wurde deaktiviert.",
"Pfad gelöscht",
MessageBoxButtons.OK,
MessageBoxIcon.Information);
}
catch (Exception ex)
{
ShowError(ex.Message);
}
}
private static void SelectListItem(
ListView listView,
Func<ListViewItem, bool> match)