- TypeScript 100%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
- setDeletedAt update nu ook updatedAt (bug 2) - getAllMeasurements filtert deletedAt IS NULL, nieuwe getAllMeasurementsForSync voor sync - mergeMeasurements detecteert server deleted:true → deletedAt (bug 1) - syncAll: pull-push split met remoteUpdatedMap, geen double-push (bug 3) - hard delete alleen na API-succes (bug 4) - DashboardScreen: refreshCache() na syncAll zodat UI niet op Laden blijft hangen - deleteMeasurement() dead code verwijderd (bug 5) - tests: 3 nieuwe testcases voor deleted → deletedAt mapping, 15/15 pass |
||
| .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 timestamp-based delta-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, 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
- Delta ophalen:
GET /api/v1/measurements?since=lastSyncAt— alleen gewijzigde records van de server - Mergen: last-writer-wins op
updatedAt - Pushen: alle lokale records met
updatedAt > lastSyncAt→ POST naar API - Verwijderen: soft delete via
deletedAttimestamp → DELETE naar API bij volgende sync - High-water mark:
lastSyncAtbijgewerkt na elke succesvolle sync
Waarom timestamp-based?
Geen syncStatus enum, geen speciale gevallen. Hetzelfde patroon dat CouchDB en PouchDB gebruiken:
updatedAtop elke wijziging (client-tijd)deletedAtvoor soft deleteslastSyncAt(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
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. initDatabase() voegt de deletedAt kolom toe via ALTER TABLE als deze nog niet bestaat:
ALTER TABLE measurements ADD COLUMN deletedAt TEXT
Testen
npm test
Tests richten zich op de merge-logica (mergeMeasurements) en database-operaties.
Licentie
Public domain (Unlicense)