using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Xml.Serialization;
namespace PolyTraderSharp.Models
{
/// Farbschema der Oberfläche.
public enum AppThemeVariant
{
/// Folgt der Einstellung des Betriebssystems.
System,
Hell,
Dunkel
}
public class ServerSettings
{
[Category("Module")]
[DisplayName("Deaktivierte Module")]
[Description("Namen der Module (z.B. \"CopyTrading\"), die beim App-Start NICHT geladen werden. " +
"Leer = alle Module aktiv. Änderungen greifen nach dem nächsten Neustart. " +
"Wird i.d.R. über das Dashboard gepflegt.")]
[XmlArray("DisabledModules")]
[XmlArrayItem("Module")]
public List DisabledModules { get; set; } = new();
[Category("Anzeige")]
[DisplayName("Farbschema")]
[Description("Hell, Dunkel oder der Einstellung des Betriebssystems folgen. " +
"Lässt sich jederzeit über den Umschalter in der Fensterleiste wechseln; " +
"der zuletzt gewählte Stand wird hier gespeichert.")]
public AppThemeVariant Theme { get; set; } = AppThemeVariant.System;
[Category("Anzeige")]
[DisplayName("Zeitzone")]
[Description("Zeitzone für Anzeige und Logdatei-Tagesgrenzen, IANA-Schreibweise (z.B. \"Europe/Berlin\", " +
"\"America/New_York\"). Leer = Zeitzone des Systems. Wird üblicherweise EINMALIG bei der " +
"Installation gesetzt: eine spätere Änderung verschiebt Tagesgrenzen in Logs und Auswertungen. " +
"Greift erst nach einem Neustart.")]
public string ApplicationTimeZoneId { get; set; } = PolyTraderSharp.Services.AppTimeZone.DefaultId;
[Category("Mullvad VPN")]
[DisplayName("VPN Enabled")]
[Description("Enable or disable automatic VPN rotation.")]
public bool VpnEnabled { get; set; } = false;
[Category("Mullvad VPN")]
[DisplayName("Mullvad Account")]
[Description("Account ID for Mullvad VPN. In server_settings.xml (gitignored) hinterlegen – NICHT im Code committen.")]
public string MullvadAccount { get; set; } = ""; // F3: Secret nicht mehr im Quellcode
[Category("Mullvad VPN")]
[DisplayName("VPN Location")]
[Description("Target VPN location (e.g. cz).")]
public string VpnLocation { get; set; } = "cz";
[Category("Mullvad VPN")]
[DisplayName("Mullvad CLI Path")]
[Description("Path to mullvad.exe")]
public string MullvadCliPath { get; set; } = @"C:\Program Files\Mullvad VPN\resources\mullvad.exe";
[Category("Blockchain Listener")]
[DisplayName("Enable Blockchain Listener")]
[Description("If true, connects to Alchemy WSS for faster on-chain signal detection.")]
public bool EnableBlockchainListener { get; set; } = true;
[Category("Blockchain Listener")]
[DisplayName("Polygon RPC URL")]
[Description("RPC-/WSS-URL inkl. Alchemy-API-Key. In server_settings.xml (gitignored) hinterlegen – der Key gehört NICHT in den Quellcode/Git. Format: wss://polygon-mainnet.g.alchemy.com/v2/")]
public string PolygonRpcUrl { get; set; } = ""; // F3: Alchemy-Key nicht mehr im Quellcode
[Category("Polymarket WebSockets")]
[DisplayName("Use Polymarket WebSockets")]
[Description("If true, connects to Polymarket WSS for live market prices and user events.")]
public bool UsePolymarketWebsockets { get; set; } = false;
[Category("Watchdog Monitoring")]
[DisplayName("Watchdog Enabled")]
[Description("Wenn true, sendet PolyTrader im festen Takt einen Heartbeat an den Watchdog " +
"des Deploymentcenters. Bleiben die Heartbeats aus, alarmiert der Watchdog " +
"(Dead-Man's-Switch). Ein nicht erreichbarer Watchdog beeinträchtigt PolyTrader NIE.")]
public bool WatchdogEnabled { get; set; } = false;
[Category("Watchdog Monitoring")]
[DisplayName("Deploymentcenter-URL")]
[Description("Basis-URL des Deploymentcenters, z.B. https://dc.mhdf.de (ersetzt den früheren " +
"eigenständigen Watchdog-Server). HTTPS ist Pflicht – http:// wird nur für localhost " +
"akzeptiert, damit der Agent-Token nicht im Klartext über das Netz geht.")]
public string WatchdogUrl { get; set; } = "";
///
/// Agent-Token für den Header X-Watchdog-Key. Bewusst NICHT im PropertyGrid sichtbar:
/// Setzen erfolgt über den Button „Watchdog-Token setzen …" mit maskierter Eingabe, damit das
/// Secret nicht offen im Fenster steht und bei gesetztem Master-Key verschlüsselt
/// (enc:v1:…) in der gitignorierten server_settings.xml landet.
///
[Browsable(false)]
public string WatchdogToken { get; set; } = "";
[Category("Watchdog Monitoring")]
[DisplayName("Agent-Token")]
[Description("Status des Agent-Tokens. Setzen/Ändern über den Button „Watchdog-Token setzen …" +
"\" in der Werkzeugleiste (maskierte Eingabe).")]
[ReadOnly(true)]
[XmlIgnore]
public string WatchdogTokenStatus =>
string.IsNullOrWhiteSpace(WatchdogToken)
? "nicht gesetzt"
: (WatchdogToken.StartsWith("enc:v1:", System.StringComparison.Ordinal)
? "gesetzt (verschlüsselt)"
: "gesetzt (Klartext)");
[Category("Watchdog Monitoring")]
[DisplayName("Source")]
[Description("Dienst-Kennung im Watchdog (muss dem dort angelegten Monitor entsprechen), z.B. \"polytrader\".")]
public string WatchdogSource { get; set; } = "polytrader";
[Category("Watchdog Monitoring")]
[DisplayName("Instance")]
[Description("Instanz-Kennung, falls mehrere PolyTrader-Instanzen laufen, z.B. \"prod-1\".")]
public string WatchdogInstance { get; set; } = "prod-1";
[Category("Watchdog Monitoring")]
[DisplayName("Heartbeat-Intervall (Sekunden)")]
[Description("Sende-Takt des Heartbeats. Der Watchdog leitet daraus seine Staleness-Frist ab " +
"(Intervall × Faktor + Grace). Minimum 10 s, Empfehlung 60 s.")]
public int WatchdogIntervalSeconds { get; set; } = 60;
///
/// Lizenzschlüssel (LicenseLabrador). Wie der Watchdog-Token bewusst NICHT im PropertyGrid
/// sichtbar; gepflegt über den Button „Lizenzschlüssel setzen …" bzw. den Startdialog. Bei
/// gesetztem Master-Key verschlüsselt (enc:v1:…) in der gitignorierten server_settings.xml.
///
[Browsable(false)]
public string LicenseKey { get; set; } = "";
[Category("Lizenz")]
[DisplayName("Lizenzschlüssel")]
[Description("Status des Lizenzschlüssels. Setzen/Ändern über den Button „Lizenzschlüssel setzen …\" " +
"in der Werkzeugleiste bzw. beim Programmstart, falls keine gültige Lizenz vorliegt.")]
[ReadOnly(true)]
[XmlIgnore]
public string LicenseKeyStatus =>
string.IsNullOrWhiteSpace(LicenseKey)
? "nicht gesetzt"
: (LicenseKey.StartsWith("enc:v1:", System.StringComparison.Ordinal)
? "gesetzt (verschlüsselt)"
: "gesetzt (Klartext)");
public static ServerSettings Load(string path)
{
if (!File.Exists(path))
return new ServerSettings();
try
{
var serializer = new XmlSerializer(typeof(ServerSettings));
using var fs = new FileStream(path, FileMode.Open);
return (ServerSettings?)serializer.Deserialize(fs) ?? new ServerSettings();
}
catch
{
return new ServerSettings();
}
}
public void Save(string path)
{
var serializer = new XmlSerializer(typeof(ServerSettings));
using var fs = new FileStream(path, FileMode.Create);
serializer.Serialize(fs, this);
}
}
}