feat: Implementata eliminazione cascata credenziali con modale di conferma

Aggiunta funzionalità completa per l'eliminazione sicura delle credenziali
con rimozione automatica di tutti i dati associati.

Modifiche principali:

Backend:
- Aggiunta interfaccia ICredentialService.DeleteCredentialCascadeAsync()
- Implementato CredentialService.DeleteCredentialCascadeAsync() con gestione transazionale
- Aggiornata IDataConnectionCredentialService con metodi cascade delete
- Implementati wrapper in DataConnectionCredentialService

Eliminazione cascata gestisce:
- Execution histories delle schedulazioni
- Profile schedules associate ai profili
- Data Coupler profiles che usano le credenziali
- Key associations per credenziali REST
- Credenziale stessa

Frontend (CredentialManagement.razor):
- Aggiunto modale Bootstrap di conferma eliminazione con design danger
- Messaggio di attenzione chiaro che elenca cosa verrà eliminato
- Refactoring metodo DeleteCredential() per usare modale invece di confirm JS
- Aggiunti metodi CloseDeleteConfirmModal() e ConfirmDeleteCredential()

Sicurezza:
- Eliminazione fisica (hard delete) con transazione database
- Rollback automatico in caso di errore
- Logging dettagliato di ogni operazione
- Conferma esplicita dell'utente richiesta
This commit is contained in:
Alessio Dal Santo
2025-10-08 15:54:54 +02:00
parent d042863a56
commit 960166be9f
5 changed files with 526 additions and 22 deletions
+83 -22
View File
@@ -536,6 +536,48 @@ else
</div>
}
@* Modale di Conferma Eliminazione *@
@if (showDeleteConfirmModal)
{
<div class="modal fade show d-block" tabindex="-1" style="background-color: rgba(0,0,0,0.5);">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header bg-danger text-white">
<h5 class="modal-title">
<i class="oi oi-warning"></i> Conferma Eliminazione
</h5>
<button type="button" class="btn-close btn-close-white" @onclick="CloseDeleteConfirmModal"></button>
</div>
<div class="modal-body">
<div class="alert alert-danger" role="alert">
<h5 class="alert-heading">
<i class="oi oi-warning"></i> ATTENZIONE!
</h5>
<p class="mb-0">
All'eliminazione delle credenziali <strong>@credentialToDeleteName</strong> verranno eliminati anche:
</p>
<ul class="mt-2 mb-2">
<li>Tutti i <strong>profili</strong> associati a queste credenziali</li>
<li>Tutte le <strong>schedulazioni</strong> associate a questi profili</li>
</ul>
<p class="mb-0">
<strong>L'eliminazione è irreversibile!</strong> Procedere?
</p>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" @onclick="CloseDeleteConfirmModal">
<i class="oi oi-x"></i> Annulla
</button>
<button type="button" class="btn btn-danger" @onclick="ConfirmDeleteCredential">
<i class="oi oi-trash"></i> Elimina Definitivamente
</button>
</div>
</div>
</div>
</div>
}
@code { private List<DatabaseCredential> databaseCredentials = new();
private List<RestApiCredential> restApiCredentials = new();
@@ -547,6 +589,9 @@ else
// Modal state
private bool showDatabaseModal = false;
private bool showRestApiModal = false;
private bool showDeleteConfirmModal = false;
private string? credentialToDeleteName = null;
private bool credentialToDeleteIsDatabase = false;
private DatabaseCredential? editingDatabaseCredential = null;
private RestApiCredential? editingRestApiCredential = null;
private DatabaseCredential currentDatabaseCredential = new();
@@ -858,32 +903,48 @@ else
#region Common Methods
private async Task DeleteCredential(string name, bool isDatabase)
private void DeleteCredential(string name, bool isDatabase)
{
if (await JSRuntime.InvokeAsync<bool>("confirm", $"Sei sicuro di voler eliminare la credenziale '{name}'?"))
{
try
{
bool success;
if (isDatabase)
success = await CredentialService.DeleteDatabaseCredentialAsync(name);
else
success = await CredentialService.DeleteRestApiCredentialAsync(name);
credentialToDeleteName = name;
credentialToDeleteIsDatabase = isDatabase;
showDeleteConfirmModal = true;
}
if (success)
{
await JSRuntime.InvokeVoidAsync("alert", "Credenziale eliminata con successo!");
await RefreshCredentials();
}
else
{
await JSRuntime.InvokeVoidAsync("alert", "Errore nell'eliminazione della credenziale.");
}
}
catch (Exception ex)
private void CloseDeleteConfirmModal()
{
showDeleteConfirmModal = false;
credentialToDeleteName = null;
credentialToDeleteIsDatabase = false;
}
private async Task ConfirmDeleteCredential()
{
if (string.IsNullOrEmpty(credentialToDeleteName))
{
CloseDeleteConfirmModal();
return;
}
try
{
bool success = await CredentialService.DeleteCredentialCascadeAsync(credentialToDeleteName);
if (success)
{
await JSRuntime.InvokeVoidAsync("alert", $"Errore nell'eliminazione: {ex.Message}");
await JSRuntime.InvokeVoidAsync("alert", "Credenziale e tutti i dati associati eliminati con successo!");
CloseDeleteConfirmModal();
await RefreshCredentials();
}
else
{
await JSRuntime.InvokeVoidAsync("alert", "Errore nell'eliminazione della credenziale.");
CloseDeleteConfirmModal();
}
}
catch (Exception ex)
{
await JSRuntime.InvokeVoidAsync("alert", $"Errore nell'eliminazione: {ex.Message}");
CloseDeleteConfirmModal();
}
}