- Add getUndeliveredStorageBytes() and evictOldestUndelivered() to MessageRepository
- Check mailbox size before saving; evict oldest undelivered messages if over 10 MB
- Return systemMessage in SendMessageResponse when eviction occurs
- App parses systemMessage and displays it in the sender's conversation
- Add SendMessageResponse to shared module for server/app interop
- Update existing tests to use new response format
- Add 3 new tests for eviction behavior
- Add VersionStore that persists versionCode/versionName to data/version.json
- Add POST /api/admin/version secured by BOLLWERK_ADMIN_TOKEN bearer auth
- GET /api/version now reads from VersionStore (fallback to env-vars)
- Update publish-apk.ps1 to use API call instead of SSH+sed+restart
- Update publish SKILL.md and vps-deploy SKILL.md documentation
Closes#100
When both access and refresh token are invalid (401 on /auth/refresh),
the app now automatically logs out and navigates to Settings (login form).
No data loss - only auth tokens are cleared, local inventory data is intact.
- AuthEventBus: singleton SharedFlow that signals session expiry
- MainViewModel: observes bus, calls logout + disconnect, navigates to Settings
- MainScreen: LaunchedEffect collects navigateToSettings event
- MessageRepositoryImpl: emits session expired when refresh fails
- SyncServiceImpl: emits session expired when refresh fails
fetchUsers() and attemptSendToServer() had no retry logic on 401.
SyncServiceImpl already had this pattern (auto-refresh on 401).
Add private refreshAccessToken(serverUrl) and retry once on 401
in both methods.
SENSITIVE_KEYS and SENSITIVE_KEY_STRINGS used eager initialization in the
companion object. When E2EEKeyManager.hasKeyPair() was the first access to
SettingsKey, it triggered SettingsKey.<clinit> which tried to resolve
StringKey.E2EEPrivateKeyset - but that class was already 'in initialization
by the current thread' (JVM spec). The JVM returned null, causing NPE in
SENSITIVE_KEY_STRINGS.map { it.key }.
Fix: use by lazy for both properties to defer initialization past <clinit>.
- inventar.md: 38 Artikel aus handschriftlicher Liste erfasst (Lebensmittel,
Medikamente, Ausrüstung, Hygiene, Energie)
- import-inventar.py: Python-Skript zum einmaligen PUT /api/inventory
(UTF-8-safe via ensure_ascii=True, kein PowerShell-Encoding-Problem)
- import-inventar.ps1: PowerShell-Variante (Umlaute via [char]-Variablen)
PS5.1 kodiert String-Bodies in Invoke-RestMethod mit der Windows-Systemkodierung
(Windows-1252) statt UTF-8. Fix: Non-ASCII-Zeichen werden vor dem Senden als
\uXXXX escaped, sodass der HTTP-Body reines ASCII ist und korrekt interpretiert
wird. Schliesst die UTF-8-Testfaelle (Szenario 6a) vollstaendig ab: 35/35 PASS.
- Add EncryptionService (AES-256-GCM) with passthrough when no key set
- Flyway V3: enable pgcrypto extension + widen name columns to TEXT
- DatabaseFactory: init EncryptionService from BOLLWERK_DB_ENCRYPTION_KEY,
run migrateEncryptData() to encrypt existing plaintext rows on startup
- InventoryRepository: encrypt on write, decrypt on read for
items.name, items.notes, categories.name, locations.name, settings.value
- MessageRepository: encrypt body on write, decrypt on read
- docker-compose.yml: document BOLLWERK_DB_ENCRYPTION_KEY env var
- docker-compose-vps.yml: pass BOLLWERK_DB_ENCRYPTION_KEY from .env
- .env.example: add key generation template
- .gitignore: add .env to ignore list
Closes#98
- 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
- Status-Dot + Verbindungstext links, Sync-Button (Refresh-Icon) rechts
in einer kompakten Zeile statt verstreuter Einzelelemente
- Bei Disconnect: Haupttext 'Keine Verbindung', Countdown als
zweite Zeile darunter (statt alles in einer langen Zeile)
- Sync-Spinner ersetzt den Button solange Sync läuft
- Aktivitätsmeldung animiert unter der Statuszeile
- Letzte-Sync-Zeit mit Divider am Card-Boden, klar abgetrennt
- 'Synchronisierung erfolgt automatisch.' entfernt (redundant)
- Neues SyncActivityMessage.CheckingForChanges 'Prüfe auf Änderungen…'
wird beim initialen Catch-up nach Connect angezeigt (statt dem
irreführenden 'Empfange Inventar-Update…')
- Neues SyncActivityMessage.NoChanges 'Keine Änderungen'
wird angezeigt wenn der initiale Sync keine Daten zurückliefert
- Echter Server-Push (InventoryUpdated/FullSyncRequired) zeigt weiterhin
'Empfange Inventar-Update…' → jede echte Kommunikation bleibt sichtbar
- 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)
Both android-ci.yml and ci.yml now only run via workflow_dispatch
(manual trigger). Automatic builds on push/PR are disabled to stop
failing pipeline notifications.
Replace all HTTP references to 195.246.231.210 with bollwerk.online
across skills, prompts, scripts, and app default settings:
- Dockerfile: rename KRISENVORRAT_JWT_SECRET to BOLLWERK_JWT_SECRET
- SettingsKey.kt: default server URL now http://bollwerk.online:8080
- publish SKILL/prompt/script: HTTP URLs updated to bollwerk.online
- vps-deploy SKILL: Admin-UI and health-check URLs updated
- run-integration-tests.ps1: default BaseUrl updated
SSH commands (ssh/scp) intentionally kept on IP, as DNS is not
used for SSH access.
- Altes Vektor-Foreground (IKEA Gosig-Style) durch PNG-basiertes Logo ersetzt
- Neue Ratte: grau, illustrativ, bronzener Plättchenpanzer, Patronengurt
- Adaptive-Icon-Foreground als PNG in allen Density-Stufen (mdpi–xxxhdpi)
- Legacy-Launcher-Icons mit dunklem Hintergrund (#16140F)
- Hintergrund von Olivgrün (#3D5229) auf App-Theme-Dunkel (#16140F) geändert
- Web-Logo auf Download-Seite eingebunden (/static/logo.png)
- Quelldatei unter docs/ratti.png archiviert
Closes#93
- Color.kt: Farbpalette von Olivgrün auf Admin-Palette umgestellt
(burnt orange #C1440E primary, warm beige #E8D5B0 text,
dark brown #16140F background, tan #A89070 labels)
- Type.kt: Neue Typography mit Monospace für Headings/Titles/Labels
(analog Admin Share Tech Mono), system-ui für Body
- Theme.kt: KrisenvorratTypography eingebunden
- Dark Mode vollständig spezifiziert, Admin-konsistent
- Alle bestehenden Screens profitieren automatisch via MaterialTheme
- Build ✅, alle Tests grün (70 tasks)
- Neues DTO: InventoryStatsPerInventoryDto (inventoryId, inventoryName,
totalItems, totalLocations, totalCategories, recentTransactions,
lastUpdated, userCount)
- InventoryRepository.getStatsPerInventory(): Stats pro Inventar
- Neuer Endpoint: GET /api/admin/stats/inventories (Admin-only)
- Admin-UI: aufklappbarer Bereich Statistiken pro Inventar (sortierbar)
- Admin-UI: Inventar-Karten durch Tabelle ersetzt mit
- Paging (10/25/50 Eintraege pro Seite)
- Sortierung per Klick auf Spaltenheader
- Filter (alle / mit Benutzern / ohne Benutzer)
- Freitextsuche nach Name oder Inventar-ID
- Tests: 3 neue InventoryRepositoryTests, 3 neue InventoryStatsTests
(401/403/Inhalt fuer neuen Endpoint), setUp bereinigt alle Tabellen
- Alle 148 Tests gruen