diff --git a/.dockerignore b/.dockerignore index cde8378..0167262 100644 --- a/.dockerignore +++ b/.dockerignore @@ -15,3 +15,6 @@ release.ps1 release.sh test.ps1 test.sh + +# Documentatie-afbeeldingen horen niet in het image +docs/ diff --git a/.env.example b/.env.example index 8b027ae..94375c6 100644 --- a/.env.example +++ b/.env.example @@ -17,5 +17,6 @@ CORS_ORIGIN=http://localhost:3400 # Update-notificaties (leeg = melding uit) UPDATE_CHECK_URL=https://vibehoogie.duckdns.org/api/v1/repos/vibe/VibeFinance/releases/latest -# Alleen nodig als de Gitea-repo niet publiek leesbaar is +# Nodig zolang de Gitea-repo niet publiek leesbaar is; scope read:repository. +# Zonder token geeft releases/latest 404 en blijft de update-melding leeg. # UPDATE_CHECK_TOKEN= diff --git a/.gitattributes b/.gitattributes index a657db7..08d10a9 100644 --- a/.gitattributes +++ b/.gitattributes @@ -9,3 +9,7 @@ docker/* text eol=lf # PowerShell mag CRLF houden (Windows-tooling) *.ps1 text eol=crlf + +# Afbeeldingen nooit door de regeleinde-normalisatie halen +*.png binary +*.jpg binary diff --git a/BEHEER.md b/BEHEER.md index b8925f6..fb30e79 100644 --- a/BEHEER.md +++ b/BEHEER.md @@ -74,15 +74,79 @@ docker compose -f docker-compose.dev.yml exec -w /app/frontend app npm run lint ``` --- + +## Tokens + +Twee tokens, gesplitst op machine en op read/write. De grens die iets betekent +ligt tussen werkstation en server, niet tussen push en release: op één machine +kan wie het ene token kan lezen, het andere ook. + +| Token | Machine | Scopes | Gebruikt voor | +|---|---|---|---| +| `vibefinance-workstation` | dit werkstation | `write:package`, `write:repository` | `docker push` (`release.ps1`, `dev.ps1 push`) en het aanmaken van de Gitea-release | +| `vibefinance-host` | de Docker-host | `read:package`, `read:repository` | `docker compose pull` en de update-check van de app | + +Aanmaken: Gitea → Settings → Applications → Generate New Token. Doe dat onder een +account dat lid is van de `Vibe`-organisatie — die is privé, dus een token van +een buitenstaander ziet de repo niet. + +**Waar ze terechtkomen** + +| | Werkstation | Docker-host | +|---|---|---| +| Registry | `docker login` (credential store), of `REGISTRY_USER` + `REGISTRY_TOKEN` in `.env.release` | `docker login` als de gebruiker die deployt | +| Gitea-API | `GITEA_TOKEN` in `.env.release` | `UPDATE_CHECK_TOKEN` in `.env` | + +**Waarom het host-token read-only blijft.** Dat staat in platte tekst in `.env` op +een server die de app op poort 3400 publiceert. Met schrijfrechten kan wie bij dat +bestand komt — via de container, een shell op die host, of een backup — een eigen +image als `latest` naar de registry pushen, en die host pullt `latest` zelf. +Read-only houdt je eigen deploy-pad uit de aanvalsketen. + +### Wat een foutcode betekent + +| Status | Betekenis | +|---|---| +| 401 | Token ongeldig of verlopen. Op de registry: helemaal niet ingelogd. | +| 403 | Het token mag de repo wél zien, maar mist de **scope** voor dit endpoint. Gitea noemt de vereiste scope in de response-body. | +| 404 | Het token mag de repo niet zien. Gitea geeft geen 403, om niet te verklappen dat iets bestaat: verkeerd account, of geen toegang tot de organisatie. | + +Scopes van een bestaand token zijn in Gitea niet aan te passen. Bij een 403 maak +je dus een nieuw token aan; bijwerken kan niet. + +Nakijken wie een token is en wat hij ziet: + +```powershell +$h = @{ Authorization = "token $env:GITEA_TOKEN" } +(Invoke-RestMethod https://vibehoogie.duckdns.org/api/v1/user -Headers $h).login +(Invoke-RestMethod https://vibehoogie.duckdns.org/api/v1/user/repos -Headers $h).full_name +``` + +Vanuit de app-container op de host valt de body van een fout anders weg — +busybox-`wget` gooit die bij een foutstatus weg, en Node zit in het image: + +```bash +docker exec vibefinance_app node -e 'fetch(process.env.UPDATE_CHECK_URL,{headers:{Accept:"application/json",Authorization:`token ${process.env.UPDATE_CHECK_TOKEN}`}}).then(async r=>console.log(r.status, (await r.text()).slice(0,300)))' +``` + +### Tokens opruimen + +De lijst onder Settings → Applications heeft een kolom **Last used**. Zet eerst +beide machines om, draai daarna een release en een pull, en kijk een dag later +welke tokens niet meer meebewegen — die kun je intrekken. Betrouwbaarder dan uit +de naam afleiden wat waar in gebruik is. Trek niets in vóór die controle: het +token achter je `docker login` op dit werkstation laat een release op de push +falen. + --- ## Release workflow ```powershell # 1. Versienummer in package.json van "x.x.x-dev" naar "x.x.x" -# 2. Changelog.md bijwerken — die sectie wordt de release-notes -# 3. Token met release-rechten zetten -$env:GITEA_TOKEN = "" +# 2. Changelog.md: kop van "Niet vrijgegeven" naar "[x.x.x] - datum" +# 3. Token staat in .env.release (zie Tokens) — of eenmalig in de shell: +$env:GITEA_TOKEN = "" .\release.ps1 # test, bouw (amd64+arm64), push, tag, Gitea-release .\release.ps1 -DryRun # droogloop — toont alle stappen zonder uit te voeren @@ -95,41 +159,44 @@ $env:GITEA_TOKEN = "" Het script bouwt één image voor `linux/amd64` en `linux/arm64`, zodat dezelfde tag op een Raspberry Pi en op een x86-server werkt. -### Tokens niet elke sessie opnieuw zetten +**Wat het script zelf bijwerkt** — met de hand aanpassen is dus dubbel werk: + +| Bestand | Wat | +|---|---| +| `frontend/package.json` | versie gelijkgetrokken met de root `package.json` | +| `README.md` | de `versie-x.x.x`-badge bovenaan | + +Beide gebeuren vóór de build, en een `-DryRun` meldt ze zonder te schrijven. Ze +blijven na een echte release als wijziging in je working tree staan; neem ze mee +in de volgende commit. + +> **De changelog-kop moet exact `## [x.x.x]` zijn.** `release.ps1` zoekt die kop +> om de release-notes te vullen. Staat je werk nog onder `## [Niet vrijgegeven]`, +> dan vindt het script niets en valt het terug op de tekst `"Release x.x.x"` — +> dan staat er geen inhoudelijke release-notes in Gitea en ziet een gebruiker in +> het belletje een placeholder. Dat overkwam 0.1.9. Achteraf repareren kan met +> een `PATCH` op `/repos///releases/`, maar vóór de release +> omzetten is een stuk minder werk. + +### `.env.release` — token niet elke sessie opnieuw zetten Staat er een `.env.release` naast `release.ps1`, dan leest het script die in -voordat er iets gebeurt. Handig voor stap 3, die anders elke nieuwe shell -opnieuw met de hand moet: +voordat er iets gebeurt, en hoeft stap 3 niet meer met de hand: ``` # .env.release — staat in .gitignore, hoort alleen op de release-machine -GITEA_TOKEN= +GITEA_TOKEN= REGISTRY_USER= -REGISTRY_TOKEN= +REGISTRY_TOKEN= ``` -Een variabele die al in je shell staat wint, zodat je per run kunt afwijken -zonder het bestand aan te passen. Het script logt alleen welke namen het heeft -geladen, nooit de waarden. +Beide tokenvariabelen krijgen hetzelfde token; zie [Tokens](#tokens) voor waarom +dat op deze machine één token mag zijn, en voor wat een 401, 403 of 404 van Gitea +betekent. -Twee **verschillende** tokens is hier de bedoeling: de registry heeft -`write:package` nodig en het aanmaken van een release `write:repository`. Eén -breed token voor beide werkt ook, maar dan gaat een repo-schrijfrecht mee bij -elke `docker push`. - -> Een **404** in stap 5 betekent bijna altijd dat de token de repo niet mag -> zien: Gitea geeft op repo-endpoints 404 in plaats van 403, om niet te -> verklappen dat iets bestaat. Controleer dan eerst wie de token is en wat hij -> ziet: -> -> ```powershell -> $h = @{ Authorization = "token $env:GITEA_TOKEN" } -> (Invoke-RestMethod https://vibehoogie.duckdns.org/api/v1/user -Headers $h).login -> (Invoke-RestMethod https://vibehoogie.duckdns.org/api/v1/user/repos -Headers $h).full_name -> ``` -> -> Staat de repo niet in die lijst, dan mist de token de repository-scope of -> hoort hij bij een account zonder toegang. +Een variabele die al in je shell staat wint, zodat je per run kunt afwijken zonder +het bestand aan te passen. Het script logt alleen welke namen het heeft geladen, +nooit de waarden. --- @@ -319,17 +386,13 @@ Inloggen op de registry: docker login vibehoogie.duckdns.org ``` -Gebruikersnaam is je Gitea-account, wachtwoord een **access token** met scope -`write:package` (Gitea → Settings → Applications → Generate New Token). Zonder -login faalt een push met `401 Unauthorized`; `release.ps1` en `dev.ps1 push` -controleren dit vóór de build en stoppen dan meteen. +Gebruikersnaam is je Gitea-account, wachtwoord het **access token** van deze +machine — `vibefinance-workstation`, zie [Tokens](#tokens). Zonder login faalt +een push met `401 Unauthorized`; `release.ps1` en `dev.ps1 push` controleren dit +vóór de build en stoppen dan meteen. -Liever niet handmatig inloggen? Zet deze variabelen, dan logt het script zelf in: - -```powershell -$env:REGISTRY_USER = "" -$env:REGISTRY_TOKEN = "" -``` +Liever niet handmatig inloggen? Zet `REGISTRY_USER` en `REGISTRY_TOKEN` in +`.env.release`, dan logt het script zelf in. Twee valkuilen: log in op `vibehoogie.duckdns.org` **zonder** `:3000` (dat is een aparte sleutel in de credential store), en doe het als dezelfde Windows-gebruiker @@ -350,7 +413,7 @@ of "pull access denied"). Eenmalig op de Docker-host: ```bash docker login vibehoogie.duckdns.org # gebruikersnaam: je Gitea-account -# wachtwoord: access token met scope read:package +# wachtwoord: het token van deze host (vibefinance-host, read:package) ``` Daarna werkt `docker compose pull && docker compose up -d`. De login komt in @@ -364,6 +427,9 @@ Controleren zonder te pullen: docker manifest inspect vibehoogie.duckdns.org/vibe/vibefinance-app:latest ``` +Ditzelfde token gaat als `UPDATE_CHECK_TOKEN` in de `.env` op deze host, voor de +update-check van de app — één token voor beide, zie [Tokens](#tokens). + --- ## Update-notificaties @@ -398,16 +464,17 @@ uit `.env` (of de default in `docker-compose.yml`). Beschikbare backend-variabel | Variabele | Betekenis | |---|---| | `UPDATE_CHECK_URL` | Release-API die gepolld wordt. Leeg = melding uit. | -| `UPDATE_CHECK_TOKEN` | Alleen nodig als de repo in Gitea niet publiek leesbaar is. | +| `UPDATE_CHECK_TOKEN` | Nodig zolang de `Vibe`-organisatie in Gitea privé is: het token van de host (`read:repository`, zie [Tokens](#tokens)). Zonder token geeft `releases/latest` 404, met een verkeerde scope 403 — in beide gevallen blijft de melding leeg, met alleen een `WARN` in het containerlog. | **Een release publiceren** Een kale Git-tag is niet genoeg: `releases/latest` geeft alleen echte releases terug. `release.ps1` maakt die aan (stap 4) met de changelog-sectie van die versie als -release-notes. Daarvoor is een Gitea-token met release-rechten nodig: +release-notes. Daarvoor is het werkstation-token met `write:repository` nodig +(zie [Tokens](#tokens)), meestal al aanwezig via `.env.release`: ```powershell -$env:GITEA_TOKEN = "" +$env:GITEA_TOKEN = "" # of gewoon uit .env.release .\release.ps1 ``` diff --git a/Changelog.md b/Changelog.md index 1bacf58..5f017c2 100644 --- a/Changelog.md +++ b/Changelog.md @@ -2,6 +2,33 @@ ## [Niet vrijgegeven] +### Gewijzigd +- `BEHEER.md` heeft één centrale `## Tokens`-sectie met het tweetoken-model + (werkstation `write:*`, Docker-host `read:*`) plus de betekenis van een 401, + 403 en 404 van Gitea; de Registry-, pull- en update-secties verwijzen daarnaar + in plaats van de scopes elk apart uit te leggen +- `.env.example` noemt bij `UPDATE_CHECK_TOKEN` de vereiste scope + (`read:repository`) en wat je ziet als die ontbreekt +- "Over VibeFinance" noemt de beschikbare update nu "Nieuwste versie" in plaats + van "Nieuw beschikbaar" +- `README.md` bijgewerkt: de Quick Start noemt de `docker login` die een pull uit + de privé-registry vereist (die stap ontbrak), de release-sectie verwijst naar + `.env.release` en `write:repository`, en `UPDATE_CHECK_TOKEN` noemt zijn scope +- `release.ps1` werkt de versiebadge in `README.md` bij, net als de versie in + `frontend/package.json` — die badge stond drie releases achter. Een `-DryRun` + meldt de wijziging en schrijft niet +- `README.md` heeft ruimte voor een screenshot onder de badges + (`docs/screenshot-dashboard.png`); `docs/` staat in `.dockerignore` en `.png` + en `.jpg` zijn in `.gitattributes` als binair vastgelegd +- Badges `React_18` → `React_19` (de app draait op React 19.2) +- `BEHEER.md` legt bij de release-workflow vast wat het script zelf bijwerkt + (`frontend/package.json` en de README-badge), en dat de changelog-kop exact + `## [x.x.x]` moet zijn — anders vallen de release-notes terug op + `"Release x.x.x"`, zoals bij 0.1.9 gebeurde +- Projectboom in `README.md` bevat `docs/` en `frontend/src/version.js` + +## [0.1.9] - 2026-09-09 + ### Gewijzigd - `release.ps1` leest een `.env.release` naast het script in (niet in git), zodat `GITEA_TOKEN` en de registry-credentials niet elke shell opnieuw gezet hoeven diff --git a/README.md b/README.md index d0ebb2b..87e3139 100644 --- a/README.md +++ b/README.md @@ -6,13 +6,15 @@ Eigen vermogen · Schulden · Voortgang · Gebruikersbeheer -![React](https://img.shields.io/badge/React_18-20232A?style=flat&logo=react&logoColor=61DAFB) +![React](https://img.shields.io/badge/React_19-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) +![Version](https://img.shields.io/badge/versie-0.1.9-8b5cf6?style=flat) ![Built with Claude](https://img.shields.io/badge/Gebouwd_met-Claude.ai-8b5cf6?style=flat&logo=anthropic&logoColor=white) +VibeFinance dashboard + --- @@ -46,6 +48,10 @@ openssl rand -hex 64 ```bash cp .env.example .env # en vul de waarden in + +# Het app-image staat in een privé-registry, dus eerst inloggen: +docker login vibehoogie.duckdns.org # wachtwoord = Gitea-token met read:package + docker compose pull && docker compose up -d ``` @@ -109,7 +115,8 @@ backend/package.json (15 dependencies) ### Productie release ```powershell -$env:GITEA_TOKEN = "" +# token: staat in .env.release, of eenmalig in de shell +$env:GITEA_TOKEN = "" .\release.ps1 ``` @@ -147,11 +154,15 @@ vibefinance/ ├── 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 │ ├── main.jsx +│ ├── version.js # Draaiende versie (__APP_VERSION__ uit de build) │ ├── constants/ # Kleuren, tabs, begindata │ ├── utils/ # Formatters, helpers │ ├── hooks/ # useTheme, useUpdateCheck @@ -248,7 +259,7 @@ Het schema wordt bij het starten van de app aangemaakt/bijgewerkt via | `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 | +| `UPDATE_CHECK_TOKEN` | — | Nodig zolang de Gitea-repo privé is; scope `read:repository` | --- @@ -277,7 +288,9 @@ Het schema wordt bij het starten van de app aangemaakt/bijgewerkt via ## 🛠️ Beheer -Zie `BEHEER.md` voor commando's voor lokale ontwikkeling, releases, logs en database-toegang. +Zie `BEHEER.md` voor commando's voor lokale ontwikkeling, releases, logs en +database-toegang — en voor de sectie **Tokens**: welk Gitea-token op welke +machine hoort, met welke scopes, en wat een 401, 403 of 404 van Gitea betekent. --- diff --git a/docs/screenshot-dashboard.png b/docs/screenshot-dashboard.png new file mode 100644 index 0000000..69a03b1 Binary files /dev/null and b/docs/screenshot-dashboard.png differ diff --git a/frontend/package.json b/frontend/package.json index 887a865..0eed82e 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "vibefinance-frontend", - "version": "0.1.8", + "version": "1.0.0", "private": true, "type": "module", "scripts": { diff --git a/frontend/src/components/NavBar.jsx b/frontend/src/components/NavBar.jsx index ba57ce4..ab7daa4 100644 --- a/frontend/src/components/NavBar.jsx +++ b/frontend/src/components/NavBar.jsx @@ -165,7 +165,7 @@ function OverPopup({ onClose, nieuweVersie, T }) { {nieuweVersie && (
- Nieuw beschikbaar + Nieuwste versie v{nieuweVersie.latest} diff --git a/release.ps1 b/release.ps1 index 2f452fc..9228163 100644 --- a/release.ps1 +++ b/release.ps1 @@ -90,6 +90,26 @@ if ($frontendPkg.version -ne $appVersion) { } } +# ── Versiebadge in README.md ────────────────────────────────────────────── +# Anders loopt die badge stil achter: hij stond drie releases achter voordat +# dit erin zat, en het script kent het nummer toch al. Zelfde regel als +# hierboven: een droogloop meldt het, maar schrijft niet. +$readmePath = Join-Path $PSScriptRoot "README.md" +if (Test-Path $readmePath) { + $readme = [System.IO.File]::ReadAllText($readmePath) + $patroon = "badge/versie-[^-]+-" + $nieuw = "badge/versie-$appVersion-" + $huidig = [regex]::Match($readme, $patroon) + if ($huidig.Success -and $huidig.Value -ne $nieuw) { + if ($DryRun) { + Write-Host "[DRY-RUN] Versiebadge in README.md: $($huidig.Value) -> $nieuw" -ForegroundColor Yellow + } else { + [System.IO.File]::WriteAllText($readmePath, ($readme -replace $patroon, $nieuw)) + Write-Host "Versiebadge in README.md bijgewerkt: $appVersion" -ForegroundColor Green + } + } +} + $gitTag = "$appVersion" # ── Registry tags (één image voor frontend + backend) ─────────────────────