Paralleles WebGUI mit Kestrel, Token-Sicherung und SSE-Logs implementiert

This commit is contained in:
Richard
2026-09-07 21:55:51 +02:00
parent 28b3aedec7
commit 10f928eb4e
36 changed files with 3114 additions and 266 deletions
@@ -15,6 +15,7 @@ using PolyTrader.Core.Security;
using PolyTraderSharp;
using PolyTraderSharp.Models;
using PolyTraderSharp.Services;
using PolyTrader.Web.Configuration;
namespace PolyTrader.App.Avalonia.Views
{
@@ -36,12 +37,14 @@ namespace PolyTrader.App.Avalonia.Views
private MullvadVpnService? _vpn;
private TerminalLogger? _logger;
private DeploymentcenterOptions? _dcOptions;
private WebUiOptions _webOptions = new();
public SettingsWindow() => AvaloniaXamlLoader.Load(this);
public SettingsWindow(IModuleUiHost host, IAccountRepository accountRepo, TradingState state,
TerminalLogger logger, WatchdogHeartbeatService? watchdog = null,
MullvadVpnService? vpn = null, DeploymentcenterOptions? dcOptions = null) : this()
MullvadVpnService? vpn = null, DeploymentcenterOptions? dcOptions = null,
WebUiOptions? webOptions = null) : this()
{
_accountRepo = accountRepo;
_state = state;
@@ -49,6 +52,7 @@ namespace PolyTrader.App.Avalonia.Views
_watchdog = watchdog;
_vpn = vpn;
_dcOptions = dcOptions;
_webOptions = webOptions ?? Program.LoadWebUiOptions();
var grid = this.FindControl<DataGrid>("gridAccounts")!;
grid.ItemsSource = _accounts;
@@ -68,8 +72,14 @@ namespace PolyTrader.App.Avalonia.Views
this.FindControl<Button>("btnAccDelete")!.Click += async (_, _) => await DeleteAccountAsync();
this.FindControl<Button>("btnAccSave")!.Click += (_, _) => SaveSelectedAccount();
this.FindControl<Button>("btnWebUiGenToken")!.Click += (_, _) => GenerateWebUiToken();
this.FindControl<Button>("btnWebUiToggleShowToken")!.Click += (_, _) => ToggleWebUiTokenVisibility();
this.FindControl<Button>("btnWebUiCopyToken")!.Click += async (_, _) => await CopyWebUiTokenAsync();
this.FindControl<Button>("btnSaveWebUi")!.Click += (_, _) => SaveWebUiSettings();
ReloadServerSettings();
LoadAccounts();
LoadWebUiSettings();
UpdateMasterKeyButtonState();
}
@@ -422,5 +432,98 @@ namespace PolyTrader.App.Avalonia.Views
btn.IsEnabled = true;
}
}
// ===== WebUI-Einstellungen =====
private void LoadWebUiSettings()
{
_webOptions = Program.LoadWebUiOptions();
var chkEnabled = this.FindControl<CheckBox>("chkWebUiEnabled")!;
var numPort = this.FindControl<NumericUpDown>("numWebUiPort")!;
var chkLocal = this.FindControl<CheckBox>("chkWebUiLocalOnly")!;
var txtToken = this.FindControl<TextBox>("txtWebUiToken")!;
var lblStatus = this.FindControl<TextBlock>("lblWebUiStatus")!;
var lblUrl = this.FindControl<TextBlock>("lblWebUiUrl")!;
chkEnabled.IsChecked = _webOptions.Enabled;
numPort.Value = _webOptions.Port;
chkLocal.IsChecked = _webOptions.LocalOnly;
txtToken.Text = _webOptions.AuthToken ?? string.Empty;
if (_webOptions.Enabled)
{
string ip = _webOptions.LocalOnly ? "127.0.0.1" : "0.0.0.0";
lblStatus.Text = $"Aktiv (lauscht auf {ip}:{_webOptions.Port})";
lblUrl.Text = $"http://{(_webOptions.LocalOnly ? "localhost" : "0.0.0.0")}:{_webOptions.Port}";
}
else
{
lblStatus.Text = "Deaktiviert (WebUi:Enabled = false)";
lblUrl.Text = "—";
}
}
private void GenerateWebUiToken()
{
string newToken = WebUiConfigManager.GenerateSecureToken();
var txtToken = this.FindControl<TextBox>("txtWebUiToken")!;
txtToken.Text = newToken;
_webOptions.AuthToken = newToken;
WebUiConfigManager.SaveToLocalSettings(_webOptions);
_logger?.Info("🔐 WebUI: Neues Zugangs-Token im Einstellungsfenster generiert und gespeichert.");
SetWebUiSaveStatus("Neues Token generiert & gespeichert.");
}
private void ToggleWebUiTokenVisibility()
{
var txt = this.FindControl<TextBox>("txtWebUiToken")!;
var btn = this.FindControl<Button>("btnWebUiToggleShowToken")!;
if (txt.PasswordChar == '●')
{
txt.PasswordChar = '\0';
btn.Content = "Ausblenden";
}
else
{
txt.PasswordChar = '●';
btn.Content = "Anzeigen";
}
}
private async System.Threading.Tasks.Task CopyWebUiTokenAsync()
{
var txt = this.FindControl<TextBox>("txtWebUiToken")!;
if (string.IsNullOrEmpty(txt.Text)) return;
var topLevel = TopLevel.GetTopLevel(this);
if (topLevel?.Clipboard != null)
{
await topLevel.Clipboard.SetTextAsync(txt.Text);
SetWebUiSaveStatus("Token in Zwischenablage kopiert.");
}
}
private void SaveWebUiSettings()
{
var chkEnabled = this.FindControl<CheckBox>("chkWebUiEnabled")!;
var numPort = this.FindControl<NumericUpDown>("numWebUiPort")!;
var chkLocal = this.FindControl<CheckBox>("chkWebUiLocalOnly")!;
var txtToken = this.FindControl<TextBox>("txtWebUiToken")!;
_webOptions.Enabled = chkEnabled.IsChecked ?? true;
_webOptions.Port = (int)(numPort.Value ?? 5001);
_webOptions.LocalOnly = chkLocal.IsChecked ?? true;
_webOptions.AuthToken = txtToken.Text?.Trim() ?? string.Empty;
WebUiConfigManager.SaveToLocalSettings(_webOptions);
_logger?.Info($"WebUI-Einstellungen in appsettings.Local.json gespeichert (Port {_webOptions.Port}, LocalOnly: {_webOptions.LocalOnly}).");
SetWebUiSaveStatus("Gespeichert in appsettings.Local.json (Port-/Netzwerkänderungen greifen nach Neustart).");
LoadWebUiSettings();
}
private void SetWebUiSaveStatus(string msg)
{
var lbl = this.FindControl<TextBlock>("lblWebUiSaveStatus")!;
lbl.Text = $"✓ {msg}";
}
}
}