Compare commits
2
Commits
796245fefe
...
v1.0.5
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e315039c35 | ||
|
|
0d41c0af0b |
@@ -333,9 +333,11 @@ De Express-app serveert zowel de API als de gebouwde frontend:
|
|||||||
|
|
||||||
| Verzoek | Wat de app doet |
|
| Verzoek | Wat de app doet |
|
||||||
|---|---|
|
|---|---|
|
||||||
| `/api/...` | REST API (rate limited) |
|
| `/api/...` | REST API (rate limited, `Cache-Control: no-store`) |
|
||||||
| `/assets/...` | statische build uit `STATIC_DIR`, 1 jaar `immutable` gecached |
|
| `/assets/...` | gehashte build-bestanden uit `STATIC_DIR`, 1 jaar `immutable` |
|
||||||
| al het overige | `index.html` — fallback voor React-routing |
|
| `/favicon.svg`, `/icons/...` | vaste namen, dus `max-age=0, must-revalidate` |
|
||||||
|
| pad mét extensie zonder bestand | echte 404, niet `index.html` |
|
||||||
|
| al het overige | `index.html` — fallback voor React-routing, `no-store` |
|
||||||
|
|
||||||
`STATIC_DIR` staat standaard op `/app/public`; daar zet de `Dockerfile` de
|
`STATIC_DIR` staat standaard op `/app/public`; daar zet de `Dockerfile` de
|
||||||
Vite-build neer. Bestaat die map niet (bijv. in dev), dan serveert de app
|
Vite-build neer. Bestaat die map niet (bijv. in dev), dan serveert de app
|
||||||
@@ -381,6 +383,12 @@ De backend slaat alle data op in **PostgreSQL**.
|
|||||||
|
|
||||||
> PostgreSQL 18 gebruikt `PGDATA=/var/lib/postgresql/18/docker`. Het volume is gemount op `/var/lib/postgresql` zodat data bewaard blijft na een container restart.
|
> PostgreSQL 18 gebruikt `PGDATA=/var/lib/postgresql/18/docker`. Het volume is gemount op `/var/lib/postgresql` zodat data bewaard blijft na een container restart.
|
||||||
|
|
||||||
|
Daarnaast heeft de app zelf een backup onder **Instellingen**: die schrijft een
|
||||||
|
ZIP met de data van de *ingelogde* gebruiker (`data.json` + `meta.json`) en leest
|
||||||
|
zo'n ZIP ook weer in. Handig per gebruiker, maar het is geen servercopie — accounts,
|
||||||
|
wachtwoordhashes en de data van andere gebruikers zitten er niet in. Voor een
|
||||||
|
volledige backup gebruik je `pg_dump` hieronder.
|
||||||
|
|
||||||
**Backup maken:**
|
**Backup maken:**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -407,12 +415,25 @@ docker exec -i vibefinance_postgres psql -U vibefinance -d vibefinance < backup-
|
|||||||
|
|
||||||
De eerste geregistreerde gebruiker krijgt automatisch de **Admin**-rol. Verdere accounts kunnen via de app aangemaakt worden (Gebruikersbeheer, Admin vereist) of via de API.
|
De eerste geregistreerde gebruiker krijgt automatisch de **Admin**-rol. Verdere accounts kunnen via de app aangemaakt worden (Gebruikersbeheer, Admin vereist) of via de API.
|
||||||
|
|
||||||
Rollen:
|
### Rollen
|
||||||
| Rol | Rechten |
|
|
||||||
|
De rolkeuze in Gebruikersbeheer biedt drie waarden, maar de backend dwingt er op
|
||||||
|
dit moment één af:
|
||||||
|
|
||||||
|
| Rol | Wat de code ermee doet |
|
||||||
|---|---|
|
|---|---|
|
||||||
| `Admin` | Alles, inclusief gebruikersbeheer |
|
| `Admin` | Enige rol die wordt gecontroleerd: `requireAdmin` in `middleware/auth.js` bewaakt alle routes onder `/api/users` |
|
||||||
| `Bewerker` | Data lezen en bewerken |
|
| `Bewerker` | Geen eigen rechten — gelijk aan `Viewer` |
|
||||||
| `Viewer` | Alleen lezen |
|
| `Viewer` | Standaardrol voor een nieuw account; mag de eigen data lezen én schrijven |
|
||||||
|
|
||||||
|
Dat `Viewer` toch mag schrijven is geen gat in de afscherming: `/api/data` werkt
|
||||||
|
per gebruiker en gaat altijd over de rij van het eigen token (`req.user.sub`).
|
||||||
|
Niemand ziet dus data van een ander. Wat ontbreekt is een read-only account —
|
||||||
|
wil je dat, dan is er een controle op `rol` nodig bij `PUT`/`DELETE /api/data`.
|
||||||
|
|
||||||
|
> De "Gebruikers"-knop in de navigatiebalk staat bij iedereen in beeld; bij een
|
||||||
|
> niet-Admin antwoordt `/api/users` met `403` en blijft de lijst leeg. De
|
||||||
|
> afscherming zit dus in de API, niet in de navigatie.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,35 @@
|
|||||||
|
|
||||||
## [Niet vrijgegeven]
|
## [Niet vrijgegeven]
|
||||||
|
|
||||||
|
### Gewijzigd
|
||||||
|
- De documentatie is gelijkgetrokken met de code. `README.md`: de bestandsboom
|
||||||
|
miste `backend/src/logger.js`, `docker/check-updates.mjs`,
|
||||||
|
`frontend/public/`, `InstellingenModal.jsx`, `LoginBackground.jsx` en de
|
||||||
|
losse bestanden in `utils/`, en noemde de update-route `update-check` terwijl
|
||||||
|
het bestand `routes/update.js` heet. De schermentabel gebruikt nu de labels
|
||||||
|
zoals ze in `constants/index.js` staan ("Schuld", "Voortgang Schuld & EV") en
|
||||||
|
vermeldt **Instellingen** — thema plus backup/restore via ZIP, dat nergens in
|
||||||
|
de README stond
|
||||||
|
- De tabel "wat de app met een verzoek doet" (in beide bestanden) noemde alleen
|
||||||
|
`/assets/...` en de SPA-fallback. `server.js` kent vier gevallen: `no-store`
|
||||||
|
op de API en op HTML, een jaar `immutable` op de gehashte bundels,
|
||||||
|
`must-revalidate` op bestanden met een vaste naam (`favicon.svg`, `icons/*`)
|
||||||
|
en een echte 404 op een pad mét extensie waar geen bestand achter zit
|
||||||
|
- `BEHEER.md` beschreef `Bewerker` als "data lezen en bewerken" en `Viewer` als
|
||||||
|
"alleen lezen". Zo werkt het niet: `requireAdmin` is de enige rolcontrole in
|
||||||
|
de code en bewaakt alleen `/api/users`. Een `Viewer` mag daardoor gewoon zijn
|
||||||
|
eigen data schrijven — geen gat in de afscherming, want `/api/data` gaat altijd
|
||||||
|
over de rij van het eigen token, maar wél iets anders dan er stond. De sectie
|
||||||
|
zegt nu wat de code doet en wat er nodig is voor een echt read-only account
|
||||||
|
- `README.md` en `BEHEER.md` wijzen bij backup naar het juiste niveau: de ZIP
|
||||||
|
onder Instellingen bevat alleen de data van de ingelogde gebruiker, `pg_dump`
|
||||||
|
is de volledige servercopie
|
||||||
|
- `DATABASE_URL` staat in de variabelentabel van `README.md`. Hij wordt door
|
||||||
|
`docker-compose.yml` samengesteld uit de `POSTGRES_*` waarden, dus zelf zetten
|
||||||
|
is alleen nodig buiten Compose
|
||||||
|
|
||||||
|
## [1.0.4] - 2026-09-10
|
||||||
|
|
||||||
### Gewijzigd
|
### Gewijzigd
|
||||||
- Git-tags krijgen weer een `v`-prefix (`v1.0.4`), net als t/m `v1.0.0`. Sinds
|
- Git-tags krijgen weer een `v`-prefix (`v1.0.4`), net als t/m `v1.0.0`. Sinds
|
||||||
1.0.1 was dat `1.0.1`, `1.0.2`, `1.0.3` — en omdat Gitea de tag op de
|
1.0.1 was dat `1.0.1`, `1.0.2`, `1.0.3` — en omdat Gitea de tag op de
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ Eigen vermogen · Schulden · Voortgang · Gebruikersbeheer
|
|||||||

|

|
||||||

|

|
||||||

|

|
||||||

|

|
||||||

|

|
||||||
|
|
||||||
<img src="docs/screenshot-dashboard.png" alt="VibeFinance dashboard" width="820">
|
<img src="docs/screenshot-dashboard.png" alt="VibeFinance dashboard" width="820">
|
||||||
@@ -168,14 +168,20 @@ build — anders zou de release met een placeholder als notes online komen.
|
|||||||
|---|---|
|
|---|---|
|
||||||
| 📊 **Dashboard** | Overzicht van totaal eigen vermogen, schulden en netto vermogen |
|
| 📊 **Dashboard** | Overzicht van totaal eigen vermogen, schulden en netto vermogen |
|
||||||
| 💰 **Eigen vermogen** | Vermogensposten per categorie beheren + geïnvesteerde fiat historiek |
|
| 💰 **Eigen vermogen** | Vermogensposten per categorie beheren + geïnvesteerde fiat historiek |
|
||||||
| 📉 **Schulden** | Schulden bijhouden per blok met afbetalingsoverzicht |
|
| 📉 **Schuld** | Schulden bijhouden per blok met afbetalingsoverzicht |
|
||||||
| 📈 **Voortgang** | Grafieken en trends van EV en schuld over tijd (apart bij te houden) |
|
| 📈 **Voortgang Schuld & EV** | Grafieken en trends van EV en schuld over tijd (apart bij te houden) |
|
||||||
| 👥 **Gebruikers** | Accounts aanmaken, bewerken en beheren (Admin-rol vereist) |
|
| 👥 **Gebruikers** | Accounts aanmaken, bewerken en beheren (Admin-rol vereist) |
|
||||||
|
| ⚙️ **Instellingen** | Licht/donker thema, backup downloaden en backup terugzetten |
|
||||||
|
|
||||||
De kolommen in de tabellen op Eigen vermogen en Voortgang zijn versleepbaar: pak
|
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
|
de rechterrand van een kolomkop. De breedtes worden per tabel in de browser
|
||||||
bewaard; dubbelklik op de greep voor de standaardbreedte.
|
bewaard; dubbelklik op de greep voor de standaardbreedte.
|
||||||
|
|
||||||
|
Backup en restore lopen via **Instellingen** in de navigatiebalk: "Backup maken"
|
||||||
|
downloadt een ZIP (`data.json` + `meta.json`) met je eigen financiële data,
|
||||||
|
"Backup terugzetten" leest zo'n ZIP weer in en schrijft hem via `/api/data` weg.
|
||||||
|
Dat is de gebruikersvariant; een volledige database-dump staat in `BEHEER.md`.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🏗️ Architectuur
|
## 🏗️ Architectuur
|
||||||
@@ -193,27 +199,33 @@ vibefinance/
|
|||||||
├── release.ps1 / release.sh # Release (test, push, tag, Gitea-release)
|
├── release.ps1 / release.sh # Release (test, push, tag, Gitea-release)
|
||||||
│
|
│
|
||||||
├── docker/
|
├── docker/
|
||||||
│ └── dev-entrypoint.sh # Start API + Vite samen in de dev-container
|
│ ├── dev-entrypoint.sh # Start API + Vite samen in de dev-container
|
||||||
|
│ ├── check-updates.mjs # Dependency-check tegen het npm-register
|
||||||
|
│ └── update-exceptions.json # Pakketten die bewust achterblijven, met reden
|
||||||
│
|
│
|
||||||
├── docs/
|
├── docs/
|
||||||
│ └── screenshot-dashboard.png # Screenshot bovenaan deze README
|
│ └── screenshot-dashboard.png # Screenshot bovenaan deze README
|
||||||
│
|
│
|
||||||
├── frontend/ # React / Vite SPA (build → dist)
|
├── frontend/ # React / Vite SPA (build → dist)
|
||||||
│ ├── vite.config.js # /api proxy naar de API in dezelfde container
|
│ ├── vite.config.js # /api proxy naar de API in dezelfde container
|
||||||
|
│ ├── index.html # Vite-entry, verwijst naar /favicon.svg
|
||||||
|
│ ├── public/ # Ongebundeld: favicon.svg + icons/
|
||||||
│ └── src/
|
│ └── src/
|
||||||
│ ├── App.jsx # Root – wiring + idle-logout
|
│ ├── App.jsx # Root – wiring + idle-logout
|
||||||
│ ├── App.css # Reset + losse klassen (o.a. .kolom-grip)
|
│ ├── App.css # Reset + losse klassen (o.a. .kolom-grip)
|
||||||
│ ├── main.jsx
|
│ ├── main.jsx
|
||||||
│ ├── version.js # Draaiende versie (__APP_VERSION__ uit de build)
|
│ ├── version.js # Draaiende versie (__APP_VERSION__ uit de build)
|
||||||
│ ├── constants/ # Kleuren, tabs, begindata
|
│ ├── constants/ # Kleuren, tabs, begindata
|
||||||
│ ├── utils/ # Formatters, helpers
|
│ ├── utils/ # format.js (bedragen/datums), storage.js
|
||||||
│ ├── hooks/ # useTheme, useUpdateCheck, useKolomBreedtes
|
│ ├── hooks/ # useTheme, useUpdateCheck, useKolomBreedtes
|
||||||
│ ├── context/ # AppContext (global state + API sync)
|
│ ├── context/ # AppContext (global state + API sync + backup)
|
||||||
│ ├── components/
|
│ ├── components/
|
||||||
│ │ ├── ui/ # Primitieve UI (VCard, DonutChart…)
|
│ │ ├── ui/ # Primitieve UI (VCard, DonutChart…)
|
||||||
│ │ ├── LoginPage.jsx
|
│ │ ├── LoginPage.jsx
|
||||||
|
│ │ ├── LoginBackground.jsx
|
||||||
│ │ ├── NavBar.jsx
|
│ │ ├── NavBar.jsx
|
||||||
│ │ ├── ProfielPopup.jsx
|
│ │ ├── ProfielPopup.jsx
|
||||||
|
│ │ ├── InstellingenModal.jsx # Thema + backup/restore
|
||||||
│ │ └── GebruikersBeheer.jsx
|
│ │ └── GebruikersBeheer.jsx
|
||||||
│ └── pages/
|
│ └── pages/
|
||||||
│ ├── DashboardTab.jsx
|
│ ├── DashboardTab.jsx
|
||||||
@@ -226,10 +238,11 @@ vibefinance/
|
|||||||
└── src/
|
└── src/
|
||||||
├── server.js # Entry point: API op /api, SPA op /
|
├── server.js # Entry point: API op /api, SPA op /
|
||||||
├── db.js # PostgreSQL pool + initDb() schema-migraties
|
├── db.js # PostgreSQL pool + initDb() schema-migraties
|
||||||
|
├── logger.js # pino (pino-pretty buiten productie)
|
||||||
├── config/ # Omgevingsvariabelen (incl. STATIC_DIR)
|
├── config/ # Omgevingsvariabelen (incl. STATIC_DIR)
|
||||||
├── constants/ # Begindata (initialData.js)
|
├── constants/ # Begindata (initialData.js)
|
||||||
├── middleware/ # auth.js, errorHandler.js
|
├── middleware/ # auth.js, errorHandler.js
|
||||||
├── routes/ # auth, data, users, health, update-check
|
├── routes/ # auth, data, users, health, update
|
||||||
└── stores/ # userStore, dataStore
|
└── stores/ # userStore, dataStore
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -245,9 +258,11 @@ frontend en API.
|
|||||||
|
|
||||||
| Verzoek | Wat de app doet |
|
| Verzoek | Wat de app doet |
|
||||||
|---|---|
|
|---|---|
|
||||||
| `/api/...` | REST API (rate limited) |
|
| `/api/...` | REST API (rate limited, `Cache-Control: no-store`) |
|
||||||
| `/assets/...` | Statische build, 1 jaar `immutable` gecached |
|
| `/assets/...` | Gehashte build-bestanden, 1 jaar `immutable` gecached |
|
||||||
| al het overige | `index.html` — fallback voor React-routing |
|
| `/favicon.svg`, `/icons/...` | Vaste namen, dus `max-age=0, must-revalidate` |
|
||||||
|
| pad mét extensie, geen bestand | echte 404 (geen HTML waar een icoon wordt verwacht) |
|
||||||
|
| al het overige | `index.html` — fallback voor React-routing, niet gecached |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -295,6 +310,7 @@ Het schema wordt bij het starten van de app aangemaakt/bijgewerkt via
|
|||||||
| `JWT_SECRET` | — | **Verplicht** — minimaal 64 tekens |
|
| `JWT_SECRET` | — | **Verplicht** — minimaal 64 tekens |
|
||||||
| `POSTGRES_USER` | `vibefinance` | Database gebruikersnaam |
|
| `POSTGRES_USER` | `vibefinance` | Database gebruikersnaam |
|
||||||
| `POSTGRES_DB` | `vibefinance` | Database naam |
|
| `POSTGRES_DB` | `vibefinance` | Database naam |
|
||||||
|
| `DATABASE_URL` | — | Connectiestring; `docker-compose.yml` stelt hem samen uit de drie `POSTGRES_*` waarden, dus zelf zetten hoeft alleen buiten Compose |
|
||||||
| `JWT_EXPIRES` | `7d` | Geldigheidsduur token |
|
| `JWT_EXPIRES` | `7d` | Geldigheidsduur token |
|
||||||
| `CORS_ORIGIN` | `http://localhost` | Externe domeinnaam (één origin, dus zelden nodig) |
|
| `CORS_ORIGIN` | `http://localhost` | Externe domeinnaam (één origin, dus zelden nodig) |
|
||||||
| `APP_PORT` | `3400` | Poort op de host (container luistert op 3001) |
|
| `APP_PORT` | `3400` | Poort op de host (container luistert op 3001) |
|
||||||
@@ -309,6 +325,10 @@ Het schema wordt bij het starten van de app aangemaakt/bijgewerkt via
|
|||||||
|
|
||||||
- Wachtwoorden gehasht met bcrypt (cost factor 12)
|
- Wachtwoorden gehasht met bcrypt (cost factor 12)
|
||||||
- JWT authenticatie met instelbare vervaltijd
|
- JWT authenticatie met instelbare vervaltijd
|
||||||
|
- Data is per gebruiker: `/api/data` leest en schrijft alleen de rij van de
|
||||||
|
ingelogde gebruiker (`sub` uit het token)
|
||||||
|
- Van de rollen wordt alleen **Admin** afgedwongen, op `/api/users` — zie de
|
||||||
|
sectie Rollen in `BEHEER.md`
|
||||||
- Frontend en API op één origin — geen interne proxy, geen CORS-uitzonderingen
|
- Frontend en API op één origin — geen interne proxy, geen CORS-uitzonderingen
|
||||||
- Content-Security-Policy via Helmet, toegespitst op wat de app laadt
|
- Content-Security-Policy via Helmet, toegespitst op wat de app laadt
|
||||||
- Database bereikbaar alleen binnen het interne Docker netwerk
|
- Database bereikbaar alleen binnen het interne Docker netwerk
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "vibefinance-frontend",
|
"name": "vibefinance-frontend",
|
||||||
"version": "1.0.3",
|
"version": "1.0.4",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 56 KiB |
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "vibefinance",
|
"name": "vibefinance",
|
||||||
"version": "1.0.4",
|
"version": "1.0.5",
|
||||||
"description": "VibeFinance — persoonlijk vermogensbeheer",
|
"description": "VibeFinance — persoonlijk vermogensbeheer",
|
||||||
"private": true,
|
"private": true,
|
||||||
"repository": "https://vibehoogie.duckdns.org/vibe/VibeFinance",
|
"repository": "https://vibehoogie.duckdns.org/vibe/VibeFinance",
|
||||||
|
|||||||
Reference in New Issue
Block a user