Files
VibeFinance/README.md
T

289 lines
11 KiB
Markdown
Raw Normal View History

<div align="center">
# 💜 VibeFinance
**Persoonlijk vermogensdashboard**
Eigen vermogen · Schulden · Voortgang · Gebruikersbeheer
![React](https://img.shields.io/badge/React_18-20232A?style=flat&logo=react&logoColor=61DAFB)
![Node.js](https://img.shields.io/badge/Node.js_22-339933?style=flat&logo=node.js&logoColor=white)
![PostgreSQL](https://img.shields.io/badge/PostgreSQL_18-4169E1?style=flat&logo=postgresql&logoColor=white)
![Docker](https://img.shields.io/badge/Docker_Compose-2496ED?style=flat&logo=docker&logoColor=white)
![Version](https://img.shields.io/badge/versie-0.1.4-8b5cf6?style=flat)
![Built with Claude](https://img.shields.io/badge/Gebouwd_met-Claude.ai-8b5cf6?style=flat&logo=anthropic&logoColor=white)
</div>
---
## 🤖 Gebouwd met Claude.ai
Deze applicatie is volledig ontworpen en ontwikkeld met behulp van [Claude.ai](https://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:
```env
POSTGRES_USER=vibefinance
POSTGRES_PASSWORD=kies_een_sterk_wachtwoord
JWT_SECRET=minimaal_64_willekeurige_tekens
APP_PORT=3400
```
```bash
# Genereer een veilige JWT_SECRET
openssl rand -hex 64
```
```bash
cp .env.example .env # en vul de waarden in
docker compose pull && docker compose up -d
```
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)
```powershell
.\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:
```powershell
.\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
```powershell
.\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`](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
```powershell
$env:GITEA_TOKEN = "<token met release-rechten>"
.\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) |
---
## 🏗️ 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
│
├── frontend/ # React / Vite SPA (build → dist)
│ ├── vite.config.js # /api proxy naar de API in dezelfde container
│ └── src/
│ ├── App.jsx # Root – wiring + idle-logout
│ ├── main.jsx
│ ├── constants/ # Kleuren, tabs, begindata
│ ├── utils/ # Formatters, helpers
│ ├── hooks/ # useTheme, useUpdateCheck
│ ├── 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` | — | Alleen voor een niet-publieke repo |
---
## 🔐 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 |
|---|---|---|
2026-09-04 12:24:22 +02:00
| 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.
---
<div align="center">
Privé gebruik · gebouwd met [Claude.ai](https://claude.ai)
</div>