docs: uitgebreide README met alle projectaspecten

This commit is contained in:
eddy 2026-07-20 20:19:48 +02:00
commit 55572a1455

151
README.md
View file

@ -1,35 +1,121 @@
# Weegschaal App # Weegschaal App
*Gemaakt met vibecoding — DeepSeek V4 Pro* _*Gemaakt met vibecoding — DeepSeek V4 Pro*_
React Native (Expo) app voor het handmatig invoeren van meetgegevens van een slimme weegschaal. Data wordt lokaal opgeslagen in SQLite en gesynchroniseerd via een eigen REST API. 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.
## Meetwaarden ## Inhoud
- Gewicht (kg) - [Functionaliteit](#functionaliteit)
- BMI - [Schermen](#schermen)
- Vetpercentage (%) - [Synchronisatie](#synchronisatie)
- Vetmassa (kg) - [Server-side API](#server-side-api)
- Spierpercentage (%) - [Beveiliging](#beveiliging)
- Spiermassa (kg) - [Offline resilience](#offline-resilience)
- Buikvet (index) - [Ontwikkeling](#ontwikkeling)
- Stofwisseling in rust (kcal) - [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 ## Schermen
- **Dashboard** — laatste meting, voorlaatste meting met gewichtsverschil, deel-knop om de kaart als PNG te delen | Scherm | Beschrijving |
- **Nieuwe meting / Meting wijzigen** — formulier met datumkiezer, vooraf ingevuld met waarden van de laatste meting |--------|-------------|
- **Meting detail** — alle meetwaarden met knoppen voor wijzigen en verwijderen | Dashboard | Laatste meting, voorlaatste meting met gewichtsverschil, grafiek, deel-knop |
- **Historie** — FlatList gesorteerd op datum (nieuwste bovenaan), pull-to-refresh | Nieuwe meting / Wijzigen | Formulier met datumkiezer, waarden vooraf ingevuld vanaf laatste meting |
- **Instellingen** — REST API URL en token, handmatige sync | 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 ## Synchronisatie
De app sync via een eigen REST API (`weegschaal.eddydevink.nl`) met Bearer token-authenticatie. Incrementele delta-sync op basis van `updatedAt` timestamps — alleen gewijzigde metingen worden uitgewisseld. De app synchroniseert via een zelfgebouwde REST API. Geen WebDAV, geen bestandssync — pure delta-sync.
Vul in het Instellingen scherm in: ### Hoe het werkt
- **API URL**: `https://weegschaal.eddydevink.nl`
- **Token**: je persoonlijke Bearer token 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 <HOST> .* - .*$
```
Logrotering wekelijks, 12 weken retentie.
### SELinux
Logmap heeft context `httpd_log_t` gezet via `semanage fcontext`.
## Ontwikkeling ## Ontwikkeling
@ -38,12 +124,37 @@ npm install
npx expo start 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 ## Testen
```bash ```bash
npm test npm test
``` ```
Tests richten zich op de merge-logica (`mergeMeasurements`) en database-operaties.
## Licentie ## Licentie
Public domain (Unlicense) Public domain (Unlicense)