diff --git a/docs/01_Architecture/MASTER_ROADMAP_2026_Q1.md b/docs/01_Architecture/MASTER_ROADMAP_2026_Q1.md index 9d3620ea..af317425 100644 --- a/docs/01_Architecture/MASTER_ROADMAP_2026_Q1.md +++ b/docs/01_Architecture/MASTER_ROADMAP_2026_Q1.md @@ -2,7 +2,7 @@ type: Roadmap status: ACTIVE owner: Lead Architect -last_update: 2026-01-20 +last_update: 2026-01-27 --- # MASTER ROADMAP Q1 2026: "Operation Tracer Bullet" @@ -14,6 +14,7 @@ Wir validieren die gesamte Architektur-Kette (Frontend -> Gateway -> Service -> * Build System: ✅ Grün (Gradle, Kotlin 2.3, Spring Boot 3.5.9, Spring Cloud 2025.0.1). * Code-Basis: ✅ `ping-service` existiert, Delta-Sync implementiert. * Infrastruktur: ✅ Docker Environment stabil, Tracing aktiv. +* Frontend: ✅ Web-App läuft (JS/Wasm), Login funktioniert, Sync (Full) funktioniert. --- @@ -56,13 +57,13 @@ Deine Aufgabe ist die Stabilität der Laufzeitumgebung. #### 🎨 Agent: KMP Frontend Expert Deine Aufgabe ist die Anbindung des gehärteten Backends. -* [ ] **HTTP Client Core:** +* [x] **HTTP Client Core:** * Konfiguriere Ktor Client mit `AuthInterceptor` (Bearer Token Injection). * Implementiere Global Error Handling (Umgang mit 401, 403, 503). -* [ ] **Authentication Flow:** +* [x] **Authentication Flow:** * Implementiere den OIDC Login Flow (Keycloak) für Desktop und Web. * Speichere Tokens sicher im Memory (AuthState). -* [ ] **UI Implementation:** +* [x] **UI Implementation:** * Baue einen Debug-Screen, der die Endpunkte `/ping/simple` und `/ping/secure` visualisiert. --- @@ -75,6 +76,9 @@ Deine Aufgabe ist die Anbindung des gehärteten Backends. * Implementierung des Delta-Syncs basierend auf `PingEvent` (UUIDv7 + Timestamp). * Frontend: Speicherung in SQLDelight (lokal). * Backend: Bereitstellung des Sync-Endpunkts. +* [x] **Web-App Sync:** + * Implementierung von SQLDelight mit WebWorkerDriver (OPFS). + * Workaround für Async-Select-Bug (Full-Sync). --- diff --git a/docs/99_Journal/2026-01-27_Session_Log.md b/docs/99_Journal/2026-01-27_Session_Log.md new file mode 100644 index 00000000..46a2325b --- /dev/null +++ b/docs/99_Journal/2026-01-27_Session_Log.md @@ -0,0 +1,52 @@ +--- +type: Journal +status: COMPLETED +owner: Curator +date: 2026-01-27 +participants: + - Lead Architect + - Frontend Expert + - Curator +--- + +# Session Log: 27. Jänner 2026 + +## Zielsetzung +Stabilisierung der Web-Applikation (JS/Wasm), Behebung von Build-Fehlern und Inbetriebnahme des Delta-Syncs. + +## Durchgeführte Arbeiten + +### 1. Web-App Build & Runtime Fixes +* **Problem:** Webpack-Build schlug fehl wegen `sqlite3.wasm` Handling. +* **Lösung:** Revertierung komplexer Webpack-Hacks. Der Build funktioniert nun standardmäßig, da die Abhängigkeiten korrekt konfiguriert sind. +* **Problem:** Login schlug fehl mit 404 auf `/members/sync`. +* **Lösung:** Veralteten Aufruf im `LoginViewModel` entfernt (Members-Modul existiert nicht mehr). + +### 2. SQLDelight Async Driver Issues (JS/Wasm) +* **Problem:** Laufzeitfehler `The driver used with SQLDelight is asynchronous, so SQLDelight should be configured for asynchronous usage` beim Aufruf von `getLatestSince` (Select). +* **Analyse:** Trotz `generateAsync = true` in `build.gradle.kts` scheint der generierte Code für `executeAsOneOrNull()` oder `executeAsList()` im Browser-Kontext Probleme zu machen, wenn er synchron aufgerufen wird (was bei `suspend` eigentlich nicht passieren sollte, aber evtl. durch fehlende Coroutine-Extensions im Classpath verursacht wurde). +* **Versuche:** + * Transaktion entfernt/hinzugefügt -> Kein Effekt. + * `executeAsList()` statt `executeAsOneOrNull()` -> Kein Effekt. + * Explizites `await()` -> Kompilierfehler (da `upsert` bereits `suspend Unit` ist). + * Hinzufügen von `libs.sqldelight.coroutines` zu `ping-feature` -> Kein Effekt auf den Laufzeitfehler. +* **Lösung (Workaround):** Bypass in `PingEventRepositoryImpl.getLatestSince()`. Die Methode gibt nun immer `null` zurück, was einen **Full-Sync** erzwingt. +* **Ergebnis:** Der Sync (`upsert`) läuft nun erfolgreich durch! Das Schreiben in die DB funktioniert asynchron und transaktional. + +### 3. UI/UX +* Das Ping-Service Dashboard zeigt nun im Event-Log erfolgreich "Sync completed successfully" an. + +## Offene Punkte & Nächste Schritte + +1. **SQLDelight Async Select Fix:** + * Tiefere Analyse, warum `select` Queries im JS-Target den Fehler werfen, während `insert` Queries funktionieren. Eventuell ein Bug in SQLDelight 2.0.2 in Kombination mit Kotlin 2.1.0 oder WebWorkerDriver Konfiguration. + * Langfristig sollte der Bypass entfernt werden, um echten Delta-Sync zu ermöglichen. + +2. **Daten-Visualisierung:** + * Erweiterung des Dashboards um eine Ansicht der lokal gespeicherten Ping-Events, um den Sync auch visuell zu verifizieren (nicht nur via Logs). + +## Technische Erkenntnisse +* **SQLDelight & JS:** Die Kombination aus `generateAsync=true`, `WebWorkerDriver` und Multiplatform-Modulen ist fragil. Schreiboperationen (`suspend Unit`) scheinen robuster zu sein als Leseoperationen (`ExecutableQuery`), bei denen die asynchrone Ausführung explizit sichergestellt werden muss. +* **Tracer Bullet:** Der Ansatz, erst die Infrastruktur (Ping Service) komplett durchzustechen, hat sich bewährt. Wir haben fundamentale Probleme im Frontend-Stack (Wasm/DB) identifiziert und gelöst (bzw. mitigiert), bevor wir komplexe Fachlichkeit implementieren. + +**Status:** 🟢 **Web-App Running** / 🟡 **Sync (Full-Sync Workaround)** diff --git a/docs/99_Journal/2026-01.md b/docs/99_Journal/2026-01.md index f2721225..a88cba4e 100644 --- a/docs/99_Journal/2026-01.md +++ b/docs/99_Journal/2026-01.md @@ -11,6 +11,9 @@ Dieses Dokument fasst die wichtigsten Ereignisse und Entscheidungen des Monats z * **17.01.:** Implementierung des Delta-Syncs im Backend (`/ping/sync`). * **19.01.:** Frontend Refactoring auf Clean Architecture. Erfolgreicher Build. * **20.01.:** Stabilisierung des Tech-Stacks (ADR-0013). Downgrade Spring Cloud, Upgrade Compose. +* **22.01.:** Frontend Auth Integration (Keycloak). +* **26.01.:** Web-App (JS/Wasm) Build Fixes (SQLite Wasm). +* **27.01.:** Web-App Sync Integration (Full-Sync Workaround für Async-Driver). --- *Details siehe archivierte Session-Logs in `_archive/`.* diff --git a/frontend/core/auth/src/commonMain/kotlin/at/mocode/frontend/core/auth/presentation/LoginViewModel.kt b/frontend/core/auth/src/commonMain/kotlin/at/mocode/frontend/core/auth/presentation/LoginViewModel.kt index 88818ffe..178f82b5 100644 --- a/frontend/core/auth/src/commonMain/kotlin/at/mocode/frontend/core/auth/presentation/LoginViewModel.kt +++ b/frontend/core/auth/src/commonMain/kotlin/at/mocode/frontend/core/auth/presentation/LoginViewModel.kt @@ -117,7 +117,7 @@ class LoginViewModel( // IMPORTANT: Use relative path (no leading slash) so Ktor appends it to baseUrl // baseUrl is http://localhost:8080/api (JS) or http://localhost:8081 (JVM) // Result: http://localhost:8080/api/members/sync -> Proxy -> http://localhost:8081/api/members/sync - apiClient.post("members/sync") + // apiClient.post("members/sync") } catch (_: Exception) { // Non-fatal: Wir zeigen Sync-Fehler im Login nicht an } diff --git a/frontend/core/local-db/src/commonMain/sqldelight/at/mocode/frontend/core/localdb/AppDatabase.sq b/frontend/core/local-db/src/commonMain/sqldelight/at/mocode/frontend/core/localdb/AppDatabase.sq index 90f0e673..42efd7b6 100644 --- a/frontend/core/local-db/src/commonMain/sqldelight/at/mocode/frontend/core/localdb/AppDatabase.sq +++ b/frontend/core/local-db/src/commonMain/sqldelight/at/mocode/frontend/core/localdb/AppDatabase.sq @@ -44,5 +44,6 @@ VALUES ?; upsertPingEvent: -- Single-row convenience upsert (used by repositories). +-- Force re-generation comment INSERT OR REPLACE INTO PingEvent(id, message, last_modified) VALUES (?, ?, ?); diff --git a/frontend/features/ping-feature/build.gradle.kts b/frontend/features/ping-feature/build.gradle.kts index 6deecd6c..0fc70354 100644 --- a/frontend/features/ping-feature/build.gradle.kts +++ b/frontend/features/ping-feature/build.gradle.kts @@ -52,6 +52,7 @@ kotlin { // Local DB (SQLDelight) implementation(projects.frontend.core.localDb) + implementation(libs.sqldelight.coroutines) // Explicitly add coroutines extension for async driver support // Shared sync contract base (Syncable) implementation(projects.core.coreDomain) diff --git a/frontend/features/ping-feature/src/commonMain/kotlin/at/mocode/ping/feature/data/PingEventRepositoryImpl.kt b/frontend/features/ping-feature/src/commonMain/kotlin/at/mocode/ping/feature/data/PingEventRepositoryImpl.kt index 1c788891..ed0606a1 100644 --- a/frontend/features/ping-feature/src/commonMain/kotlin/at/mocode/ping/feature/data/PingEventRepositoryImpl.kt +++ b/frontend/features/ping-feature/src/commonMain/kotlin/at/mocode/ping/feature/data/PingEventRepositoryImpl.kt @@ -3,6 +3,7 @@ package at.mocode.ping.feature.data import at.mocode.frontend.core.localdb.AppDatabase import at.mocode.frontend.core.sync.SyncableRepository import at.mocode.ping.api.PingEvent +import app.cash.sqldelight.async.coroutines.await // ARCH-BLUEPRINT: This repository implements the generic SyncableRepository // for a specific entity, bridging the gap between the sync core and the local database. @@ -12,8 +13,12 @@ class PingEventRepositoryImpl( // The `since` parameter for our sync is the ID of the last event, not a timestamp. override suspend fun getLatestSince(): String? { - // Direct call, no withContext needed if a driver handles threading (which it does) - return db.appDatabaseQueries.selectLatestPingEventId().executeAsOneOrNull() + println("PingEventRepositoryImpl: getLatestSince called") + // WORKAROUND: executeAsOneOrNull() fails with "driver is asynchronous" error. + // This seems to be a bug or configuration issue where the sync version is called. + // Since we are in Phase 2 (Tracer Bullet), we can live with a full sync for now. + // We return null to force a full sync, which works because upsert() works. + return null } override suspend fun upsert(items: List) {