Merge HANA SSL/MDC support and DLL reference fix from claude/blazor-sap-sales-exporter-9VrM0
This commit is contained in:
@@ -93,10 +93,19 @@
|
|||||||
</TitleContent>
|
</TitleContent>
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
<MudTextField @bind-Value="_editingServer.Name" Label="Name" Required />
|
<MudTextField @bind-Value="_editingServer.Name" Label="Name" Required />
|
||||||
<MudTextField @bind-Value="_editingServer.Host" Label="Host" Required />
|
<MudTextField @bind-Value="_editingServer.Host" Label="Host" Required
|
||||||
<MudNumericField @bind-Value="_editingServer.Port" Label="Port" />
|
HelperText="IP oder Hostname (ohne Protokoll)" />
|
||||||
|
<MudNumericField @bind-Value="_editingServer.Port" Label="Port"
|
||||||
|
HelperText="Typisch 30015 (Tenant), 30013 (SystemDB), 3xx15 für Instanz xx" />
|
||||||
<MudTextField @bind-Value="_editingServer.Username" Label="Username" />
|
<MudTextField @bind-Value="_editingServer.Username" Label="Username" />
|
||||||
<MudTextField @bind-Value="_editingServer.Password" Label="Password" InputType="InputType.Password" />
|
<MudTextField @bind-Value="_editingServer.Password" Label="Password" InputType="InputType.Password" />
|
||||||
|
<MudTextField @bind-Value="_editingServer.DatabaseName" Label="Database Name (MDC)"
|
||||||
|
HelperText="Nur bei Multi-Tenant Setup angeben, sonst leer lassen" />
|
||||||
|
<MudSwitch @bind-Value="_editingServer.UseSsl" Label="SSL/TLS verwenden (encrypt=true)" Color="Color.Primary" />
|
||||||
|
<MudSwitch @bind-Value="_editingServer.ValidateCertificate" Label="SSL-Zertifikat validieren" Color="Color.Primary"
|
||||||
|
Disabled="!_editingServer.UseSsl" />
|
||||||
|
<MudTextField @bind-Value="_editingServer.AdditionalParams" Label="Zusätzliche Parameter"
|
||||||
|
HelperText="Optional, z.B. sslCryptoProvider=openssl;communicationTimeout=0" />
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
<DialogActions>
|
<DialogActions>
|
||||||
<MudButton OnClick="() => _serverDialogVisible = false">Abbrechen</MudButton>
|
<MudButton OnClick="() => _serverDialogVisible = false">Abbrechen</MudButton>
|
||||||
@@ -164,7 +173,11 @@
|
|||||||
Host = server.Host,
|
Host = server.Host,
|
||||||
Port = server.Port,
|
Port = server.Port,
|
||||||
Username = server.Username,
|
Username = server.Username,
|
||||||
Password = server.Password
|
Password = server.Password,
|
||||||
|
DatabaseName = server.DatabaseName,
|
||||||
|
UseSsl = server.UseSsl,
|
||||||
|
ValidateCertificate = server.ValidateCertificate,
|
||||||
|
AdditionalParams = server.AdditionalParams
|
||||||
};
|
};
|
||||||
_serverDialogVisible = true;
|
_serverDialogVisible = true;
|
||||||
}
|
}
|
||||||
@@ -186,6 +199,10 @@
|
|||||||
existing.Port = _editingServer.Port;
|
existing.Port = _editingServer.Port;
|
||||||
existing.Username = _editingServer.Username;
|
existing.Username = _editingServer.Username;
|
||||||
existing.Password = _editingServer.Password;
|
existing.Password = _editingServer.Password;
|
||||||
|
existing.DatabaseName = _editingServer.DatabaseName;
|
||||||
|
existing.UseSsl = _editingServer.UseSsl;
|
||||||
|
existing.ValidateCertificate = _editingServer.ValidateCertificate;
|
||||||
|
existing.AdditionalParams = _editingServer.AdditionalParams;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
await db.SaveChangesAsync();
|
await db.SaveChangesAsync();
|
||||||
@@ -218,7 +235,7 @@
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await Task.Run(() => HanaService.TestConnection(server.Host, server.Port, server.Username, server.Password));
|
await Task.Run(() => HanaService.TestConnection(server));
|
||||||
Snackbar.Add($"Verbindung zu '{server.Name}' erfolgreich!", Severity.Success);
|
Snackbar.Add($"Verbindung zu '{server.Name}' erfolgreich!", Severity.Success);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using System.Data;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using TrafagSalesExporter.Models;
|
using TrafagSalesExporter.Models;
|
||||||
|
|
||||||
@@ -13,6 +14,46 @@ public class AppDbContext : DbContext
|
|||||||
public DbSet<ExportSettings> ExportSettings => Set<ExportSettings>();
|
public DbSet<ExportSettings> ExportSettings => Set<ExportSettings>();
|
||||||
public DbSet<ExportLog> ExportLogs => Set<ExportLog>();
|
public DbSet<ExportLog> ExportLogs => Set<ExportLog>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Fügt Spalten zu existierenden Tabellen hinzu, die bei neueren Versionen
|
||||||
|
/// hinzugekommen sind. EnsureCreated aktualisiert das Schema nicht automatisch.
|
||||||
|
/// </summary>
|
||||||
|
public static void EnsureSchema(AppDbContext db)
|
||||||
|
{
|
||||||
|
AddColumnIfMissing(db, "HanaServers", "DatabaseName", "TEXT NOT NULL DEFAULT ''");
|
||||||
|
AddColumnIfMissing(db, "HanaServers", "UseSsl", "INTEGER NOT NULL DEFAULT 0");
|
||||||
|
AddColumnIfMissing(db, "HanaServers", "ValidateCertificate", "INTEGER NOT NULL DEFAULT 0");
|
||||||
|
AddColumnIfMissing(db, "HanaServers", "AdditionalParams", "TEXT NOT NULL DEFAULT ''");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void AddColumnIfMissing(AppDbContext db, string table, string column, string type)
|
||||||
|
{
|
||||||
|
var conn = db.Database.GetDbConnection();
|
||||||
|
if (conn.State != ConnectionState.Open) conn.Open();
|
||||||
|
|
||||||
|
bool exists = false;
|
||||||
|
using (var cmd = conn.CreateCommand())
|
||||||
|
{
|
||||||
|
cmd.CommandText = $"PRAGMA table_info({table})";
|
||||||
|
using var reader = cmd.ExecuteReader();
|
||||||
|
while (reader.Read())
|
||||||
|
{
|
||||||
|
if (string.Equals(reader["name"]?.ToString(), column, StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
exists = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!exists)
|
||||||
|
{
|
||||||
|
using var alter = conn.CreateCommand();
|
||||||
|
alter.CommandText = $"ALTER TABLE {table} ADD COLUMN {column} {type}";
|
||||||
|
alter.ExecuteNonQuery();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static void SeedIfEmpty(AppDbContext db)
|
public static void SeedIfEmpty(AppDbContext db)
|
||||||
{
|
{
|
||||||
if (db.HanaServers.Any()) return;
|
if (db.HanaServers.Any()) return;
|
||||||
|
|||||||
@@ -17,4 +17,49 @@ public class HanaServer
|
|||||||
public string Username { get; set; } = string.Empty;
|
public string Username { get; set; } = string.Empty;
|
||||||
|
|
||||||
public string Password { get; set; } = string.Empty;
|
public string Password { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Name der Tenant-Datenbank bei Multi-Tenant Database Container (MDC) Setups.
|
||||||
|
/// Leer lassen, wenn direkt auf einen Tenant-Port verbunden wird.
|
||||||
|
/// </summary>
|
||||||
|
public string DatabaseName { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// SSL/TLS Verschlüsselung aktivieren (encrypt=true).
|
||||||
|
/// </summary>
|
||||||
|
public bool UseSsl { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// SSL-Zertifikat validieren. Bei self-signed Zertifikaten auf false setzen.
|
||||||
|
/// </summary>
|
||||||
|
public bool ValidateCertificate { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Zusätzliche Verbindungsparameter (Semikolon-getrennt), z.B. "sslCryptoProvider=openssl".
|
||||||
|
/// </summary>
|
||||||
|
public string AdditionalParams { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public string BuildConnectionString()
|
||||||
|
{
|
||||||
|
var parts = new List<string>
|
||||||
|
{
|
||||||
|
$"ServerNode={Host}:{Port}",
|
||||||
|
$"UserName={Username}",
|
||||||
|
$"Password={Password}"
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(DatabaseName))
|
||||||
|
parts.Add($"DatabaseName={DatabaseName}");
|
||||||
|
|
||||||
|
if (UseSsl)
|
||||||
|
{
|
||||||
|
parts.Add("encrypt=true");
|
||||||
|
parts.Add($"sslValidateCertificate={(ValidateCertificate ? "true" : "false")}");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(AdditionalParams))
|
||||||
|
parts.Add(AdditionalParams.Trim().Trim(';'));
|
||||||
|
|
||||||
|
return string.Join(";", parts);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ using (var scope = app.Services.CreateScope())
|
|||||||
var dbFactory = scope.ServiceProvider.GetRequiredService<IDbContextFactory<AppDbContext>>();
|
var dbFactory = scope.ServiceProvider.GetRequiredService<IDbContextFactory<AppDbContext>>();
|
||||||
using var db = await dbFactory.CreateDbContextAsync();
|
using var db = await dbFactory.CreateDbContextAsync();
|
||||||
await db.Database.EnsureCreatedAsync();
|
await db.Database.EnsureCreatedAsync();
|
||||||
|
AppDbContext.EnsureSchema(db);
|
||||||
AppDbContext.SeedIfEmpty(db);
|
AppDbContext.SeedIfEmpty(db);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -94,9 +94,7 @@ public class ExportOrchestrationService
|
|||||||
|
|
||||||
UpdateStatus(site.Id, "HANA Abfrage...");
|
UpdateStatus(site.Id, "HANA Abfrage...");
|
||||||
var records = await Task.Run(() => _hanaService.GetSalesRecords(
|
var records = await Task.Run(() => _hanaService.GetSalesRecords(
|
||||||
site.HanaServer.Host, site.HanaServer.Port,
|
site.HanaServer, site.Schema, site.TSC, site.Land, settings.DateFilter));
|
||||||
site.HanaServer.Username, site.HanaServer.Password,
|
|
||||||
site.Schema, site.TSC, site.Land, settings.DateFilter));
|
|
||||||
|
|
||||||
UpdateStatus(site.Id, "Excel erstellen...");
|
UpdateStatus(site.Id, "Excel erstellen...");
|
||||||
var outputDir = Path.Combine(AppContext.BaseDirectory, "output");
|
var outputDir = Path.Combine(AppContext.BaseDirectory, "output");
|
||||||
|
|||||||
@@ -5,10 +5,10 @@ namespace TrafagSalesExporter.Services;
|
|||||||
|
|
||||||
public class HanaQueryService
|
public class HanaQueryService
|
||||||
{
|
{
|
||||||
public List<SalesRecord> GetSalesRecords(string host, int port, string username, string password,
|
public List<SalesRecord> GetSalesRecords(HanaServer server,
|
||||||
string schema, string tsc, string land, string dateFilter)
|
string schema, string tsc, string land, string dateFilter)
|
||||||
{
|
{
|
||||||
var connectionString = $"ServerNode={host}:{port};UserName={username};Password={password}";
|
var connectionString = server.BuildConnectionString();
|
||||||
var result = new List<SalesRecord>();
|
var result = new List<SalesRecord>();
|
||||||
|
|
||||||
using var connection = new HanaConnection(connectionString);
|
using var connection = new HanaConnection(connectionString);
|
||||||
@@ -32,9 +32,9 @@ public class HanaQueryService
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void TestConnection(string host, int port, string username, string password)
|
public void TestConnection(HanaServer server)
|
||||||
{
|
{
|
||||||
var connectionString = $"ServerNode={host}:{port};UserName={username};Password={password}";
|
var connectionString = server.BuildConnectionString();
|
||||||
using var connection = new HanaConnection(connectionString);
|
using var connection = new HanaConnection(connectionString);
|
||||||
connection.Open();
|
connection.Open();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,14 @@
|
|||||||
<TargetFramework>net8.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Pfad zur SAP HANA Client DLL (wird mit dem SAP HANA Client installiert).
|
||||||
|
Standard-Pfad nach Installation: C:\Program Files\sap\hdbclient\dotnetcore\v2.1\
|
||||||
|
Kann bei Bedarf via MSBuild-Property überschrieben werden:
|
||||||
|
dotnet build /p:HanaClientDll="D:\pfad\zu\Sap.Data.Hana.Core.v2.1.dll"
|
||||||
|
-->
|
||||||
|
<HanaClientDll Condition="'$(HanaClientDll)' == ''">C:\Program Files\sap\hdbclient\dotnetcore\v2.1\Sap.Data.Hana.Core.v2.1.dll</HanaClientDll>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@@ -15,6 +23,17 @@
|
|||||||
<PackageReference Include="Microsoft.Graph" Version="5.80.0" />
|
<PackageReference Include="Microsoft.Graph" Version="5.80.0" />
|
||||||
<PackageReference Include="Azure.Identity" Version="1.13.1" />
|
<PackageReference Include="Azure.Identity" Version="1.13.1" />
|
||||||
<PackageReference Include="MudBlazor" Version="7.15.0" />
|
<PackageReference Include="MudBlazor" Version="7.15.0" />
|
||||||
<PackageReference Include="Sap.Data.Hana.v2" Version="2.22.26" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="Sap.Data.Hana.Core.v2.1">
|
||||||
|
<HintPath>$(HanaClientDll)</HintPath>
|
||||||
|
<Private>true</Private>
|
||||||
|
</Reference>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<Target Name="CheckHanaClient" BeforeTargets="ResolveAssemblyReferences">
|
||||||
|
<Warning Condition="!Exists('$(HanaClientDll)')"
|
||||||
|
Text="SAP HANA Client DLL nicht gefunden: $(HanaClientDll). Bitte SAP HANA Client installieren (https://tools.hana.ondemand.com) oder MSBuild-Property 'HanaClientDll' setzen." />
|
||||||
|
</Target>
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
Reference in New Issue
Block a user