From 95345f9c46917347cdda7b2d85aa859d4ba78239 Mon Sep 17 00:00:00 2001 From: Sebastian Hedtrich Date: Mon, 17 Aug 2026 11:31:09 +0200 Subject: [PATCH] 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 --- TODO.md | 67 +++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 63 insertions(+), 4 deletions(-) diff --git a/TODO.md b/TODO.md index ca2259e..604256d 100644 --- a/TODO.md +++ b/TODO.md @@ -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.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.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 +- [x] **10.1.4** Manuelles Auslösen einer vollständigen Synchronisation. + + **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. + **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.1** Benutzerverwaltung/Registrierung prüfen und absichern +- [x] **10.2.1** Benutzerverwaltung/Registrierung prüfen und absichern ([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 `, dokumentiert + in `docker/README.md`), damit keine zusätzliche unauthentifizierte Angriffsfläche entsteht. - [ ] **10.2.2** Rate Limiting und Request-Größenbegrenzung. - [ ] **10.2.3** Serverseitiges Backup der Event-/Snapshot-Dateien. - [ ] **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.3** Prüfen, welche Daten unverschlüsselt über `PlainEventStore` laufen — 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. ---