Baustein 7: TODO.md-Korrektur (Kapitel 10)

- 10.1.4/10.1.5 abgehakt (SyncStatusViewModel/SyncStatusBar waren
  bereits vorhanden, aber in der Checkliste noch nicht als erledigt
  markiert)
- 10.1.6 abgehakt mit Umsetzungsnotiz (Baustein 2-4)
- Neuer Punkt 10.1.7 fuer den zuvor komplett fehlenden, in der
  Checkliste nicht erfassten Inbound-Apply-Baustein (Baustein 5)
- Neuer Punkt 10.1.8 fuer Anhang-Sync (Baustein 6)
- 10.2.1 abgehakt mit Umsetzungsnotiz (Baustein 1)
- Neuer Punkt 10.3.4 fuer die bewusst akzeptierte v1-Einschraenkung
  beim Regel-Bypass im Event-Applier

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-17 11:31:09 +02:00
co-authored by Claude Sonnet 5
parent f0f8fa25e5
commit 95345f9c46
+63 -4
View File
@@ -1306,14 +1306,67 @@ ist aber nur aktiv, wenn eine Server-URL konfiguriert ist.
- [ ] **10.1.1** Sync-Einrichtung in den Einstellungen: Server-URL, Login, Token speichern. - [ ] **10.1.1** Sync-Einrichtung in den Einstellungen: Server-URL, Login, Token speichern.
- [ ] **10.1.2** Verbindungstest mit klarer Fehlermeldung (nicht erreichbar / Token ungültig). - [ ] **10.1.2** Verbindungstest mit klarer Fehlermeldung (nicht erreichbar / Token ungültig).
- [ ] **10.1.3** Konfliktanzeige in der UI — was `ConflictResolver` entscheidet, muss sichtbar sein. - [ ] **10.1.3** Konfliktanzeige in der UI — was `ConflictResolver` entscheidet, muss sichtbar sein.
- [ ] **10.1.4** Manuelles Auslösen einer vollständigen Synchronisation. - [x] **10.1.4** Manuelles Auslösen einer vollständigen Synchronisation.
- [ ] **10.1.5** Statusanzeige erweitern: letzter Sync, Anzahl wartender Events, Fehlerzustand.
- [ ] **10.1.6** Lokale Schreibvorgänge atomar an die Outbox (`EventQueue`) anbinden — aktuell ist **Umsetzung:** War bereits vorhanden (`SyncStatusViewModel.SyncNowCommand`,
`SyncStatusBar` in `MainWindow.axaml`), nur hier noch nicht abgehakt.
- [x] **10.1.5** Statusanzeige erweitern: letzter Sync, Anzahl wartender Events, Fehlerzustand.
**Umsetzung:** War bereits vorhanden (`SyncStatusViewModel`/`SyncStatusBar`), nur hier noch
nicht abgehakt.
- [x] **10.1.6** Lokale Schreibvorgänge atomar an die Outbox (`EventQueue`) anbinden — aktuell ist
das Sync-Grundgerüst registriert, die Repositories erzeugen aber noch keine Sync-Ereignisse. das Sync-Grundgerüst registriert, die Repositories erzeugen aber noch keine Sync-Ereignisse.
**Umsetzung:** `LiteDbContext.OnChange`-Hook (Sync-agnostisch, kein Verweis auf
`LehrerApp.Sync` aus `LehrerApp.Data`) — alle 27 Repositories rufen ihn nach Save/Delete auf,
Kaskaden (`GroupRepository.Delete` u.a.) und Batch-Methoden (`SaveMany`/`DeleteBySession`)
feuern genau ein Ereignis pro betroffener Entität statt pro Collection-Zugriff. In
`AppBootstrapper` an `SyncEventPublisher.Publish` gehängt, das den Hook in ein
AES-verschlüsseltes `EventQueue.Enqueue` übersetzt.
- [x] **10.1.7** Eingehende Sync-Ereignisse tatsächlich auf die lokale Datenbank anwenden.
Bisher komplett fehlender, in dieser Checkliste nicht erfasster Baustein: selbst mit 10.1.6
hätte `SyncEngine.PullAsync` empfangene Ereignisse nur zur Konflikterkennung genutzt, nie in
die lokale LiteDB geschrieben — ankommende Änderungen von anderen Geräten wären nirgends
sichtbar geworden.
**Umsetzung:** Neu `LehrerApp.Sync/EventApplier.cs` — entschlüsselt, dispatcht über eine
explizite `EntityType`-Tabelle, schreibt **immer direkt auf die rohe LiteDB-Collection**,
nie über eine Repository-Save/Delete-Methode (sonst würde der 10.1.6-Hook die gerade
angewendete Änderung als neues ausgehendes Ereignis re-enqueuen — Sync-Ping-Pong). Ein
gemeinsames Suppress-Flag wurde geprüft und verworfen (Timer-Thread vs. UI-Thread — ein Flag
könnte einen echten Nutzer-Save währenddessen verschlucken); der direkte Collection-Zugriff
ist zustandslos und dadurch korrekt. Kaskaden-Fälle nutzen dieselben internen
`LiteDbContext`-Hilfsmethoden wie die Repositories. Mit dediziertem Loop-Prevention-Test
abgesichert (`EventApplierTests`).
**Bekannte v1-Einschränkung:** weiche Geschäftsregeln (`ArchivedGroupWriteGuard`,
Namens-Eindeutigkeit bei Aspekten u.ä.) werden auf diesem Pfad nicht geprüft — nur harte
LiteDB-Unique-Constraints greifen noch und führen zum Überspringen des einzelnen Ereignisses.
Für Einzel-/Wenig-Geräte-Nutzung akzeptiert, siehe 10.3.4.
- [x] **10.1.8** Datei-Anhänge (Dokumentation) über den laufenden Sync mitschicken.
**Umsetzung:** Eigener, unverschlüsselt im JSON-Ereigniskanal nicht mitgeführter Binärkanal
(würde ihn für Fotos/Scans stark aufblähen) — neue Endpunkte
`POST/GET /api/sync/attachments/{storageId}` in `LehrerApp.Api`, neue
`EventQueue`-Warteliste für ausstehende Uploads, `AttachmentSyncer` (Upload, in
`SyncEngine.SyncNowAsync` nach dem Event-Push) und `EventApplier` (Download fehlender
Anhänge nach Anwenden eines `Documentation`-Ereignisses). Original-`StorageId` bleibt beim
Download erhalten (roher `db.Attachments.Upload`-Aufruf statt `IAttachmentStorage.Upload`,
das immer eine neue Id vergäbe).
### 10.2 Server ### 10.2 Server
- [ ] **10.2.1** Benutzerverwaltung/Registrierung prüfen und absichern - [x] **10.2.1** Benutzerverwaltung/Registrierung prüfen und absichern
([Endpoints.cs](LehrerApp.Api/Endpoints/Endpoints.cs)). ([Endpoints.cs](LehrerApp.Api/Endpoints/Endpoints.cs)).
**Umsetzung:** `/api/auth/login` und `/api/auth/register` akzeptierten zuvor jeden
beliebigen Nutzernamen/Passwort und stellten ein gültiges 30-Tage-JWT aus (unadressierte
`// TODO`-Kommentare im Code) — konkrete, ausnutzbare Lücke bei echtem Deployment. Neu
`PasswordHasher` (PBKDF2, Salt pro Nutzer — kein neues NuGet-Paket, gleiche BCL-Technik wie
`SyncCrypto`) und `UserStore` (LiteDB-Collection `users`). `/api/auth/register` ersatzlos
entfernt (kein offener Registrierungs-Endpunkt für ein Einzel-/Familien-Deployment); neue
Nutzer werden per CLI angelegt (`dotnet LehrerApp.Api.dll create-user <name>`, dokumentiert
in `docker/README.md`), damit keine zusätzliche unauthentifizierte Angriffsfläche entsteht.
- [ ] **10.2.2** Rate Limiting und Request-Größenbegrenzung. - [ ] **10.2.2** Rate Limiting und Request-Größenbegrenzung.
- [ ] **10.2.3** Serverseitiges Backup der Event-/Snapshot-Dateien. - [ ] **10.2.3** Serverseitiges Backup der Event-/Snapshot-Dateien.
- [ ] **10.2.4** Docker-Setup in [docker/](docker/) verifizieren und dokumentieren. - [ ] **10.2.4** Docker-Setup in [docker/](docker/) verifizieren und dokumentieren.
@@ -1323,6 +1376,12 @@ ist aber nur aktiv, wenn eine Server-URL konfiguriert ist.
- [ ] **10.3.2** Warnung und Wiederherstellungspfad bei verlorenem Schlüssel. - [ ] **10.3.2** Warnung und Wiederherstellungspfad bei verlorenem Schlüssel.
- [ ] **10.3.3** Prüfen, welche Daten unverschlüsselt über `PlainEventStore` laufen — - [ ] **10.3.3** Prüfen, welche Daten unverschlüsselt über `PlainEventStore` laufen —
personenbezogene Daten dürfen das nicht. personenbezogene Daten dürfen das nicht.
- [ ] **10.3.4** Bekannte v1-Einschränkung aus 10.1.7: weiche Geschäftsregeln greifen beim Anwenden
eingehender Sync-Ereignisse nicht, nur harte LiteDB-Unique-Constraints. Bei mehreren eigenen
Geräten in Randfällen möglich, dass sich Datenstände leicht unterscheiden. Für v1 bewusst
akzeptiert (Einzel-/Wenig-Geräte-Nutzung) — falls das je zum echten Problem wird, müsste der
Event-Applier dieselben Validierungen wie die Repository-Save-Methoden durchlaufen, ohne
dabei erneut ein Sync-Ereignis auszulösen.
--- ---