Eigenständiges token-geschütztes Verwaltungs-WebGUI (Admin WebGUI) hinzugefügt
- Unabhängige Kestrel-Instanz auf Port 5001 (vollständig getrennt vom Analyse-Webserver) - Admin API Endpunkte ohne Swagger-Exposition - Token-Gatekeeper mit sofortigem Login-Overlay bei unautorisiertem Zugriff - Live-Terminal via SSE-Log-Streaming und Log-Download-Übersicht - Steuerung von Workern, Analyse-Webserver, Wartungsaktionen und Einstellungen - Nahtlose Integration in Avalonia Desktop-Shell und Linux Headless-Modus - Unit-Tests für Admin-Filter, Optionen und Log-Puffer
This commit is contained in:
@@ -18,7 +18,6 @@ public sealed partial class MainWindowViewModel : ObservableObject
|
||||
private const int MaxLogLines = 500;
|
||||
|
||||
private readonly PredictalyticsHost _host;
|
||||
private CancellationTokenSource? _workerCts;
|
||||
private DcHeartbeatService? _heartbeat;
|
||||
private double? _lastDbSizeMb;
|
||||
|
||||
@@ -79,6 +78,22 @@ public sealed partial class MainWindowViewModel : ObservableObject
|
||||
|
||||
RestartHeartbeat();
|
||||
|
||||
if (Options.AdminServerEnabled)
|
||||
{
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
await _host.StartAdminServerAsync();
|
||||
Log.Information("Verwaltungs-WebGUI erreichbar unter {Url}", Options.AdminServerUrl);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "Verwaltungs-WebGUI konnte nicht gestartet werden");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (Options.DcUpdateCheckEnabled)
|
||||
{
|
||||
_ = CheckForUpdatesAsync(silent: true);
|
||||
@@ -292,23 +307,17 @@ public sealed partial class MainWindowViewModel : ObservableObject
|
||||
// ─── Befehle ───
|
||||
|
||||
[RelayCommand]
|
||||
private async Task ToggleServerAsync()
|
||||
private void ToggleServer()
|
||||
{
|
||||
if (!_host.WorkersRunning)
|
||||
{
|
||||
_workerCts = new CancellationTokenSource();
|
||||
Log.Information("🚀 Starte Hintergrund-Worker...");
|
||||
try
|
||||
{
|
||||
await _host.StartWorkersAsync(_workerCts.Token);
|
||||
}
|
||||
catch (OperationCanceledException) { }
|
||||
catch (Exception ex) { Log.Error(ex, "Worker error"); }
|
||||
_host.StartWorkers();
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Information("⏹ Stoppe Hintergrund-Worker...");
|
||||
_workerCts?.Cancel();
|
||||
_host.StopWorkers();
|
||||
Log.Information("Worker gestoppt.");
|
||||
}
|
||||
UpdateStatus();
|
||||
@@ -354,6 +363,17 @@ public sealed partial class MainWindowViewModel : ObservableObject
|
||||
[RelayCommand]
|
||||
private void OpenWebUi() => OpenInShell(Options.WebserverUrl);
|
||||
|
||||
[RelayCommand]
|
||||
private void OpenAdminWebUi() => OpenInShell(Options.AdminServerUrl);
|
||||
|
||||
[RelayCommand]
|
||||
private void GenerateAdminToken()
|
||||
{
|
||||
Options.AdminToken = PredictalyticsOptions.GenerateSecureToken();
|
||||
OnPropertyChanged(nameof(Options));
|
||||
Log.Information("Neuer Admin-Token generiert. Zum Übernehmen 'Speichern & übernehmen' klicken.");
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void OpenLogFolder()
|
||||
{
|
||||
@@ -476,7 +496,8 @@ public sealed partial class MainWindowViewModel : ObservableObject
|
||||
_heartbeat?.NotifyStopping();
|
||||
_heartbeat?.Dispose();
|
||||
_heartbeat = null;
|
||||
_workerCts?.Cancel();
|
||||
_host.StopWorkers();
|
||||
try { _host.StopWebServerAsync().GetAwaiter().GetResult(); } catch { /* beendet sich ohnehin */ }
|
||||
try { _host.StopAdminServerAsync().GetAwaiter().GetResult(); } catch { /* beendet sich ohnehin */ }
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user