Deployment-Center-Integration im WinFormsHost
Loest die getrennten Server Watchdog (watchdog.mhdf.de) und LicenseLabrador (license.mhdf.de) durch das Deployment Center (dc.mhdf.de) ab. - DcConfig: einkompilierte Basis-URL und Produkt-Slug, Version aus BuildInfo - DcApiClient: gemeinsamer HTTP-Zugang - DcHeartbeatService: Heartbeat mit Metriken und DB-Health-Check - DcErrorReporter/DcErrorSink: Error- und Fatal-Meldungen an den Fehler-Stream - DcUpdateService: Update-Pruefung gegen den UpdateService - LicenseGuard/LicenseDialog: Lizenzgate ueber /api/license/v1/validate, mit LicenseSession, Hardware-ID v2 und Unterscheidung transienter Fehler WatchdogHeartbeatService entfernt, Betriebsdoku ersetzt. Build: 0 Fehler. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,232 @@
|
||||
# Betrieb: Deployment Center
|
||||
|
||||
Das **Deployment Center** (`https://dc.mhdf.de`, Schwester-Repo
|
||||
`J:\Softwareprojekte\Deploymentcenter`) hat die beiden früheren Einzelserver abgelöst:
|
||||
|
||||
| Vorher | Jetzt |
|
||||
|---|---|
|
||||
| WatchDog auf `watchdog.mhdf.de`, `POST /api/heartbeat`, Header `X-Watchdog-Key` | Deployment Center, `POST /api/watchdog/v1/ping`, `Authorization: Bearer` |
|
||||
| LicenseLabrador auf `license.mhdf.de`, Ed25519-signierte Envelopes | Deployment Center, `POST /api/license/v1/validate`, Hardware-ID v2 |
|
||||
| — | UpdateService, Fehler-Stream und Bugtracker (neu) |
|
||||
|
||||
Die Anbindung sitzt im `Predictalytics.WinFormsHost` und referenziert das
|
||||
C#-SDK `Deploymentcenter.Client` aus dem Schwester-Repo:
|
||||
|
||||
```
|
||||
..\..\..\..\Deploymentcenter\client-dotnet\Deploymentcenter.Client\Deploymentcenter.Client.csproj
|
||||
```
|
||||
|
||||
Das Repo muss also neben dem Predictalytics-Checkout liegen. `LicenseLabrador`
|
||||
und `WatchDog` werden nicht mehr gebraucht.
|
||||
|
||||
---
|
||||
|
||||
## 0. Was einmalig zu tun ist
|
||||
|
||||
1. Im Deployment Center unter **Token-Verwaltung → Master-Token erstellen** ein
|
||||
Token mit den Rechten **`watchdog:ping`** und **`bugtracker:report`** anlegen.
|
||||
2. Im Host unter **Settings → Deployment Center → API Token** eintragen.
|
||||
Der alte `X-Watchdog-Key` funktioniert nicht mehr — es ist ein anderer Server.
|
||||
3. Beim ersten Start erscheint einmalig der Lizenzdialog: der Lizenzschlüssel muss
|
||||
erneut eingegeben werden, weil die Aktivierung jetzt am Deployment Center hängt.
|
||||
4. Serverseitig muss der **Evaluator-Cron** laufen — ohne ihn bleibt ein
|
||||
abgestürzter Dienst dauerhaft grün:
|
||||
```
|
||||
* * * * * curl -fsS -H "Authorization: Bearer <SHARED_KEY>" https://dc.mhdf.de/api/watchdog/v1/evaluate > /dev/null
|
||||
```
|
||||
|
||||
Der Token landet in der `settings.json` neben der Exe (nicht im Git).
|
||||
|
||||
---
|
||||
|
||||
## 1. Konfiguration (PropertyGrid, Kategorie „Deployment Center")
|
||||
|
||||
| Feld | Default | Bedeutung |
|
||||
|---|---|---|
|
||||
| `Server URL` | `https://dc.mhdf.de` | **schreibgeschützt**, siehe unten |
|
||||
| `API Token` | *(leer)* | `Authorization: Bearer`. Ohne Token: keine Heartbeats, keine Fehlermeldungen. |
|
||||
| `Heartbeat aktiv` | `true` | Dead-Man's-Switch an/aus |
|
||||
| `Monitor Source` | `Predictalytics` | Monitor-Name im Dashboard (Auto-Registrierung beim ersten Ping) |
|
||||
| `Monitor Instance` | `default` | falls mehrere Instanzen laufen |
|
||||
| `Heartbeat-Intervall (Sekunden)` | `60` | `warning` nach 2×, `down` nach 4× |
|
||||
| `Fehler melden` | `true` | Error/Fatal an den Fehler-Stream |
|
||||
| `Update-Prüfung beim Start` | `true` | prüft nur, installiert nichts von selbst |
|
||||
| `Update-Kanal` | `prod` | `prod`, `beta` oder `dev` |
|
||||
|
||||
**Warum ist die Server-URL nicht einstellbar?** Sie entscheidet, wohin die
|
||||
Lizenzprüfung geht und woher Update-Pakete kommen. Ein einstellbarer Endpoint
|
||||
würde erlauben, die App auf einen gefälschten Lizenz- oder Update-Server zu
|
||||
zeigen. Sie steht als Konstante in `Services/DcConfig.cs`.
|
||||
|
||||
---
|
||||
|
||||
## 2. Watchdog (Dead-Man's-Switch)
|
||||
|
||||
`Services/DcHeartbeatService.cs` sendet alle *n* Sekunden einen
|
||||
`POST /api/watchdog/v1/ping`. Beim regulären Schließen geht ein Ping mit
|
||||
`status: "stopped"` raus — der Evaluator lässt einen so beendeten Monitor in
|
||||
Ruhe, bis wieder ein normaler Heartbeat eintrifft. Ohne das folgt wenige Minuten
|
||||
nach jedem geordneten Beenden ein Fehlalarm.
|
||||
|
||||
**Ein Ausfall des Deployment Centers darf Predictalytics nie beeinträchtigen.**
|
||||
Alle Aufrufe sind best effort; der erste Fehlschlag wird als Warnung geloggt,
|
||||
Folgefehler nur noch auf Debug-Level (keine Log-Flut). Wird das Token abgelehnt
|
||||
(`401`/`403`), stellt der Dienst die Versuche ganz ein — Wiederholen kann daran
|
||||
nichts ändern.
|
||||
|
||||
### Mitgesendeter Gesundheitszustand
|
||||
|
||||
Ein Heartbeat beweist nur, dass ein Timer läuft. Deshalb schickt die App ihren
|
||||
selbst ermittelten Zustand mit:
|
||||
|
||||
| `checks` | Bedeutung |
|
||||
|---|---|
|
||||
| `db` | `SELECT 1` gegen die konfigurierte MySQL, gedeckelt auf 5 s. Schlägt sie fehl, stuft der Server den Heartbeat auf `warning`. |
|
||||
|
||||
| `metrics` | Bedeutung |
|
||||
|---|---|
|
||||
| `uptime_sec` | Laufzeit des Prozesses |
|
||||
| `workers_running` | 0/1 — Hintergrund-Worker gestartet |
|
||||
| `webserver_running` | 0/1 — eingebetteter Kestrel gestartet |
|
||||
| `memory_mb` | verwalteter Heap |
|
||||
| `db_size_mb` | zuletzt ermittelte Datenbankgröße |
|
||||
|
||||
Zusätzlich geht seit Server 2.1 das Feld `version` mit — im Dashboard ist damit
|
||||
sichtbar, welcher Build läuft, und ein Ausfall lässt sich einem Rollout zuordnen.
|
||||
|
||||
Gestoppte Worker sind **bewusst kein** fehlgeschlagener Check: das ist ein
|
||||
gewollter Betriebszustand und würde den Monitor sonst dauerhaft auf `warning`
|
||||
halten. Der Zustand steckt als Metrik drin und lässt sich dort auswerten.
|
||||
|
||||
Metriken werden serverseitig 14 Tage lang mit Verlauf gehalten
|
||||
(`GET /api/watchdog/v1/metrics?source=Predictalytics`).
|
||||
|
||||
### Maschine mit überwachen
|
||||
|
||||
Der Heartbeat deckt nur *diesen* Prozess ab. Für die Maschine selbst gehört
|
||||
zusätzlich ein OS-Agent auf den Host (siehe
|
||||
`Deploymentcenter/docs/WATCHDOG_INTEGRATION_GUIDE.md`). Läuft beides, sollte im
|
||||
WebUI unter **WatchDog → System-Hierarchie** die *Übergeordnete Entität* des
|
||||
Predictalytics-Monitors auf den Host gesetzt werden — dann erzeugt ein
|
||||
Maschinenausfall eine Meldung statt zwei.
|
||||
|
||||
---
|
||||
|
||||
## 3. Lizenzierung
|
||||
|
||||
`Services/LicenseGuard.cs` prüft beim Start, ob eine nutzbare Lizenz vorliegt;
|
||||
`Program.Main` bricht sonst ab, bevor die MainForm entsteht. Ohne gültige Lizenz
|
||||
erscheint `Services/LicenseDialog.cs` zur Key-Eingabe.
|
||||
|
||||
- **Produkt-Slug:** `predictalytics`
|
||||
- **Hardware-ID v2:** `2:win:<sha256>` aus `HKLM\...\Cryptography\MachineGuid`.
|
||||
Der Rechnername steckt **nicht** im Hash — Umbenennen kostet keinen
|
||||
Aktivierungsplatz.
|
||||
- **Offline-Cache:** AES-256-GCM + DPAPI unter `%AppData%\predictalytics\license\state.dat`
|
||||
(Schema 3 seit SDK 2.1, Schema 2 wird noch gelesen). Dort liegt auch der
|
||||
Lizenzschlüssel — `LicenseClient.TryGetCachedKey()` holt ihn beim Start, damit
|
||||
ohne Dialog revalidiert werden kann.
|
||||
- **Revalidierung zur Laufzeit:** alle 12 h.
|
||||
- **Gemeldete Version:** `LicenseClient.DefaultAppVersion` wird in `Program.Main`
|
||||
auf `BuildInfo.Version` gesetzt; ohne das trüge jede Installation in der
|
||||
Aktivierungsliste dieselbe „1.0.0".
|
||||
|
||||
### Offline-Gnadenfrist
|
||||
|
||||
Seit Server 2.1 ist sie **echt begrenzt**: der Server liefert `cache_ttl_hours`
|
||||
(je Projekt im WebUI, Standard 168 h = 7 Tage). Vorher galt faktisch das
|
||||
Lizenz-Ablaufdatum, bei einer Lizenz bis 2040 also unbegrenzt.
|
||||
|
||||
Läuft die Frist in weniger als 48 h ab, warnt der Host beim Start und bei jeder
|
||||
Revalidierung. Die verbleibende Frist zeigt **Deployment Center → Lizenzstatus
|
||||
anzeigen**. Falls Predictalytics planmäßig länger offline laufen soll, muss die
|
||||
TTL im WebUI für das Projekt `predictalytics` heraufgesetzt werden.
|
||||
|
||||
### Wann die App sich beendet — und wann nicht
|
||||
|
||||
Maßgeblich ist `LicenseValidationResult.IsTransient`:
|
||||
|
||||
| | Zustände | Reaktion |
|
||||
|---|---|---|
|
||||
| `IsValid` | `valid`, `valid_offline` | weiter |
|
||||
| `IsTransient` | `server_unavailable`, `cache_expired` | **kein Urteil, nur eine gescheiterte Verbindung** — weiterlaufen, beim nächsten Durchlauf erneut versuchen |
|
||||
| sonst | `revoked`, `expired`, `not_found`, `activation_limit`, `suspended`, `clock_rollback` | Meldung und Beenden |
|
||||
|
||||
`cache_expired` beendet die laufende Sitzung also nicht — der **nächste Start**
|
||||
bleibt aber am Lizenzdialog hängen, weil `IsValid` dann falsch ist. Der Host
|
||||
loggt diesen Fall deshalb als Error, nicht bloß als Warnung.
|
||||
|
||||
### Deaktivierung bei PC-Wechsel
|
||||
|
||||
`POST /api/license/v1/deactivate` verlangt den `shared_key` aus der
|
||||
Server-Konfiguration. Der gehört **nicht** in die ausgelieferte Anwendung,
|
||||
deshalb ist die Funktion hier nicht verdrahtet. Übliche Route: im WebUI unter
|
||||
**Lizenzen → Hardware-Liste → „Freigeben"**. Die Hardware-ID zeigt der Host unter
|
||||
**Deployment Center → Lizenzstatus anzeigen**.
|
||||
|
||||
---
|
||||
|
||||
## 4. Fehler-Stream
|
||||
|
||||
`Services/DcErrorReporter.cs` meldet Laufzeitfehler an
|
||||
`POST /api/errors/v1/report`. Zwei Wege führen dorthin:
|
||||
|
||||
1. **Globale Handler** — `Application.ThreadException` (UI-Thread),
|
||||
`AppDomain.UnhandledException` (Level `fatal`, wird blockierend gesendet, weil
|
||||
der Prozess gleich weg ist) und `TaskScheduler.UnobservedTaskException`.
|
||||
2. **Serilog-Sink** (`Services/DcErrorSink.cs`) — jedes `Log.Error`/`Log.Fatal`,
|
||||
auch aus Worker und Kestrel.
|
||||
|
||||
Lokale Bremse, damit das Rate-Limit (60/min und IP) nicht verbrannt wird:
|
||||
höchstens 20 Meldungen pro Minute, derselbe Fehler höchstens alle 5 Minuten.
|
||||
Serverseitig werden gleiche Fehler ohnehin gruppiert und hochgezählt;
|
||||
betriebsbedingtes Rauschen gehört in die **Ignore-Regeln** des WebUI — ein
|
||||
Treffer zählt weiter, meldet aber nicht.
|
||||
|
||||
> Der Serilog-Filter in `Program.cs` wirft `Duplicate entry`-Ausnahmen schon
|
||||
> vorher weg; die erreichen den Stream also gar nicht.
|
||||
|
||||
---
|
||||
|
||||
## 5. Updates
|
||||
|
||||
`Services/DcUpdateService.cs` fragt beim Start
|
||||
`GET /api/updateservice/v1/check` ab (ohne Token). Gefunden wird nur — installiert
|
||||
wird nichts von selbst:
|
||||
|
||||
- Kein Update: Info ins Log.
|
||||
- Update verfügbar: Warnung ins Log, Hinweis in der Statusleiste.
|
||||
- **Kritisches** Update: zusätzlich ein Dialog.
|
||||
- Menü **Deployment Center → Nach Updates suchen** prüft jederzeit von Hand.
|
||||
|
||||
Liegt `update-agent.exe` neben der Anwendung, bietet der Dialog an, ihn zu
|
||||
starten; die Anwendung meldet dann vorher `stopped` und beendet sich. Fehlt der
|
||||
Agent, weist der Dialog auf die manuelle Installation hin.
|
||||
|
||||
Seit SDK 2.1 liefert auch der API-Zweig vollständige Release-Daten
|
||||
(Download-Adresse, Prüfsumme, Changelog, Kritikalität) — der Dialog zeigt die
|
||||
Release Notes also auch dann, wenn die statische `latest.json` fehlt.
|
||||
|
||||
### Versionsstand
|
||||
|
||||
`Predictalytics.WinFormsHost.csproj` importiert `Deploymentcenter.BuildInfo.targets`
|
||||
und erzeugt daraus zur Übersetzungszeit `Predictalytics.WinFormsHost.BuildInfo`
|
||||
mit `Version`, `GitCommit`, `GitCommitShort`, `BuildDateUtc`, `Channel` und
|
||||
`Summary`. Quelle ist `<Version>` in derselben csproj — beim Release dort
|
||||
hochziehen und mit
|
||||
|
||||
```
|
||||
pack-and-deploy --project predictalytics --version <x> --channel prod
|
||||
```
|
||||
|
||||
veröffentlichen. Dieselbe Version geht an die Lizenz-Aktivierungsliste, an den
|
||||
Heartbeat und als `build` an den Fehler-Stream; `GitCommitShort` reist im
|
||||
`context` jeder Fehlermeldung mit.
|
||||
|
||||
---
|
||||
|
||||
## 6. Bugtracker
|
||||
|
||||
Der Agenten-Workflow ist in [`../CLAUDE.md`](../CLAUDE.md) beschrieben.
|
||||
Projekt-Slug: `predictalytics`. Items, deren `resolved_in_build` einer
|
||||
veröffentlichten Version entspricht, schließen sich beim Publish von selbst.
|
||||
Reference in New Issue
Block a user