2026-09-09 17:36:34 +02:00
2026-09-10 15:40:27 +02:00
2026-09-09 17:36:34 +02:00
2026-09-10 14:03:34 +02:00
2026-09-09 17:36:34 +02:00
2026-09-09 12:43:11 +02:00
2026-09-10 15:40:27 +02:00
2026-09-10 15:40:27 +02:00
2026-09-10 14:03:34 +02:00
2026-09-10 15:40:27 +02:00
2026-09-10 15:40:27 +02:00
2026-09-09 17:36:34 +02:00

💜 VibeFinance

Persoonlijk vermogensdashboard

Eigen vermogen · Schulden · Voortgang · Gebruikersbeheer

React Node.js PostgreSQL Docker Version Built with Claude

VibeFinance dashboard

🤖 Gebouwd met Claude.ai

Deze applicatie is volledig ontworpen en ontwikkeld met behulp van Claude.ai — de AI-assistent van Anthropic. Van architectuur tot gebruikersinterface: alles is stap voor stap tot stand gekomen in samenwerking met Claude.


⚡ Quick Start

Vereisten: Docker Engine 24+ en Docker Compose v2+

De app draait in 2 containers: app (Express API + gebouwde frontend) en postgres (database).

.env — kopieer .env.example en vul in:

POSTGRES_USER=vibefinance
POSTGRES_PASSWORD=kies_een_sterk_wachtwoord
JWT_SECRET=minimaal_64_willekeurige_tekens
APP_PORT=3400
# Genereer een veilige JWT_SECRET
openssl rand -hex 64
cp .env.example .env    # en vul de waarden in

docker compose pull && docker compose up -d

De Vibe-organisatie in Gitea is publiek, dus een pull werkt anoniem — geen docker login nodig. Krijg je toch 401 Unauthorized of "pull access denied", dan staat de organisatie weer op privé:

docker login vibehoogie.duckdns.org   # wachtwoord = Gitea-token met read:package

Navigeer naar http://<server-ip>:3400 en registreer je account. De eerste geregistreerde gebruiker krijgt automatisch de Admin-rol.

De container luistert intern op poort 3001 en serveert daar zowel de frontend (op /) als de API (op /api). APP_PORT bepaalt de poort op de host.

Development (hot reload)

.\dev.ps1

Eén dev-container draait de API (node --watch) én de Vite dev-server samen:

Adres Wat
http://localhost de app in de browser (Vite, hot reload)
http://localhost:3001/api/health de API rechtstreeks

Vite proxyt /api naar de API in dezelfde container, dus je hebt geen CORS- uitzonderingen nodig. Handige acties:

.\dev.ps1 up -Target app     # alleen de app herbouwen en herstarten
.\dev.ps1 logs               # logs van de app volgen
.\dev.ps1 down               # stoppen én volumes wissen (dev-database weg)
.\dev.ps1 test               # testsuite draaien

.\dev.ps1 up laat de dev-database staan; alleen .\dev.ps1 down wist de volumes.

Testen

.\test.ps1              # dependency-check + backend-tests + build + smoketest
.\test.ps1 -Unit        # dependency-check + backend-tests
.\test.ps1 -NoSmoke     # zonder de stack te starten
.\test.ps1 -Strict      # faal als een dependency achterloopt
.\test.ps1 -SkipDeps    # dependency-check overslaan (bijv. offline)

Alles loopt in Docker — Node op de host is niet nodig. De backend-tests draaien zonder database via het test-target in de Dockerfile.

Stap 1 vergelijkt elke dependency in frontend/ en backend/package.json met de laatste versie in het npm-register, zodat je bij elke testronde ziet of er updates open staan. Pakketten die bewust achterblijven staan met reden in docker/update-exceptions.json en laten de check niet falen:

frontend/package.json (9 dependencies)
  [vast ] eslint    ^9.39.5 → 10.9.1   eslint-plugin-react ondersteunt nog geen ESLint 10
  actueel op de vastgezette versies na
backend/package.json (15 dependencies)
  alles actueel

Productie release

# token: staat in .env.release, of eenmalig in de shell
# één Gitea-token met write:package (push) én write:repository (release)
$env:GITEA_TOKEN = "<token met write:package + write:repository>"
.\release.ps1

Draait de tests, bouwt het app-image voor amd64 + arm64 (Raspberry Pi), pusht naar de registry, zet de Git-tag en publiceert de Gitea-release.


🗂️ Functionaliteit

Scherm Wat het doet
📊 Dashboard Overzicht van totaal eigen vermogen, schulden en netto vermogen
💰 Eigen vermogen Vermogensposten per categorie beheren + geïnvesteerde fiat historiek
📉 Schulden Schulden bijhouden per blok met afbetalingsoverzicht
📈 Voortgang Grafieken en trends van EV en schuld over tijd (apart bij te houden)
👥 Gebruikers Accounts aanmaken, bewerken en beheren (Admin-rol vereist)

De kolommen in de tabellen op Eigen vermogen en Voortgang zijn versleepbaar: pak de rechterrand van een kolomkop. De breedtes worden per tabel in de browser bewaard; dubbelklik op de greep voor de standaardbreedte.


🏗️ Architectuur

vibefinance/
├── Dockerfile                  # Multi-stage: frontend build → app-image
├── .dockerignore
├── docker-compose.yml          # Productie: app + postgres
├── docker-compose.dev.yml      # Dev: app (API + Vite) + postgres
├── .env.example                # Kopieer naar .env en vul in
├── package.json                # Versienummer — single source of truth
├── dev.ps1 / dev.sh            # Dev script (bouwen, starten, logs, pushen)
├── test.ps1 / test.sh          # Testsuite (unit + build + smoketest)
├── release.ps1 / release.sh    # Release (test, push, tag, Gitea-release)
│
├── docker/
│   └── dev-entrypoint.sh       # Start API + Vite samen in de dev-container
│
├── docs/
│   └── screenshot-dashboard.png # Screenshot bovenaan deze README
│
├── frontend/                   # React / Vite SPA (build → dist)
│   ├── vite.config.js          # /api proxy naar de API in dezelfde container
│   └── src/
│       ├── App.jsx             # Root – wiring + idle-logout
│       ├── App.css             # Reset + losse klassen (o.a. .kolom-grip)
│       ├── main.jsx
│       ├── version.js          # Draaiende versie (__APP_VERSION__ uit de build)
│       ├── constants/          # Kleuren, tabs, begindata
│       ├── utils/              # Formatters, helpers
│       ├── hooks/              # useTheme, useUpdateCheck, useKolomBreedtes
│       ├── context/            # AppContext (global state + API sync)
│       ├── components/
│       │   ├── ui/             # Primitieve UI (VCard, DonutChart…)
│       │   ├── LoginPage.jsx
│       │   ├── NavBar.jsx
│       │   ├── ProfielPopup.jsx
│       │   └── GebruikersBeheer.jsx
│       └── pages/
│           ├── DashboardTab.jsx
│           ├── EigenVermogenTab.jsx
│           ├── SchuldTab.jsx
│           └── VoortgangTab.jsx
│
└── backend/                    # Express REST API + statische frontend
    ├── test/                   # node --test integratietests
    └── src/
        ├── server.js           # Entry point: API op /api, SPA op /
        ├── db.js               # PostgreSQL pool + initDb() schema-migraties
        ├── config/             # Omgevingsvariabelen (incl. STATIC_DIR)
        ├── constants/          # Begindata (initialData.js)
        ├── middleware/         # auth.js, errorHandler.js
        ├── routes/             # auth, data, users, health, update-check
        └── stores/             # userStore, dataStore

Stack: React 19 · Vite 8 · Node.js 22 · Express 5 · PostgreSQL 18 · JWT · bcrypt · Docker Compose v2

Waarom 2 containers

De frontend werd eerder door een eigen nginx-container geserveerd. Voor self-hosting op een Raspberry Pi is dat een container te veel: de Express-app serveert de statische build nu zelf, dus er is geen aparte webserver en geen interne proxy meer nodig. Dat betekent ook: één origin, dus geen CORS tussen frontend en API.

Verzoek Wat de app doet
/api/... REST API (rate limited)
/assets/... Statische build, 1 jaar immutable gecached
al het overige index.html — fallback voor React-routing

🔌 API

Beveiligde endpoints vereisen Authorization: Bearer <token>. Rate limiting: 20 req/15 min op auth-routes, 200 req/15 min op de rest van /api (statische bestanden zijn uitgezonderd).

Route Methoden Omschrijving
/api/auth/register POST Account aanmaken (eerste user = Admin)
/api/auth/login POST Inloggen, JWT token terug
/api/auth/me GET, PATCH Profiel ophalen en bijwerken
/api/auth/me/password POST Wachtwoord wijzigen
/api/data GET, PUT, DELETE Financiële data per gebruiker
/api/users GET, POST, PATCH, DELETE Gebruikersbeheer (Admin only)
/api/health GET Status + uptime
/api/update-check GET Laatste release (voor de update-melding)

🗄️ Database

Tabel Omschrijving
users Accounts met bcrypt hash, rol, actief-vlag en aanmaakdatum
user_data Financiële data per gebruiker (JSON)
Omgeving Volume Mount pad
Productie vibefinance_pgdata /var/lib/postgresql
Development postgres_dev_data /var/lib/postgresql

PostgreSQL 18 gebruikt PGDATA=/var/lib/postgresql/18/docker — het volume is daarom gemount op /var/lib/postgresql (niet /data).

Het schema wordt bij het starten van de app aangemaakt/bijgewerkt via initDb() in backend/src/db.js.


⚙️ Omgevingsvariabelen

Variabele Standaard Omschrijving
POSTGRES_PASSWORD — Verplicht
JWT_SECRET — Verplicht — minimaal 64 tekens
POSTGRES_USER vibefinance Database gebruikersnaam
POSTGRES_DB vibefinance Database naam
JWT_EXPIRES 7d Geldigheidsduur token
CORS_ORIGIN http://localhost Externe domeinnaam (één origin, dus zelden nodig)
APP_PORT 3400 Poort op de host (container luistert op 3001)
PORT 3001 Poort binnen de container
STATIC_DIR /app/public Map met de frontend-build
UPDATE_CHECK_URL Gitea releases-API Bron update-melding (leeg = uit)
UPDATE_CHECK_TOKEN — Optioneel: de Gitea-repo is publiek, dus de check werkt anoniem. Alleen nodig als de organisatie weer privé wordt; scope read:repository

🔐 Beveiliging

  • Wachtwoorden gehasht met bcrypt (cost factor 12)
  • JWT authenticatie met instelbare vervaltijd
  • Frontend en API op één origin — geen interne proxy, geen CORS-uitzonderingen
  • Content-Security-Policy via Helmet, toegespitst op wat de app laadt
  • Database bereikbaar alleen binnen het interne Docker netwerk
  • App-container draait als non-root
  • Rate limiting op /api (statische bestanden uitgezonderd)

🧪 Testen

Laag Wat Commando
Dependencies Staan package.json's op de laatste versies .\test.ps1 -Unit
Backend API-routes, SPA-fallback, cache- en security-headers .\test.ps1 -Unit
Build Vite-build + productie-image .\test.ps1 -NoSmoke
Integratie Smoketest op de echte app + postgres stack .\test.ps1

🛠️ Beheer

Zie BEHEER.md voor commando's voor lokale ontwikkeling, releases, logs en database-toegang — en voor de sectie Tokens: waarom één Gitea-token op het werkstation genoeg is, welke scopes het nodig heeft, en wat een 401, 403 of 404 van Gitea betekent.


Privé gebruik · gebouwd met Claude.ai

S
Description
No description provided
Readme
1.1 MiB
v1.0.13
Latest
2026-10-01 17:00:42 +02:00
Languages
JavaScript 83.3%
PowerShell 13.5%
Dockerfile 1.5%
Shell 1.2%
CSS 0.3%
Other 0.2%