using System.Collections.Generic; using System.ComponentModel; using System.IO; using System.Xml.Serialization; namespace PolyTraderSharp.Models { 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("Threema Notifications")] [DisplayName("Threema Enabled")] [Description("Enable or disable Threema notifications.")] public bool ThreemaEnabled { get; set; } = true; [Category("Threema Notifications")] [DisplayName("Gateway ID")] [Description("The Threema Gateway ID (e.g. *3MAGW01).")] public string ThreemaGatewayId { get; set; } = "*3MAGW01"; [Category("Threema Notifications")] [DisplayName("Gateway Secret")] [Description("The secret for the Threema Gateway integration.")] public string ThreemaSecret { get; set; } = ""; [Category("Threema Notifications")] [DisplayName("Private Key")] [Description("The Private Key (hex) for End-to-End encryption.")] public string ThreemaPrivateKey { get; set; } = ""; [Category("Threema Notifications")] [DisplayName("Group ID")] [Description("The Threema Group ID to send messages to.")] public string ThreemaGroupId { get; set; } = ""; [Category("Threema Notifications")] [DisplayName("Webhook Port")] [Description("The local port to listen on for incoming Threema messages (e.g. 8080).")] public int ThreemaWebhookPort { get; set; } = 8080; [Category("Threema Notifications")] [DisplayName("Report Interval (Hours)")] [Description("Interval for the automatic summary report.")] public int ThreemaReportIntervalHours { get; set; } = 6; [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 externen " + "Watchdog-Server. 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("Watchdog URL")] [Description("Basis-URL des Watchdog-Servers, z.B. https://watchdog.example.de. " + "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); } } }