Arbeitsstand gesichert: Deploymentcenter-Integration (D-0 bis D-5) und Einfenster-Shell
Sicherungscommit vor dem Aufraeumen des Repos, damit nachvollziehbar bleibt, welcher Stand vor der Bereinigung galt. Build gruen, 476 Tests gruen. Zwei Straenge, die sich ueber .csproj, Program.cs und appsettings.json ueberschneiden und darum gemeinsam abgelegt werden: Deploymentcenter-Integration (P3c, Plan D-0 bis D-5 code-seitig fertig): - Deploymentcenter.Client 2.5.0 als lokales Paket, Source-Mapping erweitert - DeploymentcenterOptions, DeploymentcenterErrorReporter, LicenseGate/LicenseCli - WatchdogHeartbeatService auf die Deploymentcenter-API umgestellt (version, os, checks, metrics, status stopped) - Security: MasterKeyResolver, SecretRedactor, FilePermissions - Directory.Build.props mit zentraler Version 0.1.0 (Packager-Versionsdisziplin) - deploy/: Packager-Vorlage und systemd-Unit; echte Zugangsdaten bleiben ueber .gitignore aussen vor - setup.json fuer die Erstinstallation - UMSETZUNGSPLAN-Deploymentcenter-Integration.md; ANALYSE-Linux-Portierung.md verweist auf den neuen Plan Einfenster-Shell (UI-Redesign): - ShellWindow + ShellNavModel als Seitenleisten-Shell - WindowMenuBar und WindowMenuModel entfallen - Modul- und Kernfenster auf die Shell-Einbettung angepasst Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,105 @@
|
||||
using System;
|
||||
using Deploymentcenter.Client;
|
||||
using PolyTrader.Core;
|
||||
using PolyTrader.Core.Configuration;
|
||||
using PolyTrader.Core.Security;
|
||||
using PolyTraderSharp.Models;
|
||||
using PolyTraderSharp.Services;
|
||||
|
||||
namespace PolyTrader.App.Avalonia.Licensing
|
||||
{
|
||||
/// <summary>
|
||||
/// Lizenz-Torwächter für den Programmstart (D-2). Ersetzt den früheren, gegen
|
||||
/// <c>LicenseLabrador.Client</c> gebauten <c>Licensing/LicenseGate.cs</c> im WinForms-Projekt
|
||||
/// (siehe docs/umsetzungsplaene/UMSETZUNGSPLAN-Deploymentcenter-Integration.md) – dort läuft
|
||||
/// ohnehin keine Lizenzprüfung, weil Avalonia die tatsächlich benutzte Anwendung ist.
|
||||
///
|
||||
/// **Grundsatz unverändert:** kein <see cref="Environment.Exit(int)"/> bei ungültiger Lizenz. Ein
|
||||
/// Trading-Bot mit offenen Positionen darf nicht mitten im Lauf abgeschossen werden. Bei
|
||||
/// fehlender/ungültiger Lizenz startet die Core-Shell (Terminal, Einstellungen) ohne die
|
||||
/// Trading-/Analyse-Module; der Schlüssel lässt sich jederzeit über den Button
|
||||
/// „Lizenzschlüssel setzen …" im Einstellungsfenster nachtragen (wirkt ab dem nächsten Start –
|
||||
/// eine Laufzeit-Revalidierung gibt es bewusst nicht, siehe Entscheidung vom 31.07.2026).
|
||||
///
|
||||
/// **Bewusst kein eigenes <see cref="ILicensePrompt"/>:** Der interaktive Weg läuft über die
|
||||
/// bereits vorhandene, erprobte Settings-UI (derselbe maskierte Prompt-Button wie beim
|
||||
/// Watchdog-Token); ein zusätzlicher Dialog nur für die Lizenz wäre eine zweite, redundante
|
||||
/// Eingabe-Oberfläche. Für den Kopflos-Betrieb (<c>--headless</c>, CLI-Schalter) genügt die vom
|
||||
/// SDK mitgelieferte <see cref="ConsoleLicensePrompt"/> – eine eigene Fassung böte keinen Mehrwert.
|
||||
/// </summary>
|
||||
public static class LicenseGate
|
||||
{
|
||||
/// <summary>
|
||||
/// Prüft still (ohne Dialog), ob die gespeicherte Lizenz aktuell nutzbar ist. Entscheidet
|
||||
/// darüber, ob die Trading-/Analyse-Module überhaupt registriert werden – muss deshalb vor
|
||||
/// dem Host-Aufbau laufen. Wirft nie: jeder Fehler in der Prüfung führt zum eingeschränkten
|
||||
/// Modus, nicht zum Absturz.
|
||||
/// </summary>
|
||||
public static bool CheckUsable(DeploymentcenterOptions options, ServerSettings settings, TerminalLogger logger)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(options.BaseUrl))
|
||||
{
|
||||
logger.Warning("⚠️ Keine Deploymentcenter-URL konfiguriert. Start im eingeschränkten Modus (nur Core-Shell).");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Befund D-3 aus dem Umsetzungsplan: das SDK erzwingt selbst kein TLS. Ohne diese
|
||||
// Prüfung ginge der Lizenzschlüssel im Klartext übers Netz, wenn die URL versehentlich
|
||||
// auf http:// steht.
|
||||
if (!Uri.TryCreate(options.BaseUrl, UriKind.Absolute, out var parsed) ||
|
||||
!(parsed.Scheme == Uri.UriSchemeHttps || (parsed.Scheme == Uri.UriSchemeHttp && parsed.IsLoopback)))
|
||||
{
|
||||
logger.Warning($"⚠️ Deploymentcenter-URL muss HTTPS verwenden (http:// nur für localhost): '{options.BaseUrl}'. " +
|
||||
"Start im eingeschränkten Modus.");
|
||||
return false;
|
||||
}
|
||||
|
||||
string key;
|
||||
try
|
||||
{
|
||||
// Erlaubt einen mit dem Master-Key verschlüsselten Schlüssel (enc:v1:…);
|
||||
// Klartext wird unverändert durchgereicht.
|
||||
key = SecretProtection.Unprotect(settings.LicenseKey);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.Warning($"⚠️ Gespeicherter Lizenzschlüssel nicht lesbar ({ex.Message}) – behandle als 'keine Lizenz'.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(key))
|
||||
{
|
||||
logger.Warning("⚠️ Kein Lizenzschlüssel gesetzt. Start im eingeschränkten Modus (nur Core-Shell). " +
|
||||
"Schlüssel über „Lizenzschlüssel setzen …\" im Einstellungsfenster eintragen.");
|
||||
return false;
|
||||
}
|
||||
|
||||
LicenseClient.DefaultAppVersion = BuildInfo.Version;
|
||||
var client = new LicenseClient();
|
||||
LicenseValidationResult result = client
|
||||
.ValidateAsync(options.ProjectSlug, key, options.BaseUrl, BuildInfo.Version)
|
||||
.GetAwaiter().GetResult();
|
||||
|
||||
if (result.IsValid)
|
||||
{
|
||||
logger.Info(result.IsCached
|
||||
? $"🔑 Lizenz im Offline-Kulanzmodus gültig ({result.Status})."
|
||||
: "🔑 Lizenz gültig (online geprüft).");
|
||||
return true;
|
||||
}
|
||||
|
||||
logger.Warning($"⚠️ Keine gültige Lizenz ({result.Status}: {result.Message}). " +
|
||||
"Start im eingeschränkten Modus (nur Core-Shell, keine Module). " +
|
||||
"Schlüssel über „Lizenzschlüssel setzen …\" im Einstellungsfenster prüfen.");
|
||||
return false;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.Error($"❌ Lizenzprüfung unerwartet fehlgeschlagen: {ex.Message}. Start im eingeschränkten Modus.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user