Migratie naar 2-container opzet (app + database)
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>
This commit is contained in:
co-authored by
Claude Opus 5
parent
3c67381de9
commit
bfec3a25f3
@@ -0,0 +1,145 @@
|
||||
# ── VibeFinance testscript (PowerShell) ──────────────────────────────────
|
||||
# Draait de volledige testsuite voor de 2-container opzet. Alles gebeurt in
|
||||
# Docker, dus je hebt geen Node op de host nodig.
|
||||
#
|
||||
# 1. Backend-tests (Dockerfile target 'test' → node --test, zonder database)
|
||||
# 2. Productiebuild (Vite-build + app-image; faalt de frontend, faalt dit)
|
||||
# 3. Smoketest (productie-image + postgres, /api/health en / opvragen)
|
||||
#
|
||||
# De smoketest publiceert geen hostpoorten en gebruikt een eigen netwerk, dus
|
||||
# hij botst niet met een draaiende dev-stack.
|
||||
#
|
||||
# Gebruik:
|
||||
# .\test.ps1 # alle stappen
|
||||
# .\test.ps1 -Unit # alleen de backend-tests
|
||||
# .\test.ps1 -NoSmoke # tests + build, zonder de stack te starten
|
||||
|
||||
param(
|
||||
[switch]$Unit,
|
||||
[switch]$NoSmoke
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
$appVersion = (Get-Content (Join-Path $PSScriptRoot "package.json") -Raw | ConvertFrom-Json).version
|
||||
$testImage = "vibefinance/app:test-$appVersion"
|
||||
$prodImage = "vibefinance/app:build-$appVersion"
|
||||
$smokeNet = "vibefinance-smoke-net"
|
||||
$smokeDb = "vibefinance-smoke-db"
|
||||
$smokeApp = "vibefinance-smoke-app"
|
||||
|
||||
function Write-Step {
|
||||
param([string]$Label)
|
||||
Write-Host ""
|
||||
Write-Host "── $Label" -ForegroundColor Cyan
|
||||
}
|
||||
|
||||
# ── 1. Backend-tests ──────────────────────────────────────────────────────
|
||||
Write-Step "[1/3] Backend-tests"
|
||||
docker build --target test -t $testImage $PSScriptRoot
|
||||
if ($LASTEXITCODE -ne 0) { Write-Error "Testimage bouwen mislukt"; exit 1 }
|
||||
|
||||
docker run --rm $testImage
|
||||
if ($LASTEXITCODE -ne 0) { Write-Error "Backend-tests mislukt"; exit 1 }
|
||||
Write-Host "Backend-tests geslaagd" -ForegroundColor Green
|
||||
|
||||
if ($Unit) {
|
||||
Write-Host ""
|
||||
Write-Host "Alleen unit-tests gevraagd — klaar." -ForegroundColor Green
|
||||
exit 0
|
||||
}
|
||||
|
||||
# ── 2. Productiebuild (inclusief Vite-build) ──────────────────────────────
|
||||
Write-Step "[2/3] Productie-image bouwen"
|
||||
docker build --target production `
|
||||
--build-arg APP_VERSION=$appVersion `
|
||||
--build-arg VITE_APP_VERSION=$appVersion `
|
||||
--build-arg VITE_API_URL=/api `
|
||||
-t $prodImage $PSScriptRoot
|
||||
if ($LASTEXITCODE -ne 0) { Write-Error "Productiebuild mislukt"; exit 1 }
|
||||
Write-Host "Productie-image gebouwd: $prodImage" -ForegroundColor Green
|
||||
|
||||
if ($NoSmoke) {
|
||||
Write-Host ""
|
||||
Write-Host "Smoketest overgeslagen (-NoSmoke) — klaar." -ForegroundColor Green
|
||||
exit 0
|
||||
}
|
||||
|
||||
# ── 3. Smoketest op het productie-image + postgres ────────────────────────
|
||||
Write-Step "[3/3] Smoketest (app + postgres)"
|
||||
|
||||
function Remove-SmokeStack {
|
||||
docker rm -f $smokeApp $smokeDb 2>$null | Out-Null
|
||||
docker network rm $smokeNet 2>$null | Out-Null
|
||||
}
|
||||
|
||||
# Eventuele resten van een eerdere run opruimen
|
||||
Remove-SmokeStack
|
||||
|
||||
try {
|
||||
docker network create $smokeNet | Out-Null
|
||||
|
||||
docker run -d --name $smokeDb --network $smokeNet `
|
||||
-e POSTGRES_DB=vibefinance `
|
||||
-e POSTGRES_USER=vibefinance `
|
||||
-e POSTGRES_PASSWORD=smokepassword `
|
||||
postgres:18-alpine | Out-Null
|
||||
if ($LASTEXITCODE -ne 0) { throw "Database-container starten mislukt" }
|
||||
|
||||
Write-Host " Wachten op postgres..." -ForegroundColor DarkGray
|
||||
$dbGereed = $false
|
||||
foreach ($poging in 1..30) {
|
||||
docker exec $smokeDb pg_isready -U vibefinance 2>$null | Out-Null
|
||||
if ($LASTEXITCODE -eq 0) { $dbGereed = $true; break }
|
||||
Start-Sleep -Seconds 2
|
||||
}
|
||||
if (-not $dbGereed) { throw "Postgres werd niet bereikbaar" }
|
||||
|
||||
docker run -d --name $smokeApp --network $smokeNet `
|
||||
-e NODE_ENV=production `
|
||||
-e JWT_SECRET=smoke-secret `
|
||||
-e UPDATE_CHECK_URL= `
|
||||
-e DATABASE_URL=postgresql://vibefinance:smokepassword@$smokeDb`:5432/vibefinance `
|
||||
$prodImage | Out-Null
|
||||
if ($LASTEXITCODE -ne 0) { throw "App-container starten mislukt" }
|
||||
|
||||
Write-Host " Wachten op de app..." -ForegroundColor DarkGray
|
||||
$health = $null
|
||||
foreach ($poging in 1..30) {
|
||||
$health = (docker exec $smokeApp wget -qO- http://127.0.0.1:3001/api/health 2>$null) -join ""
|
||||
if ($LASTEXITCODE -eq 0 -and $health -match '"status"') { break }
|
||||
$health = $null
|
||||
Start-Sleep -Seconds 2
|
||||
}
|
||||
if (-not $health) {
|
||||
docker logs --tail 40 $smokeApp
|
||||
throw "App werd niet gezond"
|
||||
}
|
||||
Write-Host " /api/health : ok" -ForegroundColor Green
|
||||
|
||||
# wget levert meerdere regels; samenvoegen tot één string, want -match op een
|
||||
# array filtert de array in plaats van een test te doen.
|
||||
$index = (docker exec $smokeApp wget -qO- http://127.0.0.1:3001/) -join "`n"
|
||||
if ($LASTEXITCODE -ne 0 -or $index -notmatch 'id="root"') { throw "Root gaf geen index.html terug" }
|
||||
Write-Host " / : index.html" -ForegroundColor Green
|
||||
|
||||
$spa = (docker exec $smokeApp wget -qO- http://127.0.0.1:3001/voortgang) -join "`n"
|
||||
if ($LASTEXITCODE -ne 0 -or $spa -notmatch 'id="root"') { throw "SPA-fallback werkt niet" }
|
||||
Write-Host " /voortgang : index.html (SPA-fallback)" -ForegroundColor Green
|
||||
|
||||
$api404 = (docker exec $smokeApp wget -qO- --server-response http://127.0.0.1:3001/api/bestaat-niet 2>&1) -join "`n"
|
||||
if ($api404 -notmatch "404") { throw "Onbekende API-route gaf geen 404" }
|
||||
Write-Host " /api/bestaat-niet: 404" -ForegroundColor Green
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "Smoketest geslaagd" -ForegroundColor Green
|
||||
}
|
||||
finally {
|
||||
Remove-SmokeStack
|
||||
}
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "════════════════════════════════════════" -ForegroundColor Cyan
|
||||
Write-Host " Alle tests geslaagd - $appVersion" -ForegroundColor Green
|
||||
Write-Host "════════════════════════════════════════" -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
Reference in New Issue
Block a user