This commit is contained in:
2026-04-13 14:06:51 +02:00
parent 2b9b40af93
commit e1259b9ca8
3 changed files with 196 additions and 38 deletions
@@ -36,14 +36,14 @@
@if (_connectionStatus.TryGetValue(context.Id, out var status)) @if (_connectionStatus.TryGetValue(context.Id, out var status))
{ {
<MudTooltip Text="@BuildStatusTooltip(status)"> <MudTooltip Text="@BuildStatusTooltip(status)">
<MudChip Color="@(status.Success ? Color.Success : Color.Error)" Variant="Variant.Filled" Size="Size.Small"> <MudChip T="string" Color="@(status.Success ? Color.Success : Color.Error)" Variant="Variant.Filled" Size="Size.Small">
@(status.Success ? "OK" : "Fehler") - @status.Stage @(status.Success ? "OK" : "Fehler") - @status.Stage
</MudChip> </MudChip>
</MudTooltip> </MudTooltip>
} }
else else
{ {
<MudChip Color="Color.Default" Variant="Variant.Outlined" Size="Size.Small">Nicht getestet</MudChip> <MudChip T="string" Color="Color.Default" Variant="Variant.Outlined" Size="Size.Small">Nicht getestet</MudChip>
} }
</MudTd> </MudTd>
<MudTd> <MudTd>
@@ -71,7 +71,7 @@
<MudTh>TSC</MudTh> <MudTh>TSC</MudTh>
<MudTh>Schema</MudTh> <MudTh>Schema</MudTh>
<MudTh>Quellsystem</MudTh> <MudTh>Quellsystem</MudTh>
<MudTh>Server</MudTh> <MudTh>Host</MudTh>
<MudTh>Aktiv</MudTh> <MudTh>Aktiv</MudTh>
<MudTh>Aktionen</MudTh> <MudTh>Aktionen</MudTh>
</HeaderContent> </HeaderContent>
@@ -80,7 +80,7 @@
<MudTd>@context.TSC</MudTd> <MudTd>@context.TSC</MudTd>
<MudTd>@context.Schema</MudTd> <MudTd>@context.Schema</MudTd>
<MudTd>@context.SourceSystem</MudTd> <MudTd>@context.SourceSystem</MudTd>
<MudTd>@(context.HanaServer?.Name ?? "-")</MudTd> <MudTd>@GetServerNode(context.HanaServer)</MudTd>
<MudTd> <MudTd>
@if (context.IsActive) @if (context.IsActive)
{ {
@@ -132,12 +132,6 @@
<MudText Typo="Typo.h6">@(_editingSite.Id == 0 ? "Standort hinzufügen" : "Standort bearbeiten")</MudText> <MudText Typo="Typo.h6">@(_editingSite.Id == 0 ? "Standort hinzufügen" : "Standort bearbeiten")</MudText>
</TitleContent> </TitleContent>
<DialogContent> <DialogContent>
<MudSelect @bind-Value="_editingSite.HanaServerId" Label="Server" Required>
@foreach (var s in _servers)
{
<MudSelectItem Value="@s.Id">@s.Name</MudSelectItem>
}
</MudSelect>
<MudTextField @bind-Value="_editingSite.Schema" Label="Schema" Required /> <MudTextField @bind-Value="_editingSite.Schema" Label="Schema" Required />
<MudTextField @bind-Value="_editingSite.TSC" Label="TSC" Required /> <MudTextField @bind-Value="_editingSite.TSC" Label="TSC" Required />
<MudTextField @bind-Value="_editingSite.Land" Label="Land" Required /> <MudTextField @bind-Value="_editingSite.Land" Label="Land" Required />
@@ -148,6 +142,25 @@
} }
</MudSelect> </MudSelect>
<MudCheckBox @bind-Value="_editingSite.IsActive" Label="Aktiv" /> <MudCheckBox @bind-Value="_editingSite.IsActive" Label="Aktiv" />
<MudDivider Class="my-4" />
<MudText Typo="Typo.h6" Class="mb-2">HANA-Verbindung</MudText>
<MudTextField @bind-Value="_editingSiteServer.Name" Label="Verbindungsname" Required
HelperText="Interner Anzeigename für diesen Standort" />
<MudTextField @bind-Value="_editingSiteServer.Host" Label="Host oder ServerNode" Required
HelperText="z.B. hana01 oder hana01:30015 oder derselbe HanaServer-Wert wie in Power BI" />
<MudNumericField @bind-Value="_editingSiteServer.Port" Label="Port"
HelperText="Wird ignoriert, wenn im Host bereits ein Port enthalten ist" />
<MudTextField @bind-Value="_editingSiteServer.Username" Label="Username" />
<MudTextField @bind-Value="_editingSiteServer.Password" Label="Password" InputType="InputType.Password" />
<MudTextField @bind-Value="_editingSiteServer.DatabaseName" Label="Database Name (MDC)"
HelperText="Nur bei Multi-Tenant Setup angeben, sonst leer lassen" />
<MudSwitch @bind-Value="_editingSiteServer.UseSsl" Label="SSL/TLS verwenden (encrypt=true)" Color="Color.Primary" />
<MudSwitch @bind-Value="_editingSiteServer.ValidateCertificate" Label="SSL-Zertifikat validieren" Color="Color.Primary"
Disabled="!_editingSiteServer.UseSsl" />
<MudTextField @bind-Value="_editingSiteServer.AdditionalParams" Label="Zusätzliche Parameter"
HelperText="Optional, z.B. sslCryptoProvider=openssl;communicationTimeout=0" />
</DialogContent> </DialogContent>
<DialogActions> <DialogActions>
<MudButton OnClick="() => _siteDialogVisible = false">Abbrechen</MudButton> <MudButton OnClick="() => _siteDialogVisible = false">Abbrechen</MudButton>
@@ -162,6 +175,7 @@
private List<Site> _sites = new(); private List<Site> _sites = new();
private HanaServer _editingServer = new(); private HanaServer _editingServer = new();
private Site _editingSite = new(); private Site _editingSite = new();
private HanaServer _editingSiteServer = new();
private bool _serverDialogVisible; private bool _serverDialogVisible;
private bool _siteDialogVisible; private bool _siteDialogVisible;
private readonly DialogOptions _dialogOptions = new() { MaxWidth = MaxWidth.Small, FullWidth = true }; private readonly DialogOptions _dialogOptions = new() { MaxWidth = MaxWidth.Small, FullWidth = true };
@@ -186,19 +200,7 @@
private void EditServer(HanaServer server) private void EditServer(HanaServer server)
{ {
_editingServer = new HanaServer _editingServer = CloneServer(server);
{
Id = server.Id,
Name = server.Name,
Host = server.Host,
Port = server.Port,
Username = server.Username,
Password = server.Password,
DatabaseName = server.DatabaseName,
UseSsl = server.UseSsl,
ValidateCertificate = server.ValidateCertificate,
AdditionalParams = server.AdditionalParams
};
_serverDialogVisible = true; _serverDialogVisible = true;
} }
@@ -283,8 +285,9 @@
{ {
IsActive = true, IsActive = true,
SourceSystem = "SAP", SourceSystem = "SAP",
HanaServerId = _servers.FirstOrDefault()?.Id ?? 0 HanaServerId = 0
}; };
_editingSiteServer = CreateDefaultSiteServer();
_siteDialogVisible = true; _siteDialogVisible = true;
} }
@@ -300,14 +303,20 @@
SourceSystem = string.IsNullOrWhiteSpace(site.SourceSystem) ? "SAP" : site.SourceSystem, SourceSystem = string.IsNullOrWhiteSpace(site.SourceSystem) ? "SAP" : site.SourceSystem,
IsActive = site.IsActive IsActive = site.IsActive
}; };
_editingSiteServer = site.HanaServer is null
? CreateDefaultSiteServer(site)
: CloneServer(site.HanaServer);
_siteDialogVisible = true; _siteDialogVisible = true;
} }
private async Task SaveSite() private async Task SaveSite()
{ {
using var db = await DbFactory.CreateDbContextAsync(); using var db = await DbFactory.CreateDbContextAsync();
var serverId = await SaveOrCreateSiteServerAsync(db);
if (_editingSite.Id == 0) if (_editingSite.Id == 0)
{ {
_editingSite.HanaServerId = serverId;
db.Sites.Add(_editingSite); db.Sites.Add(_editingSite);
} }
else else
@@ -315,7 +324,7 @@
var existing = await db.Sites.FindAsync(_editingSite.Id); var existing = await db.Sites.FindAsync(_editingSite.Id);
if (existing is not null) if (existing is not null)
{ {
existing.HanaServerId = _editingSite.HanaServerId; existing.HanaServerId = serverId;
existing.Schema = _editingSite.Schema; existing.Schema = _editingSite.Schema;
existing.TSC = _editingSite.TSC; existing.TSC = _editingSite.TSC;
existing.Land = _editingSite.Land; existing.Land = _editingSite.Land;
@@ -350,4 +359,93 @@
await LoadDataAsync(); await LoadDataAsync();
Snackbar.Add("Standort gelöscht", Severity.Info); Snackbar.Add("Standort gelöscht", Severity.Info);
} }
private static string GetServerNode(HanaServer? server)
{
if (server is null || string.IsNullOrWhiteSpace(server.Host))
return "-";
return server.Host.Contains(':', StringComparison.Ordinal) ? server.Host : $"{server.Host}:{server.Port}";
}
private HanaServer CreateDefaultSiteServer(Site? site = null)
{
var label = !string.IsNullOrWhiteSpace(site?.Land) ? site!.Land : site?.TSC;
if (string.IsNullOrWhiteSpace(label))
label = "Neuer Standort";
return new HanaServer
{
Name = $"{label} HANA",
Port = 30015
};
}
private static HanaServer CloneServer(HanaServer server)
{
return new HanaServer
{
Id = server.Id,
Name = server.Name,
Host = server.Host,
Port = server.Port,
Username = server.Username,
Password = server.Password,
DatabaseName = server.DatabaseName,
UseSsl = server.UseSsl,
ValidateCertificate = server.ValidateCertificate,
AdditionalParams = server.AdditionalParams
};
}
private async Task<int> SaveOrCreateSiteServerAsync(AppDbContext db)
{
_editingSiteServer.Name = string.IsNullOrWhiteSpace(_editingSiteServer.Name)
? $"{_editingSite.Land} HANA".Trim()
: _editingSiteServer.Name.Trim();
_editingSiteServer.Host = _editingSiteServer.Host.Trim();
_editingSiteServer.Username = _editingSiteServer.Username.Trim();
_editingSiteServer.DatabaseName = _editingSiteServer.DatabaseName.Trim();
_editingSiteServer.AdditionalParams = _editingSiteServer.AdditionalParams.Trim();
if (string.IsNullOrWhiteSpace(_editingSiteServer.Host))
throw new InvalidOperationException("Host oder ServerNode muss gesetzt sein.");
if (_editingSite.HanaServerId == 0)
{
db.HanaServers.Add(_editingSiteServer);
await db.SaveChangesAsync();
return _editingSiteServer.Id;
}
var sharedUseCount = await db.Sites.CountAsync(s => s.HanaServerId == _editingSite.HanaServerId && s.Id != _editingSite.Id);
if (sharedUseCount > 0)
{
var dedicatedServer = CloneServer(_editingSiteServer);
dedicatedServer.Id = 0;
db.HanaServers.Add(dedicatedServer);
await db.SaveChangesAsync();
return dedicatedServer.Id;
}
var existingServer = await db.HanaServers.FindAsync(_editingSite.HanaServerId);
if (existingServer is null)
{
db.HanaServers.Add(_editingSiteServer);
await db.SaveChangesAsync();
return _editingSiteServer.Id;
}
existingServer.Name = _editingSiteServer.Name;
existingServer.Host = _editingSiteServer.Host;
existingServer.Port = _editingSiteServer.Port;
existingServer.Username = _editingSiteServer.Username;
existingServer.Password = _editingSiteServer.Password;
existingServer.DatabaseName = _editingSiteServer.DatabaseName;
existingServer.UseSsl = _editingSiteServer.UseSsl;
existingServer.ValidateCertificate = _editingSiteServer.ValidateCertificate;
existingServer.AdditionalParams = _editingSiteServer.AdditionalParams;
await db.SaveChangesAsync();
return existingServer.Id;
}
} }
@@ -67,7 +67,7 @@
</MudSelect> </MudSelect>
</MudTd> </MudTd>
<MudTd> <MudTd>
<MudTextField Value="@context.Argument" ValueChanged="@(v => context.Argument = v)" Dense <MudTextField T="string" Value="@context.Argument" ValueChanged="@(v => context.Argument = v)"
HelperText="Replace: alt=>neu" /> HelperText="Replace: alt=>neu" />
</MudTd> </MudTd>
<MudTd> <MudTd>
+72 -12
View File
@@ -1,4 +1,5 @@
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.Data.Common;
namespace TrafagSalesExporter.Models; namespace TrafagSalesExporter.Models;
@@ -41,26 +42,23 @@ public class HanaServer
public string BuildConnectionString() public string BuildConnectionString()
{ {
var parts = new List<string> var builder = new DbConnectionStringBuilder();
{ builder["ServerNode"] = BuildServerNode();
$"ServerNode={Host}:{Port}", builder["UserName"] = Username.Trim();
$"UserName={Username}", builder["Password"] = Password;
$"Password={Password}"
};
if (!string.IsNullOrWhiteSpace(DatabaseName)) if (!string.IsNullOrWhiteSpace(DatabaseName))
parts.Add($"DatabaseName={DatabaseName}"); builder["DatabaseName"] = DatabaseName.Trim();
if (UseSsl) if (UseSsl)
{ {
parts.Add("encrypt=true"); builder["encrypt"] = true;
parts.Add($"sslValidateCertificate={(ValidateCertificate ? "true" : "false")}"); builder["sslValidateCertificate"] = ValidateCertificate;
} }
if (!string.IsNullOrWhiteSpace(AdditionalParams)) AppendAdditionalParams(builder);
parts.Add(AdditionalParams.Trim().Trim(';'));
return string.Join(";", parts); return builder.ConnectionString;
} }
public string GetConnectionStringPreview() public string GetConnectionStringPreview()
@@ -80,5 +78,67 @@ public class HanaServer
return copy.BuildConnectionString(); return copy.BuildConnectionString();
} }
private string BuildServerNode()
{
var normalizedHost = NormalizeHost(Host);
if (string.IsNullOrWhiteSpace(normalizedHost))
throw new InvalidOperationException("HANA Host darf nicht leer sein.");
if (HasExplicitPort(normalizedHost))
return normalizedHost;
return $"{normalizedHost}:{Port}";
}
private static string NormalizeHost(string host)
{
var value = host.Trim();
if (string.IsNullOrWhiteSpace(value))
return string.Empty;
if (Uri.TryCreate(value, UriKind.Absolute, out var uri))
{
return uri.IsDefaultPort ? uri.Host : $"{uri.Host}:{uri.Port}";
}
var schemeIndex = value.IndexOf("://", StringComparison.Ordinal);
if (schemeIndex >= 0)
value = value[(schemeIndex + 3)..];
var slashIndex = value.IndexOf('/');
if (slashIndex >= 0)
value = value[..slashIndex];
return value.Trim();
}
private static bool HasExplicitPort(string host)
{
if (host.StartsWith('['))
return host.Contains("]:", StringComparison.Ordinal);
return host.Count(c => c == ':') == 1;
}
private void AppendAdditionalParams(DbConnectionStringBuilder builder)
{
if (string.IsNullOrWhiteSpace(AdditionalParams))
return;
foreach (var rawPart in AdditionalParams.Split(';', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries))
{
var separatorIndex = rawPart.IndexOf('=');
if (separatorIndex <= 0 || separatorIndex == rawPart.Length - 1)
continue;
var key = rawPart[..separatorIndex].Trim();
var value = rawPart[(separatorIndex + 1)..].Trim();
if (key.Length == 0)
continue;
builder[key] = value;
}
}
} }