Deploymentcenter-Integration (Schritte 0-8 des Konzepts)
Bindet Lizenz, Watchdog, Fehler-Stream, UpdateService und Erstinstallation an
das Deploymentcenter 2.5.1 an. Einbauort ist IBKRTrader.Hosting - den Host
teilen sich Shell und Daemon.
Projekt-Befunde aus dem Konzept vorab bereinigt:
P1 Echte DB-Zugangsdaten als Vorgabewerte in AppSettings -> Platzhalter.
Das alte Passwort steht weiterhin in der Git-Historie und ist als
kompromittiert zu behandeln (Rotation ist Nutzer-Aktion).
P2 AppPaths fiel unter Windows auf /etc/ibkrtrader zurueck, was .NET zu
C:\etc\ibkrtrader aufloest. Jetzt %ProgramData%\IBKRTrader.
P3 Globale Ausnahmebehandler (AppDomain / TaskScheduler) - vorher gab es
keinen Logeintrag, wenn der Prozess unbehandelt wegbrach.
P4 Version einmal zentral in Directory.Build.props statt zweimal hartkodiert.
Bewusste Abweichungen vom DC-Leitfaden, beide fuer ein handelndes System:
- Lizenz-Urteil fuehrt zum Sperrbetrieb (TradingEnabled=false) statt zu
Environment.Exit(1). Keine neuen Einstiege, aber Risiko-, Exit- und
Buchhaltungslogik laufen weiter.
- exitCurrentApp bleibt immer false; der Aufrufer beendet geordnet.
Das SDK haengt als Cross-Repo-ProjectReference am Schwester-Repo
Deploymentcenter (Interim, siehe Konzept 2.2). Damit ist P5 offen: die
Gitea-CI checkt das Schwester-Repo nicht aus und wird rot, bis der Bezug auf
ein NuGet-Paket umgestellt ist.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -15,8 +15,13 @@ namespace IBKRTrader.Core.Configuration;
|
||||
/// hat immer Vorrang, damit ein Betreiber die Ablage frei bestimmen kann.</item>
|
||||
/// <item>Das Verzeichnis der Binärdatei, <b>wenn dort geschrieben werden darf</b>. Das hält das
|
||||
/// bisherige Verhalten unter Windows und beim Entwickeln unter Linux unverändert.</item>
|
||||
/// <item>Sonst die FHS-Konvention: <c>/etc/ibkrtrader</c>, <c>/var/lib/ibkrtrader</c>,
|
||||
/// <c>/var/log/ibkrtrader</c>.</item>
|
||||
/// <item>Sonst plattformabhängig: unter Linux/macOS die FHS-Konvention
|
||||
/// (<c>/etc/ibkrtrader</c>, <c>/var/lib/ibkrtrader</c>, <c>/var/log/ibkrtrader</c>), unter
|
||||
/// Windows <c>%ProgramData%\IBKRTrader</c>. <b>Der FHS-Pfad darf hier nicht als
|
||||
/// plattformübergreifender Rückfall dienen</b> – .NET löst <c>/etc/ibkrtrader</c> unter
|
||||
/// Windows zu <c>C:\etc\ibkrtrader</c> auf. Genau dieser Fall tritt ein, sobald eine
|
||||
/// Installation nach <c>Program Files</c> zeigt, wo der Dienst- bzw. Nutzerkonto-Kontext
|
||||
/// keinen Schreibzugriff neben der Binärdatei hat.</item>
|
||||
/// </list>
|
||||
///
|
||||
/// <para>Die Prüfung läuft einmal beim ersten Zugriff; die Verzeichnisse werden dabei angelegt.</para>
|
||||
@@ -26,8 +31,8 @@ public static class AppPaths
|
||||
private const string AppFolder = "ibkrtrader";
|
||||
|
||||
private static readonly Lazy<string> _config = new(() => Resolve("IBKRTRADER_CONFIG_DIR", $"/etc/{AppFolder}"));
|
||||
private static readonly Lazy<string> _data = new(() => Resolve("IBKRTRADER_DATA_DIR", $"/var/lib/{AppFolder}"));
|
||||
private static readonly Lazy<string> _logs = new(() => Resolve("IBKRTRADER_LOG_DIR", $"/var/log/{AppFolder}", "Logs"));
|
||||
private static readonly Lazy<string> _data = new(() => Resolve("IBKRTRADER_DATA_DIR", $"/var/lib/{AppFolder}", windowsSubDir: "Data"));
|
||||
private static readonly Lazy<string> _logs = new(() => Resolve("IBKRTRADER_LOG_DIR", $"/var/log/{AppFolder}", "Logs", windowsSubDir: "Logs"));
|
||||
|
||||
/// <summary>Konfiguration und Schlüsselmaterial: <c>settings.json</c>, <c>master.key</c>, <c>openrouter.key</c>.</summary>
|
||||
public static string Config => _config.Value;
|
||||
@@ -48,13 +53,18 @@ public static class AppPaths
|
||||
public static string Describe() => $"config={Config}, data={Data}, logs={Logs}";
|
||||
|
||||
/// <param name="envVar">Umgebungsvariable, die alles überstimmt.</param>
|
||||
/// <param name="fhsFallback">FHS-Pfad, wenn neben der Binärdatei nicht geschrieben werden darf.</param>
|
||||
/// <param name="fhsFallback">FHS-Pfad unter Linux/macOS, wenn neben der Binärdatei nicht geschrieben werden darf.</param>
|
||||
/// <param name="localSubDir">
|
||||
/// Unterverzeichnis im Binärverzeichnis. Für Logs ist das <c>Logs/</c>; Konfiguration und Daten
|
||||
/// lagen bisher direkt daneben und bleiben dort, damit bestehende Installationen unverändert
|
||||
/// weiterlaufen.
|
||||
/// </param>
|
||||
private static string Resolve(string envVar, string fhsFallback, string? localSubDir = null)
|
||||
/// <param name="windowsSubDir">
|
||||
/// Unterverzeichnis unter <c>%ProgramData%\IBKRTrader</c>, wenn unter Windows neben der
|
||||
/// Binärdatei nicht geschrieben werden darf (z. B. Installation nach <c>Program Files</c>).
|
||||
/// <c>null</c> = direkt in <c>%ProgramData%\IBKRTrader</c>, wie bei der Konfiguration.
|
||||
/// </param>
|
||||
private static string Resolve(string envVar, string fhsFallback, string? localSubDir = null, string? windowsSubDir = null)
|
||||
{
|
||||
var fromEnv = Environment.GetEnvironmentVariable(envVar);
|
||||
if (!string.IsNullOrWhiteSpace(fromEnv))
|
||||
@@ -67,6 +77,16 @@ public static class AppPaths
|
||||
if (IsWritable(AppContext.BaseDirectory))
|
||||
return Ensure(local);
|
||||
|
||||
// Binärverzeichnis nicht beschreibbar - der FHS-Pfad taugt nur unter Linux/macOS als
|
||||
// Rückfall. Unter Windows löst .NET "/etc/ibkrtrader" zu "C:\etc\ibkrtrader" auf; der
|
||||
// richtige Rückfall dort ist das maschinenweite Anwendungsdatenverzeichnis.
|
||||
if (OperatingSystem.IsWindows())
|
||||
{
|
||||
var programData = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData);
|
||||
var winBase = Path.Combine(programData, "IBKRTrader");
|
||||
return Ensure(windowsSubDir is null ? winBase : Path.Combine(winBase, windowsSubDir));
|
||||
}
|
||||
|
||||
return Ensure(fhsFallback);
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ public class DatabaseSettings
|
||||
[Category("Database")]
|
||||
[DisplayName("Host")]
|
||||
[Description("MySQL-Serveradresse (Hostname oder IP)")]
|
||||
public string Host { get; set; } = "larx.your-database.de";
|
||||
public string Host { get; set; } = "your-db-host";
|
||||
|
||||
[Category("Database")]
|
||||
[DisplayName("Port")]
|
||||
@@ -21,18 +21,18 @@ public class DatabaseSettings
|
||||
[Category("Database")]
|
||||
[DisplayName("Database")]
|
||||
[Description("Name der MySQL-Datenbank")]
|
||||
public string Database { get; set; } = "bergisnu_db0";
|
||||
public string Database { get; set; } = "your-db-name";
|
||||
|
||||
[Category("Database")]
|
||||
[DisplayName("User")]
|
||||
[Description("MySQL-Benutzername")]
|
||||
public string User { get; set; } = "bergisnu_0";
|
||||
public string User { get; set; } = "your-db-user";
|
||||
|
||||
[Category("Database")]
|
||||
[DisplayName("Password")]
|
||||
[Description("MySQL-Passwort")]
|
||||
[PasswordPropertyText(true)]
|
||||
public string Password { get; set; } = "o5/A49+f}:Da";
|
||||
public string Password { get; set; } = "CHANGE_ME";
|
||||
|
||||
public string BuildConnectionString() =>
|
||||
$"Server={Host};Port={Port};Database={Database};" +
|
||||
@@ -299,6 +299,61 @@ public class SupervisorSettings
|
||||
public override string ToString() => Model;
|
||||
}
|
||||
|
||||
// ─── Deploymentcenter ────────────────────────────────────────────────────────
|
||||
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))]
|
||||
public class DeploymentcenterSettings
|
||||
{
|
||||
[Category("Deploymentcenter")]
|
||||
[DisplayName("API Token")]
|
||||
[Description("Authorization: Bearer <Token>. Ohne Token: keine Heartbeats, keine Fehlermeldungen.")]
|
||||
[PasswordPropertyText(true)]
|
||||
public string ApiToken { get; set; } = "";
|
||||
|
||||
[Category("Deploymentcenter")]
|
||||
[DisplayName("Heartbeat aktiv")]
|
||||
[Description("Dead-Man's-Switch an/aus")]
|
||||
public bool HeartbeatEnabled { get; set; } = true;
|
||||
|
||||
[Category("Deploymentcenter")]
|
||||
[DisplayName("Monitor Source")]
|
||||
[Description("Monitor-Name im Dashboard. Leer = Rechnername (Auto-Registrierung beim ersten Ping)")]
|
||||
public string MonitorSource { get; set; } = "";
|
||||
|
||||
[Category("Deploymentcenter")]
|
||||
[DisplayName("Monitor Instance")]
|
||||
[Description("Für mehrere Instanzen desselben Dienstes (z. B. EU/US)")]
|
||||
public string MonitorInstance { get; set; } = "default";
|
||||
|
||||
[Category("Deploymentcenter")]
|
||||
[DisplayName("Heartbeat-Intervall (Sekunden)")]
|
||||
[Description("warning nach 2×, down nach 4× dieses Intervalls")]
|
||||
public int HeartbeatIntervalSeconds { get; set; } = 60;
|
||||
|
||||
[Category("Deploymentcenter")]
|
||||
[DisplayName("Fehler melden")]
|
||||
[Description("Error/Fatal an den Fehler-Stream (/api/errors/v1/report)")]
|
||||
public bool ReportErrors { get; set; } = true;
|
||||
|
||||
[Category("Deploymentcenter")]
|
||||
[DisplayName("Update-Prüfung beim Start")]
|
||||
[Description("Prüft nur, installiert nichts von selbst")]
|
||||
public bool CheckUpdatesOnStart { get; set; } = true;
|
||||
|
||||
[Category("Deploymentcenter")]
|
||||
[DisplayName("Update-Kanal")]
|
||||
[Description("prod, beta oder dev")]
|
||||
public string UpdateChannel { get; set; } = "prod";
|
||||
|
||||
[Category("Deploymentcenter")]
|
||||
[DisplayName("Lizenzschlüssel")]
|
||||
[Description("Nur Rückfall, falls noch kein Schlüssel im verschlüsselten Cache liegt")]
|
||||
[PasswordPropertyText(true)]
|
||||
public string LicenseKey { get; set; } = "";
|
||||
|
||||
public override string ToString() => HeartbeatEnabled ? "Aktiv" : "Deaktiviert";
|
||||
}
|
||||
|
||||
// ─── Root ────────────────────────────────────────────────────────────────────
|
||||
|
||||
public class AppSettings
|
||||
@@ -347,4 +402,9 @@ public class AppSettings
|
||||
[DisplayName("Supervisor")]
|
||||
[Description("KI-Analyse/Forensik (OpenRouter, read-only)")]
|
||||
public SupervisorSettings Supervisor { get; set; } = new();
|
||||
|
||||
[Category("Deploymentcenter")]
|
||||
[DisplayName("Deploymentcenter")]
|
||||
[Description("Lizenz, Watchdog, UpdateService, Fehler-Stream (https://dc.mhdf.de)")]
|
||||
public DeploymentcenterSettings Deploymentcenter { get; set; } = new();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user