No description
  • TypeScript 100%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
2026-08-06 12:46:26 +02:00
.claude Initial commit 2026-06-27 22:39:13 +02:00
assets assets: nieuw app icoon van weegschaal-app.jpeg 2026-07-20 19:34:40 +02:00
docs fix: sync refactor — fix 6 bugs + cache refresh na sync 2026-07-25 15:33:22 +02:00
src fix: pull-fase telt remote deletions als deleted ipv pulled 2026-08-06 04:48:30 +02:00
tests feat: outbox-pattern sync + progressive dashboard loading 2026-07-25 17:27:54 +02:00
.gitignore feat: add MeasurementDetailScreen, 365-day charts, and edit support 2026-07-14 09:14:59 +02:00
AGENTS.md Initial commit 2026-06-27 22:39:13 +02:00
app.json fix: update adaptive icon foreground naar nieuw icoon 2026-07-20 20:01:19 +02:00
App.tsx feat: add MeasurementDetailScreen, 365-day charts, and edit support 2026-07-14 09:14:59 +02:00
CLAUDE.md Initial commit 2026-06-27 22:39:13 +02:00
eas.json feat: outbox-pattern sync + progressive dashboard loading 2026-07-25 17:27:54 +02:00
index.ts Initial commit 2026-06-27 22:39:13 +02:00
LICENSE Initial commit 2026-06-27 22:39:13 +02:00
package-lock.json feat: share knop op dashboard kaart — deelt kaart als PNG via expo-sharing 2026-07-20 18:58:24 +02:00
package.json fix: sync refactor — fix 6 bugs + cache refresh na sync 2026-07-25 15:33:22 +02:00
README.md feat: outbox-pattern sync + progressive dashboard loading 2026-07-25 17:27:54 +02:00
tsconfig.json feat: wire up App.tsx with navigation 2026-06-27 22:42:33 +02:00
UNLICENSE docs: public domain (Unlicense) 2026-06-27 22:46:32 +02:00
upgrade.md db: fix insertMeasurement/upsertMeasurement — remove syncStatus param 2026-07-20 16:58:55 +02:00
weegschaal-app.jpeg assets: nieuw app icoon van weegschaal-app.jpeg 2026-07-20 19:34:40 +02:00

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

  • 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

  1. Outbox pattern: elke meting heeft een syncStatuspending (nieuw/gewijzigd), pending-delete (verwijderd), of synced (in sync met server)
  2. Push vóór pull: eerst worden lokale wijzigingen (pending + pending-delete) naar de server gepusht, daarna pas remote changes opgehaald
  3. Local-first merge: remote data overschrijft nooit lokale pending of pending-delete records — de gebruiker wint altijd
  4. Verwijderen: setDeletedAt(id) lokaal + updateSyncStatus(id, 'pending-delete') → push in outbox-fase → server DELETE → hard delete in pull-fase
  5. 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():

  • deletedAt kolom: toegevoegd via ALTER TABLE als deze nog niet bestaat
  • syncStatus migratie: bestaande records met syncStatus='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)