Commit graph

41 commits

Author SHA1 Message Date
Jens Reinemann
320f0e8880 chore: release v1.7.25 2026-05-18 23:56:45 +02:00
Jens Reinemann
c24a32b033 release: v1.7.24 – ResourceDetailScreen with markdown rendering 2026-05-18 23:42:09 +02:00
Jens Reinemann
26117ac23f feat(app): add ResourceDetailScreen with markdown rendering and clickable cards
- ResourceDetailScreen: full metadata display, download button, markdown description
- ResourceListScreen: cards now clickable, navigates to detail view
- Navigation: added ResourceDetail route with guid parameter
- Dependencies: compose-markdown 0.5.4 (JitPack), added toRoute import
- settings.gradle.kts: added JitPack repository
2026-05-18 23:37:00 +02:00
Jens Reinemann
25c5f4675f chore: version bump 1.7.22 → 1.7.23 2026-05-18 22:28:33 +02:00
Jens Reinemann
0b94a10acf chore: release v1.7.22 2026-05-18 22:01:44 +02:00
Jens Reinemann
5c187db1f9 chore: release v1.7.21 2026-05-18 21:46:15 +02:00
Jens Reinemann
b7ef6af0a4 chore: release v1.7.20 2026-05-18 20:12:09 +02:00
Jens Reinemann
309f6961a5 chore: release v1.7.19 2026-05-18 19:00:14 +02:00
Jens Reinemann
084b315b95 chore: release v1.7.18 2026-05-18 18:30:57 +02:00
Jens Reinemann
4ce585971d chore: release v1.7.17 2026-05-18 17:59:15 +02:00
Jens Reinemann
fb46c83f7b chore: release v1.7.16 2026-05-18 17:58:05 +02:00
Jens Reinemann
bce4abc1dd chore: release v1.7.15 2026-05-18 17:54:17 +02:00
Jens Reinemann
a1a9529b7d chore: release v1.7.14 2026-05-18 15:15:53 +02:00
Jens Reinemann
e3bcddac70 chore: update publish tooling and Android messaging integration 2026-05-18 15:13:49 +02:00
Jens Reinemann
73d5e62a4e chore: release v1.7 (13) 2026-05-18 15:04:59 +02:00
Jens Reinemann
38394c6350 chore: release v1.7 (12) 2026-05-18 13:23:09 +02:00
Jens Reinemann
5eae3a4813 chore: release v1.7 (11) 2026-05-18 12:34:58 +02:00
Jens Reinemann
461fca7ead chore: release v1.7 (10) 2026-05-18 12:09:11 +02:00
Jens Reinemann
8459705bb1 chore: release v1.7 (9) 2026-05-18 11:46:28 +02:00
Jens Reinemann
bb578c5076 chore: version bump 1.6 (7) -> 1.7 (8), CI Deploy-Workflow redesign
- Version 1.7 (versionCode 8) deployed auf bollwerk.online
- android-ci.yml: nur workflow_dispatch, kein auto-Trigger
- Auto-Versionierung: versionCode wird im CI hochgezählt
- CI deployed direkt auf VPS via SCP + API-Call
- VPS: BOLLWERK_ADMIN_TOKEN konfiguriert (einmalig)
Benötigte Secrets in GitHub: BOLLWERK_ADMIN_TOKEN, VPS_SSH_PRIVATE_KEY
2026-05-18 11:09:26 +02:00
Jens Reinemann
d02a38455b chore: version bump 1.5 (6) -> 1.6 (7) 2026-05-18 00:32:53 +02:00
Jens Reinemann
8c0db56223 feat: E2EE Messaging mit Tink HPKE (X25519 + ChaCha20-Poly1305)
Closes #96

## App

- E2EEKeyManager: Tink HPKE Schlüsselpaar generieren, privaten Key
  via EncryptedSharedPreferences sichern, Nachrichten verschlüsseln
  und entschlüsseln (X25519 + ChaCha20-Poly1305)
- EnsureKeyPairUseCase: Keypair-Initialisierung beim App-Start;
  Public Key via HTTP PUT an Server übermitteln
- MainActivity: EnsureKeyPairUseCase.execute() in onCreate
- SettingsKey: E2EEPrivateKeyset + E2EEPublicKeyBase64 als SENSITIVE_KEYS
- MessageRepositoryImpl: sendMessage verschlüsselt Body mit Empfänger-
  Public-Key; eingehende Nachrichten werden lokal entschlüsselt und
  als Klartext in Room gespeichert; Public-Key-Cache (in-memory) +
  key_updated Handler
- WebSocketClient: KeyUpdated Event hinzugefügt
- WebSocketClientImpl: key_updated Frame parsen; Exception-Logging
- Tink 1.15.0 als neue Dependency

## Server

- V4 Flyway Migration: ALTER TABLE users ADD COLUMN public_key TEXT
- Tables.kt: publicKey Feld in Users-Objekt
- UserRepository: getPublicKey() / setPublicKey()
- UserRoutes: PUT /api/users/{id}/public-key (Auth + Owner-Check +
  Längenvalidierung ≤ 10.000 Zeichen) und
  GET /api/users/{id}/public-key
- WebSocketManager: notifyKeyUpdated() Broadcast an alle anderen
  verbundenen Clients
- MessageRepository: EncryptionService für message body bypassed –
  Server speichert E2EE-Ciphertext direkt (Zero-Knowledge)

## Tests

- E2EEKeyManagerTest: 5 Tests (Roundtrip, Nonce-Uniqueness,
  Wrong-Key, hasKeyPair)
- EnsureKeyPairUseCaseTest: 4 Tests (generate+upload, skip wenn
  vorhanden, kein Upload ohne UserId, kein Crash bei Server-Fehler)
- MessageRepositoryImplTest: 5 neue E2EE-Tests

## Docs

- docs/migration-guide.md: E2EE-Einschränkungen dokumentiert
  (Pending-Message Klartext in SQLite)

## Follow-up

- #105: E2EE Private Key – AndroidKeysetManager statt
  CleartextKeysetHandle (Security Hardening)
2026-05-18 00:22:28 +02:00
Jens Reinemann
d00b5b245a chore: version bump 5 -> 6 (1.4 -> 1.5) 2026-05-17 22:47:13 +02:00
Jens Reinemann
045a4b7674 feat: Migration-Safety – Room v7, AutoMigration, Flyway, kein fallbackToDestructiveMigration (#99)
- fallbackToDestructiveMigration() aus DatabaseModule entfernt
- BollwerkDatabase auf Version 7 gebumpt
- AutoMigration(from=5, to=6) und (from=6, to=7) definiert
- MigrationTestHelper-Test migrate6To7_preservesData implementiert
- 7.json Schema-Export generiert
- Server: Flyway 9.22.3 integriert (baselineOnMigrate=true)
- V1__initial_schema.sql + V2__cleanup_user_id.sql angelegt
- Skill android-db-migration erstellt
- versionCode 5 / versionName 1.4
2026-05-17 21:17:24 +02:00
Jens Reinemann
3d7c01cef5 feat(update): AlertDialog bei verfuegbarem Update anzeigen
- AlertDialog in MainScreen zeigt verfuegbare Version mit Bestaetigung
- UpdateBanner blendet bei UpdateStatus.Available aus (Dialog uebernimmt)
- FEATURE_CAMERA_ENABLED temporaer deaktiviert

fix(server): Logo-Pfad und statische Ressourcen bereinigen

- /res-Route fuer classpath-Assets (logo.png etc.) hinzugefuegt
- Logo-Pfad von /static/logo.png auf /res/logo.png korrigiert
- Build-Nummer aus Versionsanzeige auf der Homepage entfernt

ci: GitHub Actions Workflow fuer Swift-Tests hinzugefuegt

style: Tabellenformatierung im Code-Reviewer-Agenten bereinigt
2026-05-17 20:52:47 +02:00
Jens Reinemann
aafb9ddd64 fix: Verbindungs-Noise unterdrücken
- loadSettings() ruft connect() nur noch auf wenn ConnectionState
  NotConfigured oder Disconnected ist – verhindert den self-triggering
  Loop (pullSync → loadSettings → connect → Connecting → Connected → …)
- pullSync() erhält Parameter silent=true für den initialen Connect-Sync;
  zeigt kein 'Empfange Inventar-Update…' mehr beim bloßen Verbindungsaufbau
- Version 1.2 (3) → 1.3 (4)
2026-05-17 19:05:37 +02:00
Jens Reinemann
a5f89e6a69 rename: Krisenvorrat -> Bollwerk
- Package: de.krisenvorrat.* -> de.bollwerk.*
- Klassen: KrisenvorratApp/Database/Theme -> Bollwerk*
- ApplicationId: de.bollwerk.app
- Server: BOLLWERK_* Env-Vars, bollwerk HOCON-Config
- Docker: bollwerk-server/db/backup Container-Namen
- Room DB: bollwerk.db, SharedPrefs: bollwerk_secure_prefs
- Export-Dateien: bollwerk_export/inventar
- UI-Strings, HTML, Admin-UI: alle auf Bollwerk
- Docs, Skills, README angepasst
- Alle Tests gruen, Build erfolgreich
2026-05-17 17:44:02 +02:00
Jens Reinemann
eb5bdd4b7b feat(security): JWT-Tokens in EncryptedSharedPreferences speichern
Sensitive Keys (auth_access_token, auth_refresh_token, auth_username,
auth_user_id, openai_api_key) werden jetzt ueber EncryptedSharedPreferences
gespeichert statt als Klartext in der Room-Settings-Tabelle.

Neue Dateien:
- SecureTokenStorage: Interface fuer sichere Key-Value-Speicherung
- EncryptedPrefsTokenStorage: Implementierung mit AndroidX Security Crypto
- SecurityModule: Hilt-Provider fuer SecureTokenStorage

Aenderungen:
- SettingsKeys: SENSITIVE_KEYS Set definiert welche Keys verschluesselt werden
- SettingsRepositoryImpl: Routet sensitive Keys an SecureTokenStorage,
  nicht-sensitive weiterhin an Room DAO
- ImportExportRepositoryImpl: Filtert sensitive Keys bei Export und Import
- SettingsRepositoryImplTest: 4 neue Tests fuer Secure-Storage-Routing

Closes #72
2026-05-17 02:55:43 +02:00
Jens Reinemann
4c2f5f08a4 feat(app): User-Konzept App-Phase - JWT-Auth, Login, WebSocket-Client (#57)
- SettingsKeys: API_KEY entfernt, AUTH_ACCESS_TOKEN/REFRESH_TOKEN/USERNAME hinzugefügt
- SyncService: login() und logout() Interface-Methoden
- SyncServiceImpl: Bearer-Token statt X-API-Key, Auto-Refresh bei 401
- AuthModels: LoginRequest, LoginResponse, RefreshRequest
- WebSocketClient: Interface + Impl mit exponentiellem Backoff
- SettingsViewModel: Login/Logout, WebSocket-Connect, FullSyncRequired auto-pullSync
- SettingsScreen: Login-Formular (Username + Passwort) statt API-Key-Feld
- NetworkModule: WebSocketClient als Singleton gebunden
- Alle Tests gruen (70 Tasks up-to-date)
2026-05-16 19:45:11 +02:00
Jens Reinemann
809e6aa069 feat: FEATURE_CAMERA_ENABLED compile flag für KI-Kamera
- build.gradle.kts: buildConfigField FEATURE_CAMERA_ENABLED (default: true)
- ItemListScreen: Camera-Icon nur wenn Flag gesetzt, onCameraClick optional
- KrisenvorratNavGraph: CameraCapture-Route und Lambda nur wenn Flag gesetzt
- SettingsScreen: KI-Erkennung-Section nur wenn Flag gesetzt
2026-05-16 18:06:12 +02:00
Jens Reinemann
f4b5197b06 infra: DB-Migration-Infrastruktur einrichten (#49)
- fallbackToDestructiveMigration() entfernt (war inakzeptabel)
- addMigrations(MIGRATION_1_2) in DatabaseModule eingetragen
- Migrations.kt: Migration(1,2) mit Tabellen-Neubau fuer SQLite < 3.25
  (kcal_per_100g -> kcal_per_kg, min_stock entfernt)
- exportSchema = true + KSP-Argument room.schemaLocation = app/schemas/
- 2.json Schema-Snapshot eingecheckt (Basis fuer kuenftige Migrationen)
- androidTest-Assets zeigen auf app/schemas/ (fuer MigrationTestHelper)
- KrisenvorratDatabaseMigrationTest: 4 instrumentierte Tests
  - Datenerhalt nach Migration
  - Korrekte Spalten nach Migration
  - Indices nach Migration
  - Fresh-Install ohne Migration
2026-05-16 14:52:06 +02:00
Jens Reinemann
215790d68e feat(app): add Ktor HTTP client and SyncService for inventory sync
domain/model/SyncError.kt:
- Sealed class with ConnectionError, Timeout, AuthError, ServerError,
  NotConfigured, Unknown subtypes for typed error handling

domain/repository/SyncService.kt:
- Interface with downloadInventory() and uploadInventory() returning
  Result<InventoryDto> for clean error propagation

data/sync/SyncServiceImpl.kt:
- Ktor Client implementation using OkHttp engine
- GET /api/inventory and PUT /api/inventory endpoints
- X-API-Key header authentication matching server contract
- Server URL and API key read from SettingsRepository
- withContext(Dispatchers.IO) for network calls
- Catches SocketTimeoutException, ConnectException specifically

di/NetworkModule.kt:
- Hilt module providing singleton HttpClient with OkHttp engine
- ContentNegotiation with kotlinx.serialization JSON
- Configurable connect/read/write timeouts (10s/30s/30s)
- Binds SyncServiceImpl to SyncService interface

Dependencies:
- ktor-client-core, ktor-client-okhttp,
  ktor-client-content-negotiation, ktor-client-mock (test)
- ktor-serialization-kotlinx-json (shared with server)
- INTERNET permission added to AndroidManifest.xml

Tests: 9 tests with Ktor MockEngine covering success, 401, 500,
missing config, trailing slash URL normalization

Closes #44
2026-05-14 21:14:40 +02:00
Jens Reinemann
c0c4978ccf feat(shared): add shared module with common DTO models
New Gradle module :shared (pure Kotlin/JVM) containing @Serializable
DTO classes for use by both the Android app and future Ktor server.

shared/src/main/kotlin/de/krisenvorrat/shared/model/:
- InventoryDto: root DTO replacing ExportData (version, categories,
  locations, items, settings)
- CategoryDto, LocationDto, ItemDto, SettingDto: extracted from
  the former *Export data classes in :app

Migration in :app:
- ExportData.kt deleted (classes moved to :shared)
- ImportExportRepositoryImpl now imports from de.krisenvorrat.shared.model
- app/build.gradle.kts adds implementation(project(:shared))

Build config:
- libs.versions.toml: added kotlin-jvm plugin entry
- build.gradle.kts (root): registered kotlin-jvm plugin
- settings.gradle.kts: include(:shared)

JSON wire format is unchanged; all 165 existing tests pass.

Closes #39
2026-05-14 19:50:23 +02:00
Jens Reinemann
8193445939 feat(settings): add JSON/Markdown export via Share Intent
Implement export functionality in the Settings screen allowing users to
share their inventory data as JSON (via FileProvider + ACTION_SEND with
EXTRA_STREAM) or Markdown (via ACTION_SEND with EXTRA_TEXT).

Key changes:
- ShareContent sealed interface for export events (Json with URI,
  Markdown with text)
- SettingsViewModel: exportJson() writes to cache file and creates
  FileProvider URI; exportMarkdown() provides text directly
- SettingsUiState: isExporting, shareContent, exportError fields
- SettingsScreen: LaunchedEffect consumes share events and opens
  Android Share Sheet via Intent.createChooser
- FileProvider registered in AndroidManifest with cache-path config
- MockK added as test dependency for FileProvider static mocking
- 8 new unit tests covering export success, failure, and state cleanup

Closes #37
2026-05-14 03:26:15 +02:00
Jens Reinemann
a4c0dc63b4 feat(navigation): implement Bottom Navigation Bar with 4 tabs and app shell
MainScreen.kt: new app shell with Scaffold + Material 3 NavigationBar
providing 4 tabs (Uebersicht, Inventur, Warnungen, Einstellungen).

TopLevelDestination.kt: enum defining tab routes, icons (Home, Inventory2,
Warning, Settings), and labels for the navigation bar.

Screen.kt: added Warnings and Settings sealed interface members.

KrisenvorratNavGraph.kt: accepts Modifier, added Warnings/Settings
composables, removed obsolete DashboardScreen navigation callback.

DashboardScreen.kt: removed Scaffold wrapper and onNavigateToItems param,
now uses Column layout (TopAppBar handled inline).

ItemListScreen.kt: removed onDashboardClick param and Dashboard menu entry
(no longer needed with tab navigation).

WarningsScreen.kt, SettingsScreen.kt: placeholder screens for future impl.

MainActivity.kt: delegates to MainScreen instead of NavGraph directly.

Added material-icons-extended dependency for Inventory2 icon.

Closes #33
2026-05-14 02:25:47 +02:00
Jens Reinemann
a27660fd4a feat(ui): add category and location management screens
Closes #25

ui/category/:
- CategoryListViewModel: StateFlow-based ViewModel with add/delete
  dialog state management, backed by CategoryRepository
- CategoryListScreen: Material 3 Scaffold with LazyColumn, FAB for
  adding, delete confirmation dialog with CASCADE warning

ui/location/:
- LocationListViewModel: same pattern for LocationRepository
- LocationListScreen: same UI pattern for location management

Tests:
- CategoryListViewModelTest: 11 tests covering init, add, delete,
  dialog state, blank name rejection
- LocationListViewModelTest: 11 tests (same coverage)

Dependencies:
- Added lifecycle-runtime-compose for collectAsStateWithLifecycle
- Added kotlinx-coroutines-test for ViewModel unit tests
2026-05-14 00:56:36 +02:00
Jens Reinemann
b719739451 feat(db): Room-Datenbank & DAOs implementieren (#18)
- KrisenvorratDatabase mit allen 4 Entities und LocalDateConverter
- CategoryDao, LocationDao, ItemDao, SettingsDao mit CRUD und Flow-Queries
- ItemDao.getExpiringSoon(daysUntil) als Default-Interface-Methode
- SettingsDao mit @Upsert (Room 2.6.1)
- Instrumentierungstests für alle 4 DAOs (in-memory DB)
- androidx.room:room-testing zu Dependencies ergänzt
2026-05-13 23:18:48 +02:00
Jens Reinemann
22154cd5b8 chore: bump version to 1.2 (versionCode 3)
Verified via hot-reload workflow on running emulator.
UI shows 'Krisenvorrat' + 'v1.2' (confirmed via screenshot + uiautomator).

Closes #16
2026-05-13 22:33:24 +02:00
Jens Reinemann
6603016369 feat(skills): add hot-reload action and robust screenshot script
android-dev.ps1:
- Added 'hot-reload' action: build + force-stop + install + launch on
  a running emulator/device without restart (saves 60-90s vs deploy-emulator)
- Removed 'screenshot' action (replaced by standalone script)

screenshot.ps1 (new):
- Uses adb pull instead of exec-out pipe to avoid PowerShell's UTF-16
  CRLF corruption of binary data (root cause of all broken screenshots)
- Validates PNG magic bytes after capture
- ADB commands wrapped with configurable timeout (prevents hangs)
- Optional -UiDump flag extracts visible text via uiautomator for
  automated verification without image viewing

SKILL.md:
- Documented hot-reload action

app/build.gradle.kts:
- Version bump 1.0 → 1.1 (versionCode 1 → 2)
2026-05-13 22:27:06 +02:00
Jens Reinemann
c818b0d46e feat(ui): add version number to start screen
app/build.gradle.kts:
- Enabled buildConfig in buildFeatures to expose VERSION_NAME

app/src/main/java/de/krisenvorrat/app/MainActivity.kt:
- Replaced plain Text greeting with centered Column layout
- Shows app title 'Krisenvorrat' (headlineLarge) and version
  'v1.0' via BuildConfig.VERSION_NAME (bodyMedium, onSurfaceVariant)

Verified: built, deployed to emulator, and confirmed via screenshot.
2026-05-13 21:57:21 +02:00
Jens Reinemann
040f007cd5 feat: Android-Projekt-Gerüst anlegen (#13)
- Gradle Kotlin DSL (settings.gradle.kts, build.gradle.kts)
- Version Catalog (libs.versions.toml) mit Compose BOM, Hilt, Room,
  Navigation Compose, kotlinx.serialization
- App-Modul (app/build.gradle.kts), minSdk 26, compileSdk 35
- AndroidManifest.xml mit KrisenvorratApp + MainActivity
- KrisenvorratApp (@HiltAndroidApp)
- MainActivity (@AndroidEntryPoint, Jetpack Compose + Material3)
- KrisenvorratTheme (ui/theme/Theme.kt)
- MVVM-Paketstruktur: data/, domain/, presentation/, di/
- Adaptive Launcher-Icons (mipmap-anydpi-v26)
- Gradle Wrapper 8.11.1 (gradlew, gradlew.bat, gradle-wrapper.jar)
2026-05-13 15:24:39 +02:00