De Express-app serveert nu zelf de gebouwde Vite-frontend op / met een SPA-fallback naar index.html; de aparte nginx-container is vervallen. Bedoeld voor self-hosting op een Raspberry Pi: één image, één origin, geen interne proxy en geen CORS tussen frontend en API. Docker - Nieuwe multi-stage Dockerfile in de root met targets test, development en production; frontend/Dockerfile, frontend/nginx-spa.conf en backend/Dockerfile verwijderd - docker-compose.yml: alleen app + postgres, app publiceert APP_PORT (standaard 3400) naar containerpoort 3001 - docker-compose.dev.yml: dev-container draait API (node --watch) en de Vite dev-server samen via docker/dev-entrypoint.sh, hot reload intact - .dockerignore, .env.example, .gitignore en .gitattributes toegevoegd Backend - express.static + SPA-fallback op STATIC_DIR (default /app/public), met immutable caching voor gehashte assets en no-store voor index.html - Rate limiting alleen nog op /api — statische bestanden liepen anders na 200 requests tegen de limiet aan - Helmet CSP toegespitst op wat de frontend laadt (Google Fonts, inline styles, base64-avatars); upgrade-insecure-requests uit voor intern http - Schema-init verplaatst naar initDb(), niet meer bij import van db.js Tests en CI/CD - backend/test/app.test.js: 11 tests (node --test) voor API-routes, SPA-fallback, cache- en security-headers, zonder database - test.ps1 / test.sh: backend-tests, productiebuild en smoketest op de echte app + postgres stack - azure-pipelines.yml: Test -> Build (incl. smoketest) -> Publish (multi-arch amd64 + arm64, alleen op main/master of een tag) Scripts en documentatie - dev.ps1/dev.sh met targets app en postgres, acties logs en test; dev.ps1 up wist de dev-database niet langer (alleen dev.ps1 down) - release.ps1/release.sh bouwen één image voor amd64 + arm64 en draaien de testsuite vóór het publiceren - README.md en BEHEER.md bijgewerkt voor de nieuwe structuur Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
8.9 KiB
VibeFinance — Beheer
De app draait in 2 containers: app (Express API + gebouwde frontend) en
postgres. Eén image, één origin — geen aparte webserver en geen interne proxy.
Ontwikkeling
.\dev.ps1 # build + start (standaard)
.\dev.ps1 build # alleen bouwen
.\dev.ps1 up -Target app # alleen de app herbouwen + herstarten
.\dev.ps1 up -Target postgres # alleen de database herstarten
.\dev.ps1 logs # logs van de app volgen
.\dev.ps1 test # testsuite draaien
.\dev.ps1 down # stoppen én volumes wissen
.\dev.ps1 push # dev-image naar registry pushen
Op macOS/Linux: gebruik
./dev.shmet dezelfde argumenten.
In dev draait één container beide processen (zie docker/dev-entrypoint.sh):
| Adres | Wat |
|---|---|
| http://localhost | Vite dev-server met hot reload |
| http://localhost:3001/api/health | de API rechtstreeks |
Vite proxyt /api naar localhost:3001 binnen dezelfde container.
⚠️ Alleen
.\dev.ps1 downwist de development-database (docker compose down -v)..\dev.ps1 updoet een gewonedownen laat het volume staan.
Testen
.\test.ps1 # backend-tests + productiebuild + smoketest
.\test.ps1 -Unit # alleen de backend-tests
.\test.ps1 -NoSmoke # tests + build, zonder de stack te starten
Alles loopt in Docker; Node op de host is niet nodig.
| Stap | Wat er gebeurt |
|---|---|
| Backend-tests | Dockerfile target test → node --test in backend/test/, zonder database |
| Productiebuild | Dockerfile target production — faalt de Vite-build, faalt deze stap |
| Smoketest | app + postgres starten en /api/health en / opvragen |
Losse backend-tests draaien in een lopende dev-container:
docker compose -f docker-compose.dev.yml exec app npm test
Release workflow
# 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 = "<token>"
.\release.ps1 # test, bouw (amd64+arm64), push, tag, Gitea-release
.\release.ps1 -DryRun # droogloop — toont alle stappen zonder uit te voeren
.\release.ps1 -NoBuild # sla docker build over, alleen tag + release
.\release.ps1 -SkipTests # sla de testsuite over (niet aanbevolen)
# 4. Versienummer in package.json naar "x.x.(x+1)-dev" voor volgende cyclus
Het script bouwt één image voor linux/amd64 en linux/arm64, zodat dezelfde
tag op een Raspberry Pi en op een x86-server werkt.
CI/CD (Azure DevOps)
azure-pipelines.yml doet in drie stages hetzelfde als de lokale scripts:
| Stage | Wat |
|---|---|
Test |
docker build --target test + docker run → backend-tests |
Build |
productie-image bouwen en smoketesten tegen een postgres-container |
Publish |
multi-arch image (amd64 + arm64) pushen naar de registry |
Publish loopt alleen op main/master of een tag. Vul vóór het eerste gebruik
de variabele dockerRegistryServiceConnection in met de naam van je Docker
Registry service connection in Azure DevOps.
Docker
# Update via pre-built images
docker compose pull && docker compose up -d
# Reset inclusief database ⚠️ wist alle data
docker compose down -v && docker compose up -d
PostgreSQL versie-upgrade
Bij een upgrade van PostgreSQL (bijv. 16 → 18) is de bestaande data niet compatibel. Stappen:
# 1. Backup maken
docker exec vibefinance_postgres pg_dump -U vibefinance vibefinance > backup-$(date +%F).sql
# 2. Oude containers en volumes verwijderen
docker compose down -v
# 3. Nieuwe versie starten
docker compose up -d
# 4. Backup terugzetten
docker exec -i vibefinance_postgres psql -U vibefinance -d vibefinance < backup-YYYY-MM-DD.sql
Architectuur — één app-container
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 |
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
alleen de API en logt dat bij het starten.
Ontbreekt de build in het image, dan krijg je op
/een 404 en werkt alleen/api/.... Controleer dan defrontend-buildstage in deDockerfile.
Logs en diagnose
# Live logs (dev) — beide processen van de app-container
docker compose -f docker-compose.dev.yml logs -f app
# Live logs (productie)
docker compose logs -f
# Shell in de app-container (dev)
docker compose -f docker-compose.dev.yml exec app sh
# Status containers
docker compose ps
# Database inspecteren (dev)
docker exec -it vibefinance-postgres-dev psql -U vibefinance -d vibefinance
# Database inspecteren (productie)
docker exec -it vibefinance_postgres psql -U vibefinance -d vibefinance
Data persistentie
De backend slaat alle data op in PostgreSQL.
| 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 gemount op/var/lib/postgresqlzodat data bewaard blijft na een container restart.
Backup maken:
# Dev
docker exec vibefinance-postgres-dev pg_dump -U vibefinance vibefinance > backup-$(date +%F).sql
# Productie
docker exec vibefinance_postgres pg_dump -U vibefinance vibefinance > backup-$(date +%F).sql
Backup terugzetten:
# Dev
docker exec -i vibefinance-postgres-dev psql -U vibefinance -d vibefinance < backup-2026-04-08.sql
# Productie
docker exec -i vibefinance_postgres psql -U vibefinance -d vibefinance < backup-2026-04-08.sql
Gebruikersbeheer
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 |
|---|---|
Admin |
Alles, inclusief gebruikersbeheer |
Bewerker |
Data lezen en bewerken |
Viewer |
Alleen lezen |
Productie checklist
POSTGRES_PASSWORDinstellen op een sterk wachtwoordJWT_SECRETaanpassen (genereer metopenssl rand -hex 64)CORS_ORIGINinstellen op je domeinnaamAPP_PORTaanpassen indien poort 3400 al in gebruik is- Testsuite groen (
test.ps1/./test.sh) - TLS afhandelen via een externe reverse proxy (bijv. Nginx Proxy Manager, Caddy, Traefik)
Registry
vibehoogie.duckdns.org/vibe/vibefinance-app (frontend + backend in één image)
Tags:
latest productie — meest recente release
x.x.x productie — specifieke versie
latest-dev development — meest recente dev build
x.x.x-dev development — specifieke dev versie
Inloggen op registry:
docker login vibehoogie.duckdns.org
Update-notificaties
De app vergelijkt de draaiende versie met de laatste release op Gitea en toont dat op het belletje in de topbalk en onder "Over VibeFinance".
Hoe de check loopt
- De frontend vraagt
/api/update-checkop de eigen backend — de Gitea API stuurt geen CORS-headers, dus de browser mag die niet direct bevragen. - De backend haalt
releases/latestbij Gitea op en cachet het antwoord 15 minuten. - De frontend kijkt bij het openen, daarna elk half uur en bij het terugkeren naar het tabblad. Alleen een écht hoger versienummer geeft een melding.
Builds met een -dev suffix melden nooit (die lopen voor op de laatste release).
Wil je de melding tijdens ontwikkeling toch testen: open de app met ?updatecheck
in de URL.
Configuratie
De bron staat in de root package.json:
"repository": "https://vibehoogie.duckdns.org/vibe/VibeFinance",
"updateCheckUrl": "https://vibehoogie.duckdns.org/api/v1/repos/vibe/VibeFinance/releases/latest"
dev.ps1 zet die URL als UPDATE_CHECK_URL in de omgeving; in productie komt hij
uit .env (of de default in docker-compose.yml). Beschikbare backend-variabelen:
| 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. |
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:
$env:GITEA_TOKEN = "<token>"
.\release.ps1
Zonder token slaat het script die stap over met een waarschuwing — images en tag staan dan wel klaar, maar gebruikers zien geen melding tot de release bestaat.