- TypeScript 100%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
| .claude | ||
| assets | ||
| docs | ||
| src | ||
| tests | ||
| .gitignore | ||
| AGENTS.md | ||
| app.json | ||
| App.tsx | ||
| CLAUDE.md | ||
| eas.json | ||
| index.ts | ||
| LICENSE | ||
| package-lock.json | ||
| package.json | ||
| README.md | ||
| tsconfig.json | ||
| UNLICENSE | ||
| upgrade.md | ||
| weegschaal-app.jpeg | ||
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 outbox-pattern sync.
Inhoud
- Functionaliteit
- Schermen
- Synchronisatie
- Server-side API
- Beveiliging
- Offline resilience
- Ontwikkeling
- Build & deploy
- Testen
- Licentie
Functionaliteit
- Metingen invoeren van een slimme weegschaal: gewicht, BMI, vetpercentage, vetmassa, spierpercentage, spiermassa, buikvet en stofwisseling
- Dashboard met laatste en voorlaatste meting (direct zichtbaar via progressieve loading), gewichtsverschil, en grafieken (gewicht, vetmassa, spiermassa)
- 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 | Progressive loading: laatste 2 metingen direct zichtbaar, grafieken volgen daarna |
| 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
- Outbox pattern: elke meting heeft een
syncStatus—pending(nieuw/gewijzigd),pending-delete(verwijderd), ofsynced(in sync met server) - Push vóór pull: eerst worden lokale wijzigingen (
pending+pending-delete) naar de server gepusht, daarna pas remote changes opgehaald - Local-first merge: remote data overschrijft nooit lokale
pendingofpending-deleterecords — de gebruiker wint altijd - Verwijderen:
setDeletedAt(id)lokaal +updateSyncStatus(id, 'pending-delete')→ push in outbox-fase → server DELETE → hard delete in pull-fase - Cursor tracking:
lastSyncAt(SecureStore) voor delta-pull, alleen records sinds laatste sync
Waarom outbox pattern?
Betrouwbaarder dan timestamp-only push: syncStatus markeert expliciet wat gepusht moet worden, onafhankelijk van klokverschillen. Push vóór pull voorkomt dat eigen wijzigingen overschreven worden door oudere remote data. Local-first merge garandeert dat de gebruiker nooit data verliest.
Offline resilience
Bij netwerkfout tijdens opslaan blijft de meting lokaal staan met syncStatus='pending'. De outbox (getPendingRecords()) pikt hem automatisch op bij de volgende sync of dashboard-focus.
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
npm install
npx expo start
Scannen met Expo Go op Android of iOS.
Build & deploy
# 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 bij initDatabase():
deletedAtkolom: toegevoegd viaALTER TABLEals deze nog niet bestaatsyncStatusmigratie: bestaande records metsyncStatus='local'worden omgezet naar'synced', nieuwe records krijgen DEFAULT'pending'
Testen
npm test
Tests richten zich op de merge-logica (mergeMeasurements) en database-operaties.
Licentie
Public domain (Unlicense)