feat: Integrazione completa gestione credenziali per database e REST API con supporto SAP B1 e Salesforce
NUOVE FUNZIONALITÀ: - Aggiunto modulo CredentialManager per gestione centralizzata credenziali - Implementata UI Blazor per gestione credenziali (CredentialManagement.razor) - Supporto completo per credenziali database (SQL Server, MySQL, PostgreSQL, Oracle, SQLite, DB2, SAP HANA) - Gestione unificata REST API con supporto specifico per SAP B1 Service Layer e Salesforce - Test reali di connessione per database, SAP B1 e Salesforce OAuth2 - Selezione dinamica tipo servizio REST (Generico, SAP B1, Salesforce) con campi specifici - Persistenza sicura di credenziali con crittografia password e campi sensibili COMPONENTI AGGIUNTI: - CredentialManager/Models/: CredentialEntity, CredentialModels (DatabaseCredential, RestApiCredential, SapB1ServiceLayerCredential, SalesforceCredential) - CredentialManager/Services/: CredentialService, EncryptionService, DatabaseInitializer - CredentialManager/Data/: CredentialDbContext con Entity Framework - DataConnection/CredentialManagement/: Interfacce e servizi di integrazione - Data_Coupler/Pages/CredentialManagement.razor: UI completa per gestione credenziali MIGLIORAMENTI UI: - Form dinamica per REST API con campi specifici per tipo servizio - Validazione campi obbligatori per Salesforce (ClientId, ClientSecret, SecurityToken) - Test connessione in tempo reale dalla modale di inserimento/modifica - Rimozione sezioni separate per SAP B1 e Salesforce (ora unificate in REST API) - Gestione stato loading durante operazioni async PERSISTENZA AVANZATA: - Campo RestServiceType aggiunto a CredentialEntity con migrazione automatica - Serializzazione campi specifici Salesforce/SAP B1 in AdditionalParameters JSON - Mapping bidirezionale tra entità database e modelli business - Gestione nullability e conversioni tipo sicure SICUREZZA: - Crittografia AES-256 per password e token sensibili - Gestione sicura ConnectionString database - Validazione input e sanitizzazione dati TESTING E CONNETTIVITÀ: - Test autenticazione reale SAP B1 Service Layer - Test OAuth2 Salesforce con supporto Connected App - Test connettività database multi-provider - Logging dettagliato per debugging e monitoraggio CONFIGURAZIONE: - Dependency injection per tutti i servizi - Configurazione Entity Framework con SQLite - Tasks VS Code per build e run - Gestione connection string centralizzata CORREZIONI: - Risolti errori nullability in CredentialService - Aggiunto using Microsoft.JSInterop per IJSRuntime - Fix compilazione e warning Files modificati: 35+ file tra nuovi e aggiornati
This commit is contained in:
@@ -0,0 +1,963 @@
|
||||
@page "/credentials"
|
||||
@using CredentialManager.Models
|
||||
@using DataConnection.CredentialManagement.Interfaces
|
||||
@using DataConnection.CredentialManagement.Models
|
||||
@using Microsoft.AspNetCore.Components.Forms
|
||||
@using Microsoft.JSInterop
|
||||
@inject IDataConnectionCredentialService CredentialService
|
||||
@inject IJSRuntime JSRuntime
|
||||
|
||||
<PageTitle>Gestione Credenziali</PageTitle>
|
||||
|
||||
<h3>Gestione Credenziali</h3>
|
||||
|
||||
<div class="row mb-3">
|
||||
<div class="col">
|
||||
<div class="btn-group" role="group">
|
||||
<button class="btn btn-primary" @onclick="ShowAddDatabaseModal">
|
||||
<i class="oi oi-plus"></i> Database
|
||||
</button>
|
||||
<button class="btn btn-secondary" @onclick="ShowAddRestApiModal">
|
||||
<i class="oi oi-plus"></i> REST API / Servizi
|
||||
</button>
|
||||
</div>
|
||||
<button class="btn btn-info ms-3" @onclick="RefreshCredentials">
|
||||
<i class="oi oi-reload"></i> Aggiorna
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if (loading)
|
||||
{
|
||||
<div class="text-center">
|
||||
<div class="spinner-border" role="status">
|
||||
<span class="visually-hidden">Caricamento...</span>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
else if (!string.IsNullOrEmpty(errorMessage))
|
||||
{
|
||||
<div class="alert alert-danger" role="alert">
|
||||
<h4 class="alert-heading">Errore di Sistema</h4>
|
||||
<p>@errorMessage</p>
|
||||
<hr>
|
||||
<p class="mb-0">
|
||||
<button class="btn btn-outline-danger" @onclick="RefreshCredentials">
|
||||
<i class="oi oi-reload"></i> Riprova
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<!-- Credenziali Database -->
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<h4>Credenziali Database (@databaseCredentials.Count)</h4>
|
||||
@if (databaseCredentials.Any())
|
||||
{
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Nome</th>
|
||||
<th>Tipo Database</th>
|
||||
<th>Host:Porta</th>
|
||||
<th>Database</th>
|
||||
<th>Username</th>
|
||||
<th>Azioni</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var credential in databaseCredentials)
|
||||
{ <tr>
|
||||
<td>@credential.Name</td>
|
||||
<td>@credential.DatabaseType</td>
|
||||
<td>@credential.Host:@credential.Port</td>
|
||||
<td>
|
||||
@if (string.IsNullOrEmpty(credential.DatabaseName))
|
||||
{
|
||||
<em class="text-muted">Connessione server</em>
|
||||
}
|
||||
else
|
||||
{
|
||||
@credential.DatabaseName
|
||||
}
|
||||
</td>
|
||||
<td>@credential.Username</td>
|
||||
<td>
|
||||
<button class="btn btn-sm btn-outline-primary" @onclick="() => EditDatabaseCredential(credential)">
|
||||
<i class="oi oi-pencil"></i>
|
||||
</button>
|
||||
<button class="btn btn-sm btn-outline-success ms-1" @onclick="() => TestDatabaseConnection(credential)">
|
||||
<i class="oi oi-check"></i>
|
||||
</button>
|
||||
<button class="btn btn-sm btn-outline-danger ms-1" @onclick="() => DeleteCredential(credential.Name, true)">
|
||||
<i class="oi oi-trash"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="alert alert-info">
|
||||
Nessuna credenziale database configurata.
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr /> <!-- Credenziali REST API -->
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<h4>Credenziali REST API / Servizi (@restApiCredentials.Count)</h4>
|
||||
@if (restApiCredentials.Any())
|
||||
{
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Nome</th>
|
||||
<th>Tipo Servizio</th>
|
||||
<th>Base URL</th>
|
||||
<th>Autenticazione</th>
|
||||
<th>Timeout (s)</th>
|
||||
<th>Azioni</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var credential in restApiCredentials)
|
||||
{
|
||||
<tr>
|
||||
<td>@credential.Name</td>
|
||||
<td>
|
||||
@if (credential.ServiceType == RestServiceType.SapB1ServiceLayer)
|
||||
{
|
||||
<span class="badge bg-warning">SAP B1</span>
|
||||
}
|
||||
else if (credential.ServiceType == RestServiceType.Salesforce)
|
||||
{
|
||||
<span class="badge bg-success">Salesforce</span>
|
||||
@if (credential.IsSandbox)
|
||||
{
|
||||
<span class="badge bg-secondary ms-1">Sandbox</span>
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
<span class="badge bg-info">Generic REST</span>
|
||||
}
|
||||
</td>
|
||||
<td>@credential.BaseUrl</td>
|
||||
<td>@GetAuthenticationType(credential)</td>
|
||||
<td>@credential.TimeoutSeconds</td>
|
||||
<td>
|
||||
<button class="btn btn-sm btn-outline-primary" @onclick="() => EditRestApiCredential(credential)">
|
||||
<i class="oi oi-pencil"></i>
|
||||
</button>
|
||||
<button class="btn btn-sm btn-outline-success ms-1" @onclick="() => TestRestApiConnection(credential)">
|
||||
<i class="oi oi-check"></i>
|
||||
</button>
|
||||
<button class="btn btn-sm btn-outline-danger ms-1" @onclick="() => DeleteCredential(credential.Name, false)">
|
||||
<i class="oi oi-trash"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div> }
|
||||
else
|
||||
{
|
||||
<div class="alert alert-info">
|
||||
Nessuna credenziale REST API configurata.
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<!-- Modal per Aggiungere/Modificare Credenziale Database -->
|
||||
@if (showDatabaseModal)
|
||||
{
|
||||
<div class="modal fade show d-block" tabindex="-1" style="background-color: rgba(0,0,0,0.5);">
|
||||
<div class="modal-dialog modal-lg">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">@(editingDatabaseCredential == null ? "Aggiungi" : "Modifica") Credenziale Database</h5>
|
||||
<button type="button" class="btn-close" @onclick="CloseDatabaseModal"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<EditForm Model="currentDatabaseCredential" OnValidSubmit="SaveDatabaseCredential">
|
||||
<DataAnnotationsValidator />
|
||||
<ValidationSummary />
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Nome *</label>
|
||||
<InputText class="form-control" @bind-Value="currentDatabaseCredential.Name" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Tipo Database *</label>
|
||||
<InputSelect class="form-select" @bind-Value="currentDatabaseCredential.DatabaseType">
|
||||
<option value="@CredentialManager.Models.DatabaseType.SqlServer">SQL Server</option>
|
||||
<option value="@CredentialManager.Models.DatabaseType.MySql">MySQL</option>
|
||||
<option value="@CredentialManager.Models.DatabaseType.PostgreSql">PostgreSQL</option>
|
||||
<option value="@CredentialManager.Models.DatabaseType.Oracle">Oracle</option>
|
||||
<option value="@CredentialManager.Models.DatabaseType.Sqlite">SQLite</option>
|
||||
<option value="@CredentialManager.Models.DatabaseType.DB2">DB2</option>
|
||||
<option value="@CredentialManager.Models.DatabaseType.SapHana">SAP HANA</option>
|
||||
</InputSelect>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-8">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Host *</label>
|
||||
<InputText class="form-control" @bind-Value="currentDatabaseCredential.Host" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Porta *</label>
|
||||
<InputNumber class="form-control" @bind-Value="currentDatabaseCredential.Port" />
|
||||
</div>
|
||||
</div>
|
||||
</div> <div class="mb-3">
|
||||
<label class="form-label">Nome Database <small class="text-muted">(opzionale)</small></label>
|
||||
<InputText class="form-control" @bind-Value="currentDatabaseCredential.DatabaseName"
|
||||
placeholder="Lascia vuoto per connessione al server senza database specifico" />
|
||||
<div class="form-text">Se non specificato, la connessione sarà al server senza selezionare un database specifico</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Username *</label>
|
||||
<InputText class="form-control" @bind-Value="currentDatabaseCredential.Username" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Password *</label>
|
||||
<InputText type="password" class="form-control" @bind-Value="currentDatabaseCredential.Password" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Timeout Comando (s)</label>
|
||||
<InputNumber class="form-control" @bind-Value="currentDatabaseCredential.CommandTimeout" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3 form-check">
|
||||
<InputCheckbox class="form-check-input" @bind-Value="currentDatabaseCredential.IgnoreSslErrors" />
|
||||
<label class="form-check-label">
|
||||
Ignora errori SSL
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div> <div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" @onclick="CloseDatabaseModal">Annulla</button>
|
||||
<button type="button" class="btn btn-info" @onclick="TestCurrentDatabaseConnection" disabled="@testingConnection">
|
||||
@if (testingConnection)
|
||||
{
|
||||
<span class="spinner-border spinner-border-sm me-2" role="status" aria-hidden="true"></span>
|
||||
}
|
||||
<i class="oi oi-check"></i> Testa Connessione
|
||||
</button>
|
||||
<button type="submit" class="btn btn-primary">Salva</button>
|
||||
</div>
|
||||
</EditForm>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<!-- Modal per Aggiungere/Modificare Credenziale REST API -->
|
||||
@if (showRestApiModal)
|
||||
{
|
||||
<div class="modal fade show d-block" tabindex="-1" style="background-color: rgba(0,0,0,0.5);">
|
||||
<div class="modal-dialog modal-lg">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">
|
||||
@(editingRestApiCredential == null ? "Aggiungi" : "Modifica") Credenziale
|
||||
@GetServiceTypeDisplayName(currentRestApiCredential.ServiceType)
|
||||
</h5>
|
||||
<button type="button" class="btn-close" @onclick="CloseRestApiModal"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<EditForm Model="currentRestApiCredential" OnValidSubmit="SaveRestApiCredential">
|
||||
<DataAnnotationsValidator />
|
||||
<ValidationSummary />
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Nome *</label>
|
||||
<InputText class="form-control" @bind-Value="currentRestApiCredential.Name" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Tipo Servizio *</label>
|
||||
<InputSelect class="form-select" @bind-Value="currentRestApiCredential.ServiceType"
|
||||
@onchange="OnServiceTypeChanged">
|
||||
<option value="@RestServiceType.Generic">REST API Generico</option>
|
||||
<option value="@RestServiceType.SapB1ServiceLayer">SAP B1 Service Layer</option>
|
||||
<option value="@RestServiceType.Salesforce">Salesforce</option>
|
||||
</InputSelect>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Campi comuni -->
|
||||
<div class="mb-3">
|
||||
<label class="form-label">
|
||||
@GetUrlFieldLabel(currentRestApiCredential.ServiceType) *
|
||||
</label>
|
||||
<InputText class="form-control" @bind-Value="currentRestApiCredential.BaseUrl"
|
||||
placeholder="@GetUrlPlaceholder(currentRestApiCredential.ServiceType)" />
|
||||
<div class="form-text">@GetUrlHelpText(currentRestApiCredential.ServiceType)</div>
|
||||
</div>
|
||||
|
||||
<!-- Campi specifici per SAP B1 Service Layer -->
|
||||
@if (currentRestApiCredential.ServiceType == RestServiceType.SapB1ServiceLayer)
|
||||
{
|
||||
<div class="row">
|
||||
<div class="col-md-6"> <div class="mb-3">
|
||||
<label class="form-label">@GetFieldLabel("CompanyDatabase", currentRestApiCredential.ServiceType)</label>
|
||||
<InputText class="form-control" @bind-Value="currentRestApiCredential.CompanyDatabase"
|
||||
placeholder="SBODemoUS" />
|
||||
<div class="form-text">Nome del database azienda SAP B1</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Versione Service Layer</label>
|
||||
<InputSelect class="form-select" @bind-Value="currentRestApiCredential.Version">
|
||||
<option value="v1">v1</option>
|
||||
<option value="v2">v2</option>
|
||||
</InputSelect>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Lingua</label>
|
||||
<InputSelect class="form-select" @bind-Value="currentRestApiCredential.Language">
|
||||
<option value="en-US">English (US)</option>
|
||||
<option value="it-IT">Italiano</option>
|
||||
<option value="de-DE">Deutsch</option>
|
||||
<option value="fr-FR">Français</option>
|
||||
<option value="es-ES">Español</option>
|
||||
</InputSelect>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-check mt-4">
|
||||
<InputCheckbox class="form-check-input" @bind-Value="currentRestApiCredential.UseTrustedConnection" />
|
||||
<label class="form-check-label">
|
||||
Usa autenticazione Windows
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
} <!-- Campi specifici per Salesforce -->
|
||||
@if (currentRestApiCredential.ServiceType == RestServiceType.Salesforce)
|
||||
{
|
||||
<div class="alert alert-info">
|
||||
<strong>Opzioni di Autenticazione:</strong><br/>
|
||||
• <strong>Username/Password + Security Token:</strong> Autenticazione standard<br/>
|
||||
• <strong>Username/Password + Client ID/Secret:</strong> Autenticazione OAuth<br/>
|
||||
• Il Security Token è richiesto solo se non si configura una Connected App (Client ID/Secret)
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Tipo Ambiente</label>
|
||||
<InputSelect class="form-select" @bind-Value="currentRestApiCredential.IsSandbox"
|
||||
@onchange="OnSandboxChanged">
|
||||
<option value="false">Production</option>
|
||||
<option value="true">Sandbox</option>
|
||||
</InputSelect>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">API Version</label>
|
||||
<InputText class="form-control" @bind-Value="currentRestApiCredential.ApiVersion" />
|
||||
<div class="form-text">Esempio: 59.0</div>
|
||||
</div>
|
||||
</div>
|
||||
</div> <div class="mb-3">
|
||||
<label class="form-label">@GetFieldLabel("SecurityToken", currentRestApiCredential.ServiceType)</label>
|
||||
<InputText type="password" class="form-control" @bind-Value="currentRestApiCredential.SecurityToken" />
|
||||
<div class="form-text">Token di sicurezza Salesforce (richiesto solo se non si usa OAuth o Connected App)</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Client ID (Connected App)</label>
|
||||
<InputText class="form-control" @bind-Value="currentRestApiCredential.ClientId" />
|
||||
<div class="form-text">Consumer Key per OAuth</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Client Secret (Connected App)</label>
|
||||
<InputText type="password" class="form-control" @bind-Value="currentRestApiCredential.ClientSecret" />
|
||||
<div class="form-text">Consumer Secret per OAuth</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-check mb-3">
|
||||
<InputCheckbox class="form-check-input" @bind-Value="currentRestApiCredential.UseSoapApi" />
|
||||
<label class="form-check-label">
|
||||
Usa SOAP API (invece di REST)
|
||||
</label>
|
||||
</div>
|
||||
}
|
||||
|
||||
<!-- Campi per autenticazione generica (solo per REST Generico) -->
|
||||
@if (currentRestApiCredential.ServiceType == RestServiceType.Generic)
|
||||
{
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">API Key</label>
|
||||
<InputText class="form-control" @bind-Value="currentRestApiCredential.ApiKey" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Auth Token</label>
|
||||
<InputText class="form-control" @bind-Value="currentRestApiCredential.AuthToken" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<!-- Campi comuni per autenticazione username/password -->
|
||||
@if (currentRestApiCredential.ServiceType != RestServiceType.Generic ||
|
||||
(string.IsNullOrEmpty(currentRestApiCredential.ApiKey) && string.IsNullOrEmpty(currentRestApiCredential.AuthToken)))
|
||||
{ <div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">@GetFieldLabel("Username", currentRestApiCredential.ServiceType)</label>
|
||||
<InputText class="form-control" @bind-Value="currentRestApiCredential.Username" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">@GetFieldLabel("Password", currentRestApiCredential.ServiceType)</label>
|
||||
<InputText type="password" class="form-control" @bind-Value="currentRestApiCredential.Password" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<!-- Campi comuni finali -->
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Timeout (secondi)</label>
|
||||
<InputNumber class="form-control" @bind-Value="currentRestApiCredential.TimeoutSeconds" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-check mt-4">
|
||||
<InputCheckbox class="form-check-input" @bind-Value="currentRestApiCredential.IgnoreSslErrors" />
|
||||
<label class="form-check-label">
|
||||
Ignora errori SSL
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div> <div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" @onclick="CloseRestApiModal">Annulla</button>
|
||||
@if (!string.IsNullOrEmpty(currentRestApiCredential.Name))
|
||||
{
|
||||
<button type="button" class="btn btn-info" @onclick="() => TestRestApiConnectionFromModal()"
|
||||
disabled="@testingConnection">
|
||||
@if (testingConnection)
|
||||
{
|
||||
<span class="spinner-border spinner-border-sm me-2" role="status"></span>
|
||||
}
|
||||
<i class="oi oi-check"></i> Test Connessione
|
||||
</button>
|
||||
}
|
||||
<button type="submit" class="btn btn-primary">Salva</button>
|
||||
</div>
|
||||
</EditForm>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
|
||||
@code { private List<DatabaseCredential> databaseCredentials = new();
|
||||
private List<RestApiCredential> restApiCredentials = new();
|
||||
private bool loading = true;
|
||||
private string? errorMessage = null;
|
||||
private bool testingConnection = false;
|
||||
|
||||
// Modal state
|
||||
private bool showDatabaseModal = false;
|
||||
private bool showRestApiModal = false;
|
||||
private DatabaseCredential? editingDatabaseCredential = null;
|
||||
private RestApiCredential? editingRestApiCredential = null;
|
||||
private DatabaseCredential currentDatabaseCredential = new();
|
||||
private RestApiCredential currentRestApiCredential = new();
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
await RefreshCredentials();
|
||||
} private async Task RefreshCredentials()
|
||||
{
|
||||
loading = true;
|
||||
errorMessage = null;
|
||||
try
|
||||
{
|
||||
databaseCredentials = await CredentialService.GetAllDatabaseCredentialsAsync();
|
||||
restApiCredentials = await CredentialService.GetAllRestApiCredentialsAsync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
errorMessage = $"Errore nel caricamento delle credenziali: {ex.Message}";
|
||||
// Se l'errore è relativo alla tabella mancante, mostriamo un messaggio più specifico
|
||||
if (ex.Message.Contains("no such table: Credentials"))
|
||||
{
|
||||
errorMessage = "Database non inizializzato correttamente. Riavviare l'applicazione.";
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
loading = false;
|
||||
}
|
||||
}
|
||||
|
||||
#region Database Credential Methods
|
||||
|
||||
private void ShowAddDatabaseModal()
|
||||
{
|
||||
editingDatabaseCredential = null;
|
||||
currentDatabaseCredential = new DatabaseCredential
|
||||
{
|
||||
DatabaseType = CredentialManager.Models.DatabaseType.SqlServer,
|
||||
Port = 1433,
|
||||
CommandTimeout = 30
|
||||
};
|
||||
showDatabaseModal = true;
|
||||
}
|
||||
|
||||
private void EditDatabaseCredential(DatabaseCredential credential)
|
||||
{
|
||||
editingDatabaseCredential = credential;
|
||||
currentDatabaseCredential = new DatabaseCredential
|
||||
{
|
||||
Name = credential.Name,
|
||||
DatabaseType = credential.DatabaseType,
|
||||
Host = credential.Host,
|
||||
Port = credential.Port,
|
||||
DatabaseName = credential.DatabaseName,
|
||||
Username = credential.Username,
|
||||
Password = credential.Password,
|
||||
CommandTimeout = credential.CommandTimeout,
|
||||
IgnoreSslErrors = credential.IgnoreSslErrors
|
||||
};
|
||||
showDatabaseModal = true;
|
||||
}
|
||||
|
||||
private async Task SaveDatabaseCredential()
|
||||
{
|
||||
try
|
||||
{
|
||||
await CredentialService.SaveDatabaseCredentialAsync(currentDatabaseCredential);
|
||||
await JSRuntime.InvokeVoidAsync("alert", "Credenziale database salvata con successo!");
|
||||
CloseDatabaseModal();
|
||||
await RefreshCredentials();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await JSRuntime.InvokeVoidAsync("alert", $"Errore nel salvare la credenziale: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private void CloseDatabaseModal()
|
||||
{
|
||||
showDatabaseModal = false;
|
||||
editingDatabaseCredential = null;
|
||||
} private async Task TestDatabaseConnection(DatabaseCredential credential)
|
||||
{
|
||||
try
|
||||
{
|
||||
var (success, message) = await CredentialService.TestDatabaseConnectionAsync(credential.Name);
|
||||
|
||||
var title = success ? "Test Connessione - Successo" : "Test Connessione - Errore";
|
||||
await JSRuntime.InvokeVoidAsync("alert", $"{title}\n\n{message}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await JSRuntime.InvokeVoidAsync("alert", $"Errore nel test della connessione: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task TestCurrentDatabaseConnection()
|
||||
{
|
||||
if (testingConnection) return;
|
||||
|
||||
testingConnection = true;
|
||||
try
|
||||
{
|
||||
// Valida i campi obbligatori
|
||||
if (string.IsNullOrEmpty(currentDatabaseCredential.Name) ||
|
||||
string.IsNullOrEmpty(currentDatabaseCredential.Host) ||
|
||||
string.IsNullOrEmpty(currentDatabaseCredential.Username) ||
|
||||
string.IsNullOrEmpty(currentDatabaseCredential.Password))
|
||||
{
|
||||
await JSRuntime.InvokeVoidAsync("alert", "Compila tutti i campi obbligatori prima di testare la connessione.");
|
||||
return;
|
||||
}
|
||||
|
||||
var (success, message) = await CredentialService.TestDatabaseConnectionAsync(currentDatabaseCredential);
|
||||
|
||||
var title = success ? "Test Connessione - Successo" : "Test Connessione - Errore";
|
||||
await JSRuntime.InvokeVoidAsync("alert", $"{title}\n\n{message}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await JSRuntime.InvokeVoidAsync("alert", $"Errore nel test della connessione: {ex.Message}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
testingConnection = false;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region REST API Credential Methods
|
||||
|
||||
private void ShowAddRestApiModal()
|
||||
{
|
||||
editingRestApiCredential = null;
|
||||
currentRestApiCredential = new RestApiCredential
|
||||
{
|
||||
ServiceType = RestServiceType.Generic,
|
||||
TimeoutSeconds = 100
|
||||
};
|
||||
SetDefaultsForServiceType(currentRestApiCredential.ServiceType);
|
||||
showRestApiModal = true;
|
||||
}
|
||||
|
||||
private void EditRestApiCredential(RestApiCredential credential)
|
||||
{
|
||||
editingRestApiCredential = credential;
|
||||
currentRestApiCredential = new RestApiCredential
|
||||
{
|
||||
Name = credential.Name,
|
||||
ServiceType = credential.ServiceType,
|
||||
BaseUrl = credential.BaseUrl,
|
||||
ApiKey = credential.ApiKey,
|
||||
Username = credential.Username,
|
||||
Password = credential.Password,
|
||||
AuthToken = credential.AuthToken,
|
||||
BearerToken = credential.BearerToken,
|
||||
TimeoutSeconds = credential.TimeoutSeconds,
|
||||
IgnoreSslErrors = credential.IgnoreSslErrors,
|
||||
Headers = credential.Headers,
|
||||
AdditionalParameters = credential.AdditionalParameters,
|
||||
// Campi SAP B1
|
||||
CompanyDatabase = credential.CompanyDatabase,
|
||||
Language = credential.Language,
|
||||
Version = credential.Version,
|
||||
UseTrustedConnection = credential.UseTrustedConnection,
|
||||
// Campi Salesforce
|
||||
SecurityToken = credential.SecurityToken,
|
||||
ClientId = credential.ClientId,
|
||||
ClientSecret = credential.ClientSecret,
|
||||
ApiVersion = credential.ApiVersion,
|
||||
IsSandbox = credential.IsSandbox,
|
||||
UseSoapApi = credential.UseSoapApi,
|
||||
RefreshToken = credential.RefreshToken,
|
||||
AccessToken = credential.AccessToken,
|
||||
TokenExpiry = credential.TokenExpiry
|
||||
};
|
||||
showRestApiModal = true;
|
||||
}
|
||||
|
||||
private async Task SaveRestApiCredential()
|
||||
{
|
||||
try
|
||||
{
|
||||
await CredentialService.SaveRestApiCredentialAsync(currentRestApiCredential);
|
||||
await JSRuntime.InvokeVoidAsync("alert", $"Credenziale {GetServiceTypeDisplayName(currentRestApiCredential.ServiceType)} salvata con successo!");
|
||||
CloseRestApiModal();
|
||||
await RefreshCredentials();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await JSRuntime.InvokeVoidAsync("alert", $"Errore nel salvare la credenziale: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private void CloseRestApiModal()
|
||||
{
|
||||
showRestApiModal = false;
|
||||
editingRestApiCredential = null;
|
||||
}
|
||||
|
||||
private async Task TestRestApiConnection(RestApiCredential credential)
|
||||
{
|
||||
try
|
||||
{
|
||||
var (success, message) = credential.ServiceType switch
|
||||
{
|
||||
RestServiceType.SapB1ServiceLayer => await CredentialService.TestSapB1ConnectionAsync(credential.Name),
|
||||
RestServiceType.Salesforce => await CredentialService.TestSalesforceConnectionAsync(credential.Name),
|
||||
_ => await CredentialService.TestRestApiConnectionAsync(credential.Name)
|
||||
};
|
||||
|
||||
var title = success ? $"Test {GetServiceTypeDisplayName(credential.ServiceType)} - Successo" : $"Test {GetServiceTypeDisplayName(credential.ServiceType)} - Errore";
|
||||
await JSRuntime.InvokeVoidAsync("alert", $"{title}\n\n{message}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await JSRuntime.InvokeVoidAsync("alert", $"Errore nel test della connessione: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private void OnServiceTypeChanged(ChangeEventArgs e)
|
||||
{
|
||||
if (Enum.TryParse<RestServiceType>(e.Value?.ToString(), out var serviceType))
|
||||
{
|
||||
currentRestApiCredential.ServiceType = serviceType;
|
||||
SetDefaultsForServiceType(serviceType);
|
||||
}
|
||||
}
|
||||
|
||||
private void SetDefaultsForServiceType(RestServiceType serviceType)
|
||||
{
|
||||
switch (serviceType)
|
||||
{
|
||||
case RestServiceType.SapB1ServiceLayer:
|
||||
currentRestApiCredential.Language = "en-US";
|
||||
currentRestApiCredential.Version = "v1";
|
||||
currentRestApiCredential.TimeoutSeconds = 300;
|
||||
if (string.IsNullOrEmpty(currentRestApiCredential.BaseUrl))
|
||||
currentRestApiCredential.BaseUrl = "https://server:50000/b1s/v1/";
|
||||
break;
|
||||
case RestServiceType.Salesforce:
|
||||
currentRestApiCredential.ApiVersion = "59.0";
|
||||
currentRestApiCredential.TimeoutSeconds = 120;
|
||||
currentRestApiCredential.IsSandbox = false;
|
||||
currentRestApiCredential.UseSoapApi = false;
|
||||
if (string.IsNullOrEmpty(currentRestApiCredential.BaseUrl))
|
||||
currentRestApiCredential.BaseUrl = "https://login.salesforce.com";
|
||||
break;
|
||||
case RestServiceType.Generic:
|
||||
default:
|
||||
currentRestApiCredential.TimeoutSeconds = 100;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private string GetServiceTypeDisplayName(RestServiceType serviceType)
|
||||
{
|
||||
return serviceType switch
|
||||
{
|
||||
RestServiceType.SapB1ServiceLayer => "SAP B1 Service Layer",
|
||||
RestServiceType.Salesforce => "Salesforce",
|
||||
RestServiceType.Generic => "REST API",
|
||||
_ => "REST API"
|
||||
};
|
||||
}
|
||||
|
||||
private string GetUrlFieldLabel(RestServiceType serviceType)
|
||||
{
|
||||
return serviceType switch
|
||||
{
|
||||
RestServiceType.SapB1ServiceLayer => "Server URL",
|
||||
RestServiceType.Salesforce => "Login URL",
|
||||
_ => "Base URL"
|
||||
};
|
||||
}
|
||||
|
||||
private string GetUrlPlaceholder(RestServiceType serviceType)
|
||||
{
|
||||
return serviceType switch
|
||||
{
|
||||
RestServiceType.SapB1ServiceLayer => "https://server:50000/b1s/v1/",
|
||||
RestServiceType.Salesforce => "https://login.salesforce.com",
|
||||
_ => "https://api.example.com"
|
||||
};
|
||||
}
|
||||
|
||||
private string GetUrlHelpText(RestServiceType serviceType)
|
||||
{
|
||||
return serviceType switch
|
||||
{
|
||||
RestServiceType.SapB1ServiceLayer => "URL del SAP B1 Service Layer (esempio: https://server:50000/b1s/v1/)",
|
||||
RestServiceType.Salesforce => "Production: https://login.salesforce.com | Sandbox: https://test.salesforce.com",
|
||||
_ => "URL base del servizio REST API"
|
||||
};
|
||||
} private void OnSandboxChanged(ChangeEventArgs e)
|
||||
{
|
||||
if (bool.TryParse(e.Value?.ToString(), out bool isSandbox))
|
||||
{
|
||||
currentRestApiCredential.IsSandbox = isSandbox;
|
||||
currentRestApiCredential.BaseUrl = isSandbox
|
||||
? "https://test.salesforce.com"
|
||||
: "https://login.salesforce.com";
|
||||
} }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Common Methods
|
||||
|
||||
private async Task 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);
|
||||
|
||||
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)
|
||||
{
|
||||
await JSRuntime.InvokeVoidAsync("alert", $"Errore nell'eliminazione: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string GetAuthenticationType(RestApiCredential credential)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(credential.ApiKey))
|
||||
return "API Key";
|
||||
if (!string.IsNullOrEmpty(credential.AuthToken))
|
||||
return "Auth Token";
|
||||
if (!string.IsNullOrEmpty(credential.Username))
|
||||
return "Basic Auth";
|
||||
return "Nessuna";
|
||||
}
|
||||
|
||||
private async Task TestRestApiConnectionFromModal()
|
||||
{
|
||||
try
|
||||
{
|
||||
testingConnection = true;
|
||||
|
||||
// Creiamo una credenziale temporanea per il test
|
||||
var tempCredential = new RestApiCredential
|
||||
{
|
||||
Name = $"temp_test_{Guid.NewGuid():N}",
|
||||
ServiceType = currentRestApiCredential.ServiceType,
|
||||
BaseUrl = currentRestApiCredential.BaseUrl,
|
||||
Username = currentRestApiCredential.Username,
|
||||
Password = currentRestApiCredential.Password,
|
||||
ApiKey = currentRestApiCredential.ApiKey,
|
||||
AuthToken = currentRestApiCredential.AuthToken,
|
||||
TimeoutSeconds = currentRestApiCredential.TimeoutSeconds,
|
||||
IgnoreSslErrors = currentRestApiCredential.IgnoreSslErrors,
|
||||
// Campi SAP B1
|
||||
CompanyDatabase = currentRestApiCredential.CompanyDatabase,
|
||||
Version = currentRestApiCredential.Version,
|
||||
Language = currentRestApiCredential.Language,
|
||||
UseTrustedConnection = currentRestApiCredential.UseTrustedConnection,
|
||||
// Campi Salesforce
|
||||
SecurityToken = currentRestApiCredential.SecurityToken,
|
||||
ClientId = currentRestApiCredential.ClientId,
|
||||
ClientSecret = currentRestApiCredential.ClientSecret,
|
||||
ApiVersion = currentRestApiCredential.ApiVersion,
|
||||
IsSandbox = currentRestApiCredential.IsSandbox,
|
||||
UseSoapApi = currentRestApiCredential.UseSoapApi
|
||||
}; // Salviamo temporaneamente la credenziale per il test
|
||||
await CredentialService.SaveRestApiCredentialAsync(tempCredential);
|
||||
|
||||
// Testiamo la connessione
|
||||
var (success, message) = await CredentialService.TestRestApiConnectionAsync(tempCredential.Name);
|
||||
|
||||
// Rimuoviamo la credenziale temporanea
|
||||
await CredentialService.DeleteRestApiCredentialAsync(tempCredential.Name);
|
||||
|
||||
var title = success ? "Test Connessione - Successo" : "Test Connessione - Errore";
|
||||
await JSRuntime.InvokeVoidAsync("alert", $"{title}\n\n{message}");
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await JSRuntime.InvokeVoidAsync("alert", $"Errore nel test della connessione: {ex.Message}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
testingConnection = false;
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsFieldRequired(string fieldName, RestServiceType serviceType)
|
||||
{
|
||||
return (fieldName, serviceType) switch
|
||||
{
|
||||
("Username", RestServiceType.Generic) => string.IsNullOrEmpty(currentRestApiCredential.ApiKey) && string.IsNullOrEmpty(currentRestApiCredential.AuthToken),
|
||||
("Password", RestServiceType.Generic) => string.IsNullOrEmpty(currentRestApiCredential.ApiKey) && string.IsNullOrEmpty(currentRestApiCredential.AuthToken),
|
||||
("Username", RestServiceType.SapB1ServiceLayer) => !currentRestApiCredential.UseTrustedConnection,
|
||||
("Password", RestServiceType.SapB1ServiceLayer) => !currentRestApiCredential.UseTrustedConnection,
|
||||
("CompanyDatabase", RestServiceType.SapB1ServiceLayer) => true,
|
||||
("Username", RestServiceType.Salesforce) => true,
|
||||
("Password", RestServiceType.Salesforce) => true,
|
||||
("SecurityToken", RestServiceType.Salesforce) => string.IsNullOrEmpty(currentRestApiCredential.ClientId) && string.IsNullOrEmpty(currentRestApiCredential.ClientSecret),
|
||||
_ => false
|
||||
};
|
||||
}
|
||||
|
||||
private string GetFieldLabel(string fieldName, RestServiceType serviceType)
|
||||
{
|
||||
var label = (fieldName, serviceType) switch
|
||||
{
|
||||
("Username", RestServiceType.SapB1ServiceLayer) => "Username",
|
||||
("Password", RestServiceType.SapB1ServiceLayer) => "Password",
|
||||
("CompanyDatabase", RestServiceType.SapB1ServiceLayer) => "Company Database",
|
||||
("Username", RestServiceType.Salesforce) => "Username",
|
||||
("Password", RestServiceType.Salesforce) => "Password",
|
||||
("SecurityToken", RestServiceType.Salesforce) => "Security Token",
|
||||
_ => fieldName
|
||||
};
|
||||
|
||||
return IsFieldRequired(fieldName, serviceType) ? $"{label} *" : label;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
Reference in New Issue
Block a user