diff --git a/TrafagSalesExporter/Components/Pages/Standorte.razor b/TrafagSalesExporter/Components/Pages/Standorte.razor index 963fe2f..c82c636 100644 --- a/TrafagSalesExporter/Components/Pages/Standorte.razor +++ b/TrafagSalesExporter/Components/Pages/Standorte.razor @@ -36,14 +36,14 @@ @if (_connectionStatus.TryGetValue(context.Id, out var status)) { - + @(status.Success ? "OK" : "Fehler") - @status.Stage } else { - Nicht getestet + Nicht getestet } @@ -71,7 +71,7 @@ TSC Schema Quellsystem - Server + Host Aktiv Aktionen @@ -80,7 +80,7 @@ @context.TSC @context.Schema @context.SourceSystem - @(context.HanaServer?.Name ?? "-") + @GetServerNode(context.HanaServer) @if (context.IsActive) { @@ -132,12 +132,6 @@ @(_editingSite.Id == 0 ? "Standort hinzufügen" : "Standort bearbeiten") - - @foreach (var s in _servers) - { - @s.Name - } - @@ -148,6 +142,25 @@ } + + + + HANA-Verbindung + + + + + + + + + Abbrechen @@ -162,6 +175,7 @@ private List _sites = new(); private HanaServer _editingServer = new(); private Site _editingSite = new(); + private HanaServer _editingSiteServer = new(); private bool _serverDialogVisible; private bool _siteDialogVisible; private readonly DialogOptions _dialogOptions = new() { MaxWidth = MaxWidth.Small, FullWidth = true }; @@ -186,19 +200,7 @@ private void EditServer(HanaServer server) { - _editingServer = 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 - }; + _editingServer = CloneServer(server); _serverDialogVisible = true; } @@ -283,8 +285,9 @@ { IsActive = true, SourceSystem = "SAP", - HanaServerId = _servers.FirstOrDefault()?.Id ?? 0 + HanaServerId = 0 }; + _editingSiteServer = CreateDefaultSiteServer(); _siteDialogVisible = true; } @@ -300,14 +303,20 @@ SourceSystem = string.IsNullOrWhiteSpace(site.SourceSystem) ? "SAP" : site.SourceSystem, IsActive = site.IsActive }; + _editingSiteServer = site.HanaServer is null + ? CreateDefaultSiteServer(site) + : CloneServer(site.HanaServer); _siteDialogVisible = true; } private async Task SaveSite() { using var db = await DbFactory.CreateDbContextAsync(); + var serverId = await SaveOrCreateSiteServerAsync(db); + if (_editingSite.Id == 0) { + _editingSite.HanaServerId = serverId; db.Sites.Add(_editingSite); } else @@ -315,7 +324,7 @@ var existing = await db.Sites.FindAsync(_editingSite.Id); if (existing is not null) { - existing.HanaServerId = _editingSite.HanaServerId; + existing.HanaServerId = serverId; existing.Schema = _editingSite.Schema; existing.TSC = _editingSite.TSC; existing.Land = _editingSite.Land; @@ -350,4 +359,93 @@ await LoadDataAsync(); 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 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; + } } diff --git a/TrafagSalesExporter/Components/Pages/Transformations.razor b/TrafagSalesExporter/Components/Pages/Transformations.razor index 96086a3..5a05580 100644 --- a/TrafagSalesExporter/Components/Pages/Transformations.razor +++ b/TrafagSalesExporter/Components/Pages/Transformations.razor @@ -67,7 +67,7 @@ - diff --git a/TrafagSalesExporter/Models/HanaServer.cs b/TrafagSalesExporter/Models/HanaServer.cs index 36fd579..535823f 100644 --- a/TrafagSalesExporter/Models/HanaServer.cs +++ b/TrafagSalesExporter/Models/HanaServer.cs @@ -1,4 +1,5 @@ using System.ComponentModel.DataAnnotations; +using System.Data.Common; namespace TrafagSalesExporter.Models; @@ -41,26 +42,23 @@ public class HanaServer public string BuildConnectionString() { - var parts = new List - { - $"ServerNode={Host}:{Port}", - $"UserName={Username}", - $"Password={Password}" - }; + var builder = new DbConnectionStringBuilder(); + builder["ServerNode"] = BuildServerNode(); + builder["UserName"] = Username.Trim(); + builder["Password"] = Password; if (!string.IsNullOrWhiteSpace(DatabaseName)) - parts.Add($"DatabaseName={DatabaseName}"); + builder["DatabaseName"] = DatabaseName.Trim(); if (UseSsl) { - parts.Add("encrypt=true"); - parts.Add($"sslValidateCertificate={(ValidateCertificate ? "true" : "false")}"); + builder["encrypt"] = true; + builder["sslValidateCertificate"] = ValidateCertificate; } - if (!string.IsNullOrWhiteSpace(AdditionalParams)) - parts.Add(AdditionalParams.Trim().Trim(';')); + AppendAdditionalParams(builder); - return string.Join(";", parts); + return builder.ConnectionString; } public string GetConnectionStringPreview() @@ -80,5 +78,67 @@ public class HanaServer 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; + } + } }