Watchdog-Heartbeat + LicenseLabrador-Lizenzgate im WinFormsHost

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>
This commit is contained in:
Richard
2026-07-30 12:12:54 +02:00
co-authored by Claude Fable 5
parent 02c5a4d6f4
commit 3a82397651
8 changed files with 515 additions and 0 deletions
@@ -11,6 +11,7 @@ public partial class MainForm : Form
private bool _workerRunning;
private bool _webServerRunning;
private AppSettings _settings = null!;
private WatchdogHeartbeatService? _watchdog;
/// <summary>Exposes the terminal RichTextBox for the Serilog sink.</summary>
public RichTextBox Terminal => rtb_terminal;
@@ -40,6 +41,7 @@ public partial class MainForm : Form
_webServer.DbConnectionDebug = _settings.DbConnectionDebug;
_webServer.EgressChannelsText = _settings.EgressChannelsText;
}
RestartWatchdog();
};
_webServer = new EmbeddedWebServer();
@@ -69,6 +71,39 @@ public partial class MainForm : Form
var dbSizeTimer = new System.Windows.Forms.Timer { Interval = 6 * 60 * 60 * 1000 };
dbSizeTimer.Tick += async (s, e) => await UpdateDbSizeAsync();
dbSizeTimer.Start();
RestartWatchdog();
}
/// <summary>
/// (Re-)creates the Watchdog heartbeat sender from the current settings.
/// Called at startup and whenever settings change.
/// </summary>
private void RestartWatchdog()
{
_watchdog?.Dispose();
_watchdog = null;
if (!_settings.WatchdogEnabled) return;
if (string.IsNullOrWhiteSpace(_settings.WatchdogApiKey) || string.IsNullOrWhiteSpace(_settings.WatchdogUrl))
{
Log.Information("🐕 Watchdog ist aktiviert, aber URL/API Key fehlen — bitte in den Settings eintragen.");
return;
}
_watchdog = new WatchdogHeartbeatService(
_settings.WatchdogUrl,
_settings.WatchdogApiKey,
_settings.WatchdogSource,
_settings.WatchdogInstance,
_settings.WatchdogIntervalSeconds,
metadataProvider: () => new
{
workersRunning = _workerRunning,
webserverRunning = _webServerRunning
});
_watchdog.Start();
}
private async void Btn_serverstart_Click(object? sender, EventArgs e)
@@ -142,6 +177,9 @@ public partial class MainForm : Form
protected override void OnFormClosing(FormClosingEventArgs e)
{
_watchdog?.NotifyStopping();
_watchdog?.Dispose();
_watchdog = null;
_workerCts?.Cancel();
_webServer?.StopWebServerAsync().GetAwaiter().GetResult();
base.OnFormClosing(e);