2 Commits
4 changed files with 87 additions and 17 deletions
+29 -8
View File
@@ -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.
--- ---
+29
View File
@@ -1,5 +1,34 @@
# Changelog # Changelog
## [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 ## [1.0.4] - 2026-09-10
### Gewijzigd ### Gewijzigd
+29 -9
View File
@@ -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
Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB