Der Status des Projekts stand verstreut in elf Umsetzungsplaenen, drei Konzepten, der Linux-Analyse und dem Projektstand - teils widersprechend, teils wochenlang veraltet. Ab jetzt gibt es genau eine Statusquelle. docs/ROADMAP.md (neu): - Alle Vorhaben in vier Stufen A bis D, plus technische Schuld und Verlauf. Die Stufen sind eine Reihenfolge, keine Termine: jede schafft die Voraussetzung fuer die naechste. - Statuszeichen: erledigt / offen / blockiert (mit Ursache) / bewusst zurueckgestellt / Idee, nicht beschlossen. Damit ist das, was wir NICHT bauen wollen, sichtbar vorgehalten statt unauffindbar in einem Plan zu schlummern. - Inhaltlich getragen, nicht nur verlinkt: je Vorhaben Ziel, Phasen, Akzeptanzkriterien, offene Entscheidungen und Leitplanken aus den Quelldokumenten. - Sichtbar gemacht, was vorher zwischen den Dokumenten verborgen lag: CopyTrading Phase 1 ist der Engpass der gesamten Roadmap (MarketMaking und BundleArbitrage haben harte Voraussetzungen darauf), und die Sniper-Metriken aus Phase 3.2 sind ein Spezialfall des StrategieDrift-Fingerprints - zusammen bauen statt doppelt. Archiv (docs/archiv/): - 15 Dokumente verschoben (11 Umsetzungsplaene, 3 Konzepte, ANALYSE-Linux-Portierung). Sie bleiben die Bauanleitungen mit Code-Bezuegen, Risikotabellen und Begruendungen - eingefroren ist nur ihr Status. - archiv/README.md ordnet jedes Dokument seinem Roadmap-Punkt zu. Verweise nachgezogen - der eigentliche Aufwand: - 25 Markdown-Links repariert. 15 davon verschiebungsbedingt (eine Ebene tiefer), der Rest war schon vorher falsch: die Ideensammlung verlinkte Quellcode relativ zum Repo-Wurzelverzeichnis statt zu docs/. - 12 Dateien ausserhalb von docs/ verwiesen in Kommentaren auf die Plaene (csproj, props, setup.json, sechs Quelldateien) - alle auf archiv/ umgebogen. - Verweise auf Dateien, die der Fruehjahrsputz geloescht hat (Ui/, Program.cs, WindowMenuBar), zu Klartext entschaerft statt tote Links zu lassen. - Gegenprobe: 85 Links geprueft, 0 kaputt. Build gruen, 476 Tests gruen. PROJEKTSTAND.md entdoppelt: Abschnitt "Offen" verweist jetzt auf die Roadmap. Arbeitsteilung ist damit klar - Projektstand sagt was IST, Roadmap was KOMMT. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
106 lines
5.4 KiB
C#
106 lines
5.4 KiB
C#
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/archiv/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;
|
||
}
|
||
}
|
||
}
|
||
}
|