- Shutdown-Event sendet stopped_graceful statt stopping: der Server kennt "stopping" im Router, aber event_log.kind ist ein ENUM ohne diesen Wert. Folge war HTTP 500 nach dem Zustandswechsel, das Event fehlte in der Historie. - Default-Source auf "Predictalytics" korrigiert (Schreibweise des bereits angelegten Monitors auf dem Server). Heartbeat, Metrics-Payload, Lizenzvalidierung und der Startpfad ueber den Cache sind end-to-end gegen die Produktivserver getestet. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
138 lines
5.0 KiB
C#
138 lines
5.0 KiB
C#
using System.ComponentModel;
|
||
using System.Text.Json;
|
||
|
||
namespace Predictalytics.WinFormsHost;
|
||
|
||
public class AppSettings
|
||
{
|
||
private const string FileName = "settings.json";
|
||
|
||
[Category("Webserver")]
|
||
[DisplayName("Port")]
|
||
[Description("Der Port, über den die WebUI und API erreichbar sind.")]
|
||
[DefaultValue(5000)]
|
||
public int WebserverPort { get; set; } = 5000;
|
||
|
||
[Category("Webserver")]
|
||
[DisplayName("Database Debug")]
|
||
[Description("Wenn aktiv, werden detaillierte Verbindungsinformationen im Terminal angezeigt.")]
|
||
[DefaultValue(false)]
|
||
public bool DbConnectionDebug { get; set; } = false;
|
||
|
||
private string _egressChannelsText = "";
|
||
|
||
[Category("Egress (Proxy/IP)")]
|
||
[DisplayName("Egress Channels")]
|
||
[Description("Liste der Egress-Kanäle im Format: id|type|value (Zeilengetrennt). Beispiel: prox-1|Proxy|http://user:pass@proxy:8080\nip-1|SourceIp|192.168.1.100")]
|
||
[Editor("System.ComponentModel.Design.MultilineStringEditor, System.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", typeof(System.Drawing.Design.UITypeEditor))]
|
||
public string EgressChannelsText { get => _egressChannelsText; set => _egressChannelsText = value ?? ""; }
|
||
|
||
[Category("Watchdog")]
|
||
[DisplayName("Enabled")]
|
||
[Description("Sendet periodische Heartbeats an den externen Watchdog-Server (Dead-Man's-Switch). Benötigt einen API Key.")]
|
||
[DefaultValue(true)]
|
||
public bool WatchdogEnabled { get; set; } = true;
|
||
|
||
[Category("Watchdog")]
|
||
[DisplayName("Server URL")]
|
||
[Description("Basis-URL des Watchdog-Servers.")]
|
||
[DefaultValue("https://watchdog.mhdf.de")]
|
||
public string WatchdogUrl { get; set; } = "https://watchdog.mhdf.de";
|
||
|
||
[Category("Watchdog")]
|
||
[DisplayName("API Key")]
|
||
[Description("Shared Key oder Agent-Token des Watchdog-Servers (X-Watchdog-Key). Ohne Key werden keine Heartbeats gesendet.")]
|
||
[PasswordPropertyText(true)]
|
||
public string WatchdogApiKey { get; set; } = "";
|
||
|
||
[Category("Watchdog")]
|
||
[DisplayName("Source")]
|
||
[Description("Eindeutiger Monitor-Name dieses Dienstes im Watchdog-Dashboard.")]
|
||
[DefaultValue("Predictalytics")]
|
||
public string WatchdogSource { get; set; } = "Predictalytics";
|
||
|
||
[Category("Watchdog")]
|
||
[DisplayName("Instance")]
|
||
[Description("Instanz-Kennung, falls mehrere Predictalytics-Instanzen laufen.")]
|
||
[DefaultValue("default")]
|
||
public string WatchdogInstance { get; set; } = "default";
|
||
|
||
[Category("Watchdog")]
|
||
[DisplayName("Interval (Sekunden)")]
|
||
[Description("Sende-Takt der Heartbeats. Der Watchdog alarmiert, wenn ~1,5× dieses Intervall + 30 s ohne Heartbeat vergehen.")]
|
||
[DefaultValue(60)]
|
||
public int WatchdogIntervalSeconds { get; set; } = 60;
|
||
|
||
private string _dbServer = "localhost";
|
||
private string _dbName = "";
|
||
private string _dbUser = "";
|
||
private string _dbPassword = "";
|
||
|
||
[Category("Database")]
|
||
[DisplayName("Server")]
|
||
public string DbServer { get => _dbServer; set => _dbServer = string.IsNullOrWhiteSpace(value) ? _dbServer : value.Trim(); }
|
||
|
||
[Category("Database")]
|
||
[DisplayName("Database")]
|
||
public string DbName { get => _dbName; set => _dbName = string.IsNullOrWhiteSpace(value) ? _dbName : value.Trim(); }
|
||
|
||
[Category("Database")]
|
||
[DisplayName("User")]
|
||
public string DbUser { get => _dbUser; set => _dbUser = string.IsNullOrWhiteSpace(value) ? _dbUser : value.Trim(); }
|
||
|
||
[Category("Database")]
|
||
[DisplayName("Password")]
|
||
[PasswordPropertyText(true)]
|
||
public string DbPassword { get => _dbPassword; set => _dbPassword = string.IsNullOrWhiteSpace(value) ? _dbPassword : value.Trim(); }
|
||
|
||
[Browsable(false)]
|
||
public string ConnectionString
|
||
{
|
||
get
|
||
{
|
||
var builder = new MySqlConnector.MySqlConnectionStringBuilder
|
||
{
|
||
Server = DbServer?.Trim(),
|
||
Database = DbName?.Trim(),
|
||
UserID = DbUser?.Trim(),
|
||
Password = DbPassword?.Trim(),
|
||
AllowPublicKeyRetrieval = true,
|
||
SslMode = MySqlConnector.MySqlSslMode.None,
|
||
Pooling = true,
|
||
MinimumPoolSize = 0,
|
||
MaximumPoolSize = 100
|
||
};
|
||
return builder.ConnectionString;
|
||
}
|
||
}
|
||
|
||
public static AppSettings Load()
|
||
{
|
||
var filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, FileName);
|
||
if (!File.Exists(filePath))
|
||
{
|
||
var settings = new AppSettings();
|
||
settings.Save();
|
||
return settings;
|
||
}
|
||
|
||
try
|
||
{
|
||
var json = File.ReadAllText(filePath);
|
||
return JsonSerializer.Deserialize<AppSettings>(json) ?? new AppSettings();
|
||
}
|
||
catch
|
||
{
|
||
return new AppSettings();
|
||
}
|
||
}
|
||
|
||
public void Save()
|
||
{
|
||
var filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, FileName);
|
||
var options = new JsonSerializerOptions { WriteIndented = true };
|
||
var json = JsonSerializer.Serialize(this, options);
|
||
File.WriteAllText(filePath, json);
|
||
}
|
||
}
|