# Weegschaal App _*Gemaakt met vibecoding — DeepSeek V4 Pro*_ React Native (Expo) app voor het invoeren en synchroniseren van lichaamsmetingen (weegschaal). Data wordt lokaal opgeslagen in SQLite en gesynchroniseerd via een eigen REST API met timestamp-based delta-sync. ## Inhoud - [Functionaliteit](#functionaliteit) - [Schermen](#schermen) - [Synchronisatie](#synchronisatie) - [Server-side API](#server-side-api) - [Beveiliging](#beveiliging) - [Offline resilience](#offline-resilience) - [Ontwikkeling](#ontwikkeling) - [Build & deploy](#build--deploy) - [Testen](#testen) - [Licentie](#licentie) ## Functionaliteit - **Metingen invoeren** van een slimme weegschaal: gewicht, BMI, vetpercentage, vetmassa, spierpercentage, spiermassa, buikvet en stofwisseling - **Dashboard** met laatste en voorlaatste meting, gewichtsverschil, en grafiek - **Delen** van meetresultaten als PNG-afbeelding via WhatsApp, e-mail, etc. - **Historie** van alle metingen, gesorteerd op datum - **Offline-first**: metingen worden lokaal opgeslagen en later automatisch gesynchroniseerd - **Instellingen** voor API-configuratie en handmatige sync ## Schermen | Scherm | Beschrijving | |--------|-------------| | Dashboard | Laatste meting, voorlaatste meting met gewichtsverschil, grafiek, deel-knop | | Nieuwe meting / Wijzigen | Formulier met datumkiezer, waarden vooraf ingevuld vanaf laatste meting | | Meting detail | Alle meetwaarden met knoppen voor wijzigen en verwijderen | | Historie | FlatList van alle metingen, pull-to-refresh, tik voor detail | | Instellingen | API URL + token, opslaan/wissen, handmatige sync | ## Synchronisatie De app synchroniseert via een zelfgebouwde REST API. Geen WebDAV, geen bestandssync — pure delta-sync. ### Hoe het werkt 1. **Delta ophalen**: `GET /api/v1/measurements?since=lastSyncAt` — alleen gewijzigde records van de server 2. **Mergen**: last-writer-wins op `updatedAt` 3. **Pushen**: alle lokale records met `updatedAt > lastSyncAt` → POST naar API 4. **Verwijderen**: soft delete via `deletedAt` timestamp → DELETE naar API bij volgende sync 5. **High-water mark**: `lastSyncAt` bijgewerkt na elke succesvolle sync ### Waarom timestamp-based? Geen `syncStatus` enum, geen speciale gevallen. Hetzelfde patroon dat CouchDB en PouchDB gebruiken: - `updatedAt` op elke wijziging (client-tijd) - `deletedAt` voor soft deletes - `lastSyncAt` (opgeslagen in SecureStore) voor delta-tracking - Merge: last-writer-wins, zonder uitzonderingen ### Offline resilience Bij netwerkfout tijdens opslaan blijft de meting lokaal staan. De volgende sync (`updatedAt > lastSyncAt`) pikt hem automatisch op. Geen handmatige tussenkomst nodig. ## Server-side API **Stack**: nginx, PHP 8.5 (Slim Framework 4), MariaDB, Let's Encrypt SSL, SELinux **Endpoints**: | Methode | Pad | Auth | Beschrijving | |---------|-----|------|-------------| | `GET` | `/api/v1/measurements?since=ISO` | Bearer | Delta-sync | | `POST` | `/api/v1/measurements` | Bearer | Upsert (INSERT ON DUPLICATE KEY) | | `DELETE` | `/api/v1/measurements/:id` | Bearer | Soft delete (`deleted=1`, update `updated_at`) | **Database**: MariaDB met drie tabellen — `users`, `auth_tokens`, `measurements`. De `measurements` tabel gebruikt `ON DUPLICATE KEY UPDATE` voor conflictvrije upserts. **Bestandsstructuur**: ``` /usr/share/nginx/html/weegschaal-api/ ├── composer.json ├── config.php ├── public/ │ └── index.php └── src/ ├── Auth.php ← Bearer token validatie middleware ├── Database.php ← PDO singleton ├── Measurements.php ← CRUD + sync queries └── helpers.php ← JSON response, datetime conversie, logging ``` ## Beveiliging ### Authenticatie Bearer token-validatie via `auth_tokens` tabel. Tokens worden handmatig aangemaakt via SQL. ### Logging Elke mislukte authenticatie en 404 wordt gelogd naar `/var/log/weegschaal-api/auth.log`: ``` [2026-07-20 18:06:07] 401 94.157.121.179 GET /api/v1/measurements - invalid Bearer token [2026-07-20 18:06:07] 404 94.157.121.179 GET /.env - unknown route ``` ### Fail2ban Jail `weegschaal-api`: 5 foutieve pogingen binnen 5 minuten = 1 uur IP-ban. Filter regex: ``` ^\[.*\] 401 .* - .*$ ``` Logrotering wekelijks, 12 weken retentie. ### SELinux Logmap heeft context `httpd_log_t` gezet via `semanage fcontext`. ## Ontwikkeling ```bash npm install npx expo start ``` Scannen met Expo Go op Android of iOS. ## Build & deploy ```bash # APK bouwen (lokaal via EAS) npx eas build --local --profile apk --platform android # Draadloos sideloaden via ADB adb-wireless-connect adb install -r build-*.apk ``` Vereisten: Android SDK 36, OpenJDK 21, ADB 37+, Gradle 8.14. ## Database migratie Bestaande SQLite-databases worden automatisch gemigreerd. `initDatabase()` voegt de `deletedAt` kolom toe via `ALTER TABLE` als deze nog niet bestaat: ```sql ALTER TABLE measurements ADD COLUMN deletedAt TEXT ``` ## Testen ```bash npm test ``` Tests richten zich op de merge-logica (`mergeMeasurements`) en database-operaties. ## Licentie Public domain (Unlicense)