Bindet die beiden neuen Betriebsprojekte an: - WatchdogHeartbeatService: periodischer POST /api/heartbeat an watchdog.mhdf.de (Dead-Man's-Switch), stopping-Event beim Beenden, Konfiguration ueber AppSettings-Kategorie "Watchdog". Fehler sind best effort und beeintraechtigen die App nie. - LicenseGuard + LicenseDialog: Lizenzpruefung vor dem Start der MainForm, 12h-Revalidierung zur Laufzeit, Checksum-Haertung. Produkt-Slug/Endpoint/Public-Key sind einkompiliert. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
119 lines
3.6 KiB
C#
119 lines
3.6 KiB
C#
using LicenseLabrador.Client;
|
|
|
|
namespace Predictalytics.WinFormsHost.Services;
|
|
|
|
/// <summary>
|
|
/// Modal dialog shown at startup when no usable license is present.
|
|
/// Lets the user enter/activate a license key; closes with OK only after a
|
|
/// successful, checksum-verified validation.
|
|
/// </summary>
|
|
public sealed class LicenseDialog : Form
|
|
{
|
|
private readonly LicenseClient _client;
|
|
private readonly Label _lblStatus;
|
|
private readonly TextBox _txtKey;
|
|
private readonly Button _btnActivate;
|
|
private readonly Button _btnExit;
|
|
|
|
public LicenseResult? Result { get; private set; }
|
|
|
|
public LicenseDialog(LicenseClient client, LicenseResult? lastResult)
|
|
{
|
|
_client = client;
|
|
|
|
Text = "Predictalytics — Lizenzaktivierung";
|
|
FormBorderStyle = FormBorderStyle.FixedDialog;
|
|
MaximizeBox = false;
|
|
MinimizeBox = false;
|
|
StartPosition = FormStartPosition.CenterScreen;
|
|
ClientSize = new Size(460, 190);
|
|
|
|
var lblInfo = new Label
|
|
{
|
|
Text = "Diese Installation benötigt eine gültige Lizenz.\nBitte Lizenzschlüssel eingeben (Format: XXXXX-XXXXX-XXXXX-XXXXX-XXXXX):",
|
|
Location = new Point(12, 12),
|
|
Size = new Size(436, 34)
|
|
};
|
|
|
|
_txtKey = new TextBox
|
|
{
|
|
Location = new Point(12, 52),
|
|
Size = new Size(436, 26),
|
|
Font = new Font("Consolas", 11f),
|
|
CharacterCasing = CharacterCasing.Upper
|
|
};
|
|
|
|
_lblStatus = new Label
|
|
{
|
|
Location = new Point(12, 84),
|
|
Size = new Size(436, 50),
|
|
ForeColor = Color.Firebrick,
|
|
Text = FormatInitialStatus(lastResult)
|
|
};
|
|
|
|
_btnActivate = new Button
|
|
{
|
|
Text = "Aktivieren",
|
|
Location = new Point(252, 146),
|
|
Size = new Size(96, 30)
|
|
};
|
|
_btnActivate.Click += async (_, _) => await ActivateAsync();
|
|
|
|
_btnExit = new Button
|
|
{
|
|
Text = "Beenden",
|
|
Location = new Point(354, 146),
|
|
Size = new Size(94, 30),
|
|
DialogResult = DialogResult.Cancel
|
|
};
|
|
|
|
AcceptButton = _btnActivate;
|
|
CancelButton = _btnExit;
|
|
Controls.AddRange(new Control[] { lblInfo, _txtKey, _lblStatus, _btnActivate, _btnExit });
|
|
}
|
|
|
|
private static string FormatInitialStatus(LicenseResult? lastResult)
|
|
{
|
|
if (lastResult == null || lastResult.State == LicenseState.NoLicense) return "";
|
|
return $"Letzte Prüfung: {lastResult.State} — {lastResult.Message}";
|
|
}
|
|
|
|
private async Task ActivateAsync()
|
|
{
|
|
var key = _txtKey.Text.Trim();
|
|
if (string.IsNullOrWhiteSpace(key))
|
|
{
|
|
_lblStatus.Text = "Bitte einen Lizenzschlüssel eingeben.";
|
|
return;
|
|
}
|
|
|
|
_btnActivate.Enabled = false;
|
|
_lblStatus.ForeColor = Color.DimGray;
|
|
_lblStatus.Text = "Prüfe Lizenz am Server...";
|
|
|
|
try
|
|
{
|
|
var result = await _client.ValidateAsync(key);
|
|
if (result.IsUsable && _client.VerifyChecksum(result))
|
|
{
|
|
Result = result;
|
|
DialogResult = DialogResult.OK;
|
|
Close();
|
|
return;
|
|
}
|
|
|
|
_lblStatus.ForeColor = Color.Firebrick;
|
|
_lblStatus.Text = $"Lizenz nicht nutzbar ({result.State}):\n{result.Message}";
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_lblStatus.ForeColor = Color.Firebrick;
|
|
_lblStatus.Text = $"Fehler bei der Prüfung: {ex.Message}";
|
|
}
|
|
finally
|
|
{
|
|
_btnActivate.Enabled = true;
|
|
}
|
|
}
|
|
}
|