Zentrales PW

This commit is contained in:
2026-04-14 10:05:59 +02:00
parent cf20bd94d0
commit df90a4a172
7 changed files with 235 additions and 2 deletions
@@ -1,10 +1,12 @@
@page "/settings"
@using Microsoft.EntityFrameworkCore
@using TrafagSalesExporter.Data
@using TrafagSalesExporter.Models
@using TrafagSalesExporter.Services
@inject IDbContextFactory<AppDbContext> DbFactory
@inject ISharePointUploadService SpService
@inject TimerBackgroundService TimerService
@inject IHanaQueryService HanaService
@inject ISnackbar Snackbar
<PageTitle>Settings</PageTitle>
@@ -53,6 +55,74 @@
</MudGrid>
</MudPaper>
<MudText Typo="Typo.h5" Class="mb-2">Zentrale Quellsystem-Zugangsdaten</MudText>
<MudPaper Class="pa-4 mb-6" Elevation="1">
<MudGrid>
<MudItem xs="12">
<MudAlert Severity="Severity.Info" Dense="true" Variant="Variant.Outlined">
Diese Zugangsdaten werden pro Quellsystem als Standard verwendet. Ein Standort kann sie bei Bedarf mit eigenen Overrides überschreiben.
</MudAlert>
</MudItem>
<MudItem xs="12" md="4">
<MudText Typo="Typo.h6" Class="mb-2">SAP</MudText>
<MudTextField @bind-Value="_exportSettings.SapUsername" Label="SAP Username" />
<MudTextField @bind-Value="_exportSettings.SapPassword" Label="SAP Password" InputType="InputType.Password" />
<MudButton Variant="Variant.Outlined" Color="Color.Info" OnClick='@(() => TestCentralCredentials("SAP"))'
StartIcon="@Icons.Material.Filled.NetworkCheck" Disabled='@_testingSystems.Contains("SAP")' Class="mt-2">
@if (_testingSystems.Contains("SAP"))
{
<MudProgressCircular Size="Size.Small" Indeterminate Class="mr-2" />
@("Teste...")
}
else
{
@("SAP testen")
}
</MudButton>
</MudItem>
<MudItem xs="12" md="4">
<MudText Typo="Typo.h6" Class="mb-2">BI1</MudText>
<MudTextField @bind-Value="_exportSettings.Bi1Username" Label="BI1 Username" />
<MudTextField @bind-Value="_exportSettings.Bi1Password" Label="BI1 Password" InputType="InputType.Password" />
<MudButton Variant="Variant.Outlined" Color="Color.Info" OnClick='@(() => TestCentralCredentials("BI1"))'
StartIcon="@Icons.Material.Filled.NetworkCheck" Disabled='@_testingSystems.Contains("BI1")' Class="mt-2">
@if (_testingSystems.Contains("BI1"))
{
<MudProgressCircular Size="Size.Small" Indeterminate Class="mr-2" />
@("Teste...")
}
else
{
@("BI1 testen")
}
</MudButton>
</MudItem>
<MudItem xs="12" md="4">
<MudText Typo="Typo.h6" Class="mb-2">SAGE</MudText>
<MudTextField @bind-Value="_exportSettings.SageUsername" Label="SAGE Username" />
<MudTextField @bind-Value="_exportSettings.SagePassword" Label="SAGE Password" InputType="InputType.Password" />
<MudButton Variant="Variant.Outlined" Color="Color.Info" OnClick='@(() => TestCentralCredentials("SAGE"))'
StartIcon="@Icons.Material.Filled.NetworkCheck" Disabled='@_testingSystems.Contains("SAGE")' Class="mt-2">
@if (_testingSystems.Contains("SAGE"))
{
<MudProgressCircular Size="Size.Small" Indeterminate Class="mr-2" />
@("Teste...")
}
else
{
@("SAGE testen")
}
</MudButton>
</MudItem>
<MudItem xs="12">
<MudButton Variant="Variant.Filled" Color="Color.Primary" OnClick="SaveExportSettings"
StartIcon="@Icons.Material.Filled.Save">
Speichern
</MudButton>
</MudItem>
</MudGrid>
</MudPaper>
@* Export Settings *@
<MudText Typo="Typo.h5" Class="mb-2">Export Einstellungen</MudText>
<MudPaper Class="pa-4 mb-6" Elevation="1">
@@ -95,6 +165,7 @@
private SharePointConfig _spConfig = new();
private ExportSettings _exportSettings = new();
private bool _testingSp;
private readonly HashSet<string> _testingSystems = [];
protected override async Task OnInitializedAsync()
{
@@ -156,9 +227,87 @@
existing.TimerHour = _exportSettings.TimerHour;
existing.TimerMinute = _exportSettings.TimerMinute;
existing.TimerEnabled = _exportSettings.TimerEnabled;
existing.SapUsername = _exportSettings.SapUsername;
existing.SapPassword = _exportSettings.SapPassword;
existing.Bi1Username = _exportSettings.Bi1Username;
existing.Bi1Password = _exportSettings.Bi1Password;
existing.SageUsername = _exportSettings.SageUsername;
existing.SagePassword = _exportSettings.SagePassword;
}
await db.SaveChangesAsync();
TimerService.Recalculate();
Snackbar.Add("Export Einstellungen gespeichert", Severity.Success);
}
private async Task TestCentralCredentials(string sourceSystem)
{
if (!_testingSystems.Add(sourceSystem))
return;
try
{
var username = GetCentralUsername(sourceSystem);
var password = GetCentralPassword(sourceSystem);
if (string.IsNullOrWhiteSpace(username) || string.IsNullOrWhiteSpace(password))
{
Snackbar.Add($"Für {sourceSystem} sind keine zentralen Zugangsdaten gepflegt.", Severity.Warning);
return;
}
using var db = await DbFactory.CreateDbContextAsync();
var site = await db.Sites
.Include(s => s.HanaServer)
.Where(s => (string.IsNullOrWhiteSpace(s.SourceSystem) ? "SAP" : s.SourceSystem) == sourceSystem)
.OrderBy(s => s.Land)
.FirstOrDefaultAsync();
if (site?.HanaServer is null)
{
Snackbar.Add($"Kein Standort mit Quellsystem {sourceSystem} und HANA-Verbindung gefunden.", Severity.Warning);
return;
}
var testServer = new HanaServer
{
Name = $"{sourceSystem} Central Test",
Host = site.HanaServer.Host,
Port = site.HanaServer.Port,
Username = username.Trim(),
Password = password.Trim(),
DatabaseName = site.HanaServer.DatabaseName,
UseSsl = site.HanaServer.UseSsl,
ValidateCertificate = site.HanaServer.ValidateCertificate,
AdditionalParams = site.HanaServer.AdditionalParams
};
var result = await Task.Run(() => HanaService.TestConnectionDetailed(testServer));
if (result.Success)
{
Snackbar.Add($"{sourceSystem}: Verbindung erfolgreich über Standort '{site.Land}'.", Severity.Success);
}
else
{
Snackbar.Add($"{sourceSystem}: {result.ExceptionType} - {result.ErrorMessage}", Severity.Error);
}
}
finally
{
_testingSystems.Remove(sourceSystem);
}
}
private string GetCentralUsername(string sourceSystem) => sourceSystem switch
{
"BI1" => _exportSettings.Bi1Username,
"SAGE" => _exportSettings.SageUsername,
_ => _exportSettings.SapUsername
};
private string GetCentralPassword(string sourceSystem) => sourceSystem switch
{
"BI1" => _exportSettings.Bi1Password,
"SAGE" => _exportSettings.SagePassword,
_ => _exportSettings.SapPassword
};
}
@@ -141,11 +141,18 @@
<MudSelectItem Value="system">@system</MudSelectItem>
}
</MudSelect>
<MudTextField @bind-Value="_editingSite.UsernameOverride" Label="Username Override"
HelperText="Optional. Wenn leer, wird der zentrale Username des Quellsystems verwendet." />
<MudTextField @bind-Value="_editingSite.PasswordOverride" Label="Password Override" InputType="InputType.Password"
HelperText="Optional. Wenn leer, wird das zentrale Passwort des Quellsystems verwendet." />
<MudCheckBox @bind-Value="_editingSite.IsActive" Label="Aktiv" />
<MudDivider Class="my-4" />
<MudText Typo="Typo.h6" Class="mb-2">HANA-Verbindung</MudText>
<MudAlert Severity="Severity.Info" Dense="true" Variant="Variant.Outlined" Class="mb-3">
Host, Port und technische HANA-Parameter kommen von dieser Verbindung. Username und Password hier dienen nur noch als Fallback für bestehende Einträge.
</MudAlert>
<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
@@ -301,6 +308,8 @@
TSC = site.TSC,
Land = site.Land,
SourceSystem = string.IsNullOrWhiteSpace(site.SourceSystem) ? "SAP" : site.SourceSystem,
UsernameOverride = site.UsernameOverride,
PasswordOverride = site.PasswordOverride,
IsActive = site.IsActive
};
_editingSiteServer = site.HanaServer is null
@@ -329,6 +338,8 @@
existing.TSC = _editingSite.TSC;
existing.Land = _editingSite.Land;
existing.SourceSystem = _editingSite.SourceSystem;
existing.UsernameOverride = _editingSite.UsernameOverride;
existing.PasswordOverride = _editingSite.PasswordOverride;
existing.IsActive = _editingSite.IsActive;
}
}
@@ -403,6 +414,8 @@
_editingSiteServer.Name = string.IsNullOrWhiteSpace(_editingSiteServer.Name)
? $"{_editingSite.Land} HANA".Trim()
: _editingSiteServer.Name.Trim();
_editingSite.UsernameOverride = _editingSite.UsernameOverride.Trim();
_editingSite.PasswordOverride = _editingSite.PasswordOverride.Trim();
_editingSiteServer.Host = _editingSiteServer.Host.Trim();
_editingSiteServer.Username = _editingSiteServer.Username.Trim();
_editingSiteServer.DatabaseName = _editingSiteServer.DatabaseName.Trim();
@@ -7,4 +7,10 @@ public class ExportSettings
public int TimerHour { get; set; } = 3;
public int TimerMinute { get; set; }
public bool TimerEnabled { get; set; } = true;
public string SapUsername { get; set; } = string.Empty;
public string SapPassword { get; set; } = string.Empty;
public string Bi1Username { get; set; } = string.Empty;
public string Bi1Password { get; set; } = string.Empty;
public string SageUsername { get; set; } = string.Empty;
public string SagePassword { get; set; } = string.Empty;
}
+4 -1
View File
@@ -97,7 +97,10 @@ public class HanaServer
if (string.IsNullOrWhiteSpace(value))
return string.Empty;
if (Uri.TryCreate(value, UriKind.Absolute, out var uri))
// Treat plain "host:port" values as HANA ServerNode, not as a URI scheme.
// Only parse as URI when an explicit scheme is present.
if (value.Contains("://", StringComparison.Ordinal) &&
Uri.TryCreate(value, UriKind.Absolute, out var uri))
{
return uri.IsDefaultPort ? uri.Host : $"{uri.Host}:{uri.Port}";
}
+4
View File
@@ -24,5 +24,9 @@ public class Site
[Required]
public string SourceSystem { get; set; } = "SAP";
public string UsernameOverride { get; set; } = string.Empty;
public string PasswordOverride { get; set; } = string.Empty;
public bool IsActive { get; set; } = true;
}
@@ -29,6 +29,14 @@ public class DatabaseInitializationService : IDatabaseInitializationService
AddColumnIfMissing(db, "HanaServers", "ValidateCertificate", "INTEGER NOT NULL DEFAULT 0");
AddColumnIfMissing(db, "HanaServers", "AdditionalParams", "TEXT NOT NULL DEFAULT ''");
AddColumnIfMissing(db, "Sites", "SourceSystem", "TEXT NOT NULL DEFAULT 'SAP'");
AddColumnIfMissing(db, "Sites", "UsernameOverride", "TEXT NOT NULL DEFAULT ''");
AddColumnIfMissing(db, "Sites", "PasswordOverride", "TEXT NOT NULL DEFAULT ''");
AddColumnIfMissing(db, "ExportSettings", "SapUsername", "TEXT NOT NULL DEFAULT ''");
AddColumnIfMissing(db, "ExportSettings", "SapPassword", "TEXT NOT NULL DEFAULT ''");
AddColumnIfMissing(db, "ExportSettings", "Bi1Username", "TEXT NOT NULL DEFAULT ''");
AddColumnIfMissing(db, "ExportSettings", "Bi1Password", "TEXT NOT NULL DEFAULT ''");
AddColumnIfMissing(db, "ExportSettings", "SageUsername", "TEXT NOT NULL DEFAULT ''");
AddColumnIfMissing(db, "ExportSettings", "SagePassword", "TEXT NOT NULL DEFAULT ''");
EnsureTransformationTable(db);
}
@@ -49,10 +49,11 @@ public class SiteExportService : ISiteExportService
using var db = await _dbFactory.CreateDbContextAsync();
var settings = await db.ExportSettings.FirstOrDefaultAsync() ?? new ExportSettings();
var spConfig = await db.SharePointConfigs.FirstOrDefaultAsync();
var exportServer = BuildEffectiveServer(site, settings);
updateStatus?.Invoke("HANA Abfrage...");
var records = await Task.Run(() => _hanaService.GetSalesRecords(
site.HanaServer, site.Schema, site.TSC, site.Land, settings.DateFilter));
exportServer, site.Schema, site.TSC, site.Land, settings.DateFilter));
updateStatus?.Invoke("Transformationen anwenden...");
var rules = await db.FieldTransformationRules
@@ -111,4 +112,53 @@ public class SiteExportService : ISiteExportService
};
}
}
private static HanaServer BuildEffectiveServer(Site site, ExportSettings settings)
{
if (site.HanaServer is null)
throw new InvalidOperationException($"Standort '{site.Land}' hat keinen HANA-Server.");
var sourceSystem = string.IsNullOrWhiteSpace(site.SourceSystem) ? "SAP" : site.SourceSystem.Trim().ToUpperInvariant();
var inheritedUsername = GetCentralUsername(sourceSystem, settings);
var inheritedPassword = GetCentralPassword(sourceSystem, settings);
return new HanaServer
{
Id = site.HanaServer.Id,
Name = site.HanaServer.Name,
Host = site.HanaServer.Host,
Port = site.HanaServer.Port,
Username = FirstNonEmpty(site.UsernameOverride, inheritedUsername, site.HanaServer.Username),
Password = FirstNonEmpty(site.PasswordOverride, inheritedPassword, site.HanaServer.Password),
DatabaseName = site.HanaServer.DatabaseName,
UseSsl = site.HanaServer.UseSsl,
ValidateCertificate = site.HanaServer.ValidateCertificate,
AdditionalParams = site.HanaServer.AdditionalParams
};
}
private static string GetCentralUsername(string sourceSystem, ExportSettings settings) => sourceSystem switch
{
"BI1" => settings.Bi1Username,
"SAGE" => settings.SageUsername,
_ => settings.SapUsername
};
private static string GetCentralPassword(string sourceSystem, ExportSettings settings) => sourceSystem switch
{
"BI1" => settings.Bi1Password,
"SAGE" => settings.SagePassword,
_ => settings.SapPassword
};
private static string FirstNonEmpty(params string[] values)
{
foreach (var value in values)
{
if (!string.IsNullOrWhiteSpace(value))
return value.Trim();
}
return string.Empty;
}
}