Compare commits
8
Commits
1.0.3
...
a9b6203dec
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a9b6203dec | ||
|
|
e315039c35 | ||
|
|
36f49b216f | ||
|
|
0d41c0af0b | ||
|
|
796245fefe | ||
|
|
04e70208bd | ||
|
|
0bbb166b32 | ||
|
|
fe6b94aa7f |
@@ -240,6 +240,7 @@ tag op een Raspberry Pi en op een x86-server werkt.
|
||||
| Bestand | Wat | Wanneer |
|
||||
|---|---|---|
|
||||
| `Changelog.md` | kop `## [Niet vrijgegeven]` → `## [x.x.x] - datum` | vóór de build |
|
||||
| Git-tag | `vx.x.x` op `HEAD`, gepusht naar origin | stap 4 |
|
||||
| `frontend/package.json` | versie gelijkgetrokken met de root `package.json` | vóór de build |
|
||||
| `README.md` | de `versie-x.x.x`-badge bovenaan | vóór de build |
|
||||
| `package.json` (root) | versie → `x.x.(x+1)-dev` voor de volgende cyclus | **ná** het publiceren |
|
||||
@@ -332,9 +333,11 @@ De Express-app serveert zowel de API als de gebouwde frontend:
|
||||
|
||||
| Verzoek | Wat de app doet |
|
||||
|---|---|
|
||||
| `/api/...` | REST API (rate limited) |
|
||||
| `/assets/...` | statische build uit `STATIC_DIR`, 1 jaar `immutable` gecached |
|
||||
| al het overige | `index.html` — fallback voor React-routing |
|
||||
| `/api/...` | REST API (rate limited, `Cache-Control: no-store`) |
|
||||
| `/assets/...` | gehashte build-bestanden uit `STATIC_DIR`, 1 jaar `immutable` |
|
||||
| `/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
|
||||
Vite-build neer. Bestaat die map niet (bijv. in dev), dan serveert de app
|
||||
@@ -380,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.
|
||||
|
||||
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:**
|
||||
|
||||
```bash
|
||||
@@ -406,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.
|
||||
|
||||
Rollen:
|
||||
| Rol | Rechten |
|
||||
### Rollen
|
||||
|
||||
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 |
|
||||
| `Bewerker` | Data lezen en bewerken |
|
||||
| `Viewer` | Alleen lezen |
|
||||
| `Admin` | Enige rol die wordt gecontroleerd: `requireAdmin` in `middleware/auth.js` bewaakt alle routes onder `/api/users` |
|
||||
| `Bewerker` | Geen eigen rechten — gelijk aan `Viewer` |
|
||||
| `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.
|
||||
|
||||
---
|
||||
|
||||
@@ -473,6 +495,10 @@ Tags:
|
||||
x.x.x-dev development — specifieke dev versie
|
||||
```
|
||||
|
||||
> Image-tags hebben géén `v`-prefix (`1.0.4`), Git-tags wél (`v1.0.4`). Dat is
|
||||
> geen inconsistentie maar de conventie aan beide kanten: container-registries
|
||||
> taggen op het kale nummer, Git-releases met `v`.
|
||||
|
||||
Inloggen op de registry:
|
||||
|
||||
```bash
|
||||
|
||||
@@ -2,6 +2,53 @@
|
||||
|
||||
## [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
|
||||
- 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
|
||||
release-pagina toont, stond daar het kale nummer terwijl de release-naam wél
|
||||
`v1.0.3` was. De image-tags in de registry houden bewust géén `v`; dat is de
|
||||
conventie voor container-tags. De update-check verdraagt beide: backend en
|
||||
frontend strippen een leidende `v` voordat ze versies vergelijken
|
||||
|
||||
### Opgelost
|
||||
- Het omzetten van de changelog-kop slokte de lege regel eronder op, waardoor
|
||||
`## [x.x.x] - datum` direct tegen `### Toegevoegd` aan kwam te staan. Oorzaak
|
||||
was `\s*$` in het zoekpatroon: `\s` matcht ook newlines. Het patroon eindigt nu
|
||||
op `[ \t]*(?=\r?$)` — een lookahead, want dit bestand heeft CRLF en met alleen
|
||||
`[ \t]*$` matchte de kop helemaal niet meer (de `\r` zit ertussen). Zo blijft
|
||||
de match binnen één regel én blijft het regeleinde ongemoeid
|
||||
|
||||
## [1.0.3] - 2026-09-10
|
||||
|
||||
### Toegevoegd
|
||||
- `release.ps1` zet de changelog-kop `## [Niet vrijgegeven]` nu zelf om naar
|
||||
`## [x.x.x] - datum`, net zoals het al deed met `frontend/package.json` en de
|
||||
|
||||
@@ -10,7 +10,7 @@ Eigen vermogen · Schulden · Voortgang · Gebruikersbeheer
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
<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 |
|
||||
| 💰 **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) |
|
||||
| 📉 **Schuld** | Schulden bijhouden per blok met afbetalingsoverzicht |
|
||||
| 📈 **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) |
|
||||
| ⚙️ **Instellingen** | Licht/donker thema, backup downloaden en backup terugzetten |
|
||||
|
||||
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.
|
||||
|
||||
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
|
||||
@@ -193,27 +199,33 @@ vibefinance/
|
||||
├── release.ps1 / release.sh # Release (test, push, tag, Gitea-release)
|
||||
│
|
||||
├── 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/
|
||||
│ └── screenshot-dashboard.png # Screenshot bovenaan deze README
|
||||
│
|
||||
├── frontend/ # React / Vite SPA (build → dist)
|
||||
│ ├── 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/
|
||||
│ ├── 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
|
||||
│ ├── utils/ # format.js (bedragen/datums), storage.js
|
||||
│ ├── hooks/ # useTheme, useUpdateCheck, useKolomBreedtes
|
||||
│ ├── context/ # AppContext (global state + API sync)
|
||||
│ ├── context/ # AppContext (global state + API sync + backup)
|
||||
│ ├── components/
|
||||
│ │ ├── ui/ # Primitieve UI (VCard, DonutChart…)
|
||||
│ │ ├── LoginPage.jsx
|
||||
│ │ ├── LoginBackground.jsx
|
||||
│ │ ├── NavBar.jsx
|
||||
│ │ ├── ProfielPopup.jsx
|
||||
│ │ ├── InstellingenModal.jsx # Thema + backup/restore
|
||||
│ │ └── GebruikersBeheer.jsx
|
||||
│ └── pages/
|
||||
│ ├── DashboardTab.jsx
|
||||
@@ -226,10 +238,11 @@ vibefinance/
|
||||
└── src/
|
||||
├── server.js # Entry point: API op /api, SPA op /
|
||||
├── db.js # PostgreSQL pool + initDb() schema-migraties
|
||||
├── logger.js # pino (pino-pretty buiten productie)
|
||||
├── config/ # Omgevingsvariabelen (incl. STATIC_DIR)
|
||||
├── constants/ # Begindata (initialData.js)
|
||||
├── middleware/ # auth.js, errorHandler.js
|
||||
├── routes/ # auth, data, users, health, update-check
|
||||
├── routes/ # auth, data, users, health, update
|
||||
└── stores/ # userStore, dataStore
|
||||
```
|
||||
|
||||
@@ -245,9 +258,11 @@ 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/...` | REST API (rate limited, `Cache-Control: no-store`) |
|
||||
| `/assets/...` | Gehashte build-bestanden, 1 jaar `immutable` gecached |
|
||||
| `/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 |
|
||||
| `POSTGRES_USER` | `vibefinance` | Database gebruikersnaam |
|
||||
| `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 |
|
||||
| `CORS_ORIGIN` | `http://localhost` | Externe domeinnaam (één origin, dus zelden nodig) |
|
||||
| `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)
|
||||
- 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
|
||||
- Content-Security-Policy via Helmet, toegespitst op wat de app laadt
|
||||
- Database bereikbaar alleen binnen het interne Docker netwerk
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "vibefinance-frontend",
|
||||
"version": "1.0.2",
|
||||
"version": "1.0.4",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 56 KiB |
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "vibefinance",
|
||||
"version": "1.0.3",
|
||||
"version": "1.0.5",
|
||||
"description": "VibeFinance — persoonlijk vermogensbeheer",
|
||||
"private": true,
|
||||
"repository": "https://vibehoogie.duckdns.org/vibe/VibeFinance",
|
||||
|
||||
+13
-2
@@ -91,7 +91,13 @@ if ($appVersion -match "-dev$") {
|
||||
exit 1
|
||||
}
|
||||
|
||||
$gitTag = "$appVersion"
|
||||
# Git-tag met `v`-prefix, net als de release-naam hieronder en net als de tags
|
||||
# t/m v1.0.0. Gitea toont de tag op de release-pagina, dus zonder prefix stond
|
||||
# daar het kale nummer. De image-tags in de registry houden bewust géén `v`:
|
||||
# dat is de conventie voor container-tags.
|
||||
# De update-check verdraagt beide: zowel backend/src/routes/update.js als
|
||||
# useUpdateCheck.js strippen een leidende `v` voordat ze versies vergelijken.
|
||||
$gitTag = "v$appVersion"
|
||||
|
||||
# ── Registry tags (één image voor frontend + backend) ─────────────────────
|
||||
$appBase = "$registry/$appImage"
|
||||
@@ -167,7 +173,12 @@ $nieuweKop = "## [$appVersion] - $(Get-Date -Format 'yyyy-MM-dd')"
|
||||
$changelogTxt = if (Test-Path $changelogPath) { [System.IO.File]::ReadAllText($changelogPath) } else { "" }
|
||||
|
||||
$kopPatroon = "(?m)^##\s*\[" + [regex]::Escape($appVersion) + "\]"
|
||||
$onderwegPatroon = "(?m)^##\s*\[\s*Niet vrijgegeven\s*\]\s*$"
|
||||
# Niet `\s*$`: `\s` matcht ook newlines, en dan slokt de match de lege regel ná
|
||||
# de kop op — die verdween daardoor bij het omzetten. En niet `[ \t]*$` alleen:
|
||||
# dit bestand heeft CRLF (zie .gitattributes), dus `$` staat achter een `\r` die
|
||||
# geen space of tab is. De lookahead laat het regeleinde buiten de match, zodat
|
||||
# de CRLF intact blijft.
|
||||
$onderwegPatroon = "(?m)^##[ \t]*\[[ \t]*Niet vrijgegeven[ \t]*\][ \t]*(?=\r?$)"
|
||||
|
||||
# Eerst alleen in het geheugen omzetten. Schrijven gebeurt na de controle
|
||||
# hieronder: bij een lege `[Niet vrijgegeven]`-sectie zou de kop anders al
|
||||
|
||||
Reference in New Issue
Block a user