# ╔══════════════════════════════════════════════════════════════════════════╗ # ║ VibeFinance — één app-image: Express API + gebouwde Vite-frontend ║ # ║ ║ # ║ Targets: ║ # ║ test backend-tests (npm test, geen database nodig) ║ # ║ development API (node --watch) + Vite dev-server, hot reload ║ # ║ production API die de statische build op / serveert ║ # ╚══════════════════════════════════════════════════════════════════════════╝ # ── Stage 1: frontend dependencies ──────────────────────────────────────────── FROM node:22-alpine AS frontend-deps WORKDIR /build/frontend COPY frontend/package.json frontend/package-lock.json* ./ RUN npm install && npm cache clean --force # ── Stage 2: frontend bouwen (Vite → dist) ──────────────────────────────────── FROM frontend-deps AS frontend-build ARG VITE_API_URL=/api ARG VITE_APP_VERSION=dev ENV VITE_API_URL=$VITE_API_URL ENV VITE_APP_VERSION=$VITE_APP_VERSION COPY frontend/ ./ RUN npm run build # ── Stage 3: backend dependencies ───────────────────────────────────────────── FROM node:22-alpine AS backend-deps WORKDIR /app COPY backend/package.json backend/package-lock.json* ./ RUN npm install && npm cache clean --force # ── Stage 4: productie-dependencies (zonder devDependencies) ────────────────── FROM backend-deps AS backend-prod-deps RUN npm prune --omit=dev # ── Stage 5: tests (backend, zonder database) ───────────────────────────────── FROM backend-deps AS test WORKDIR /app ENV NODE_ENV=test COPY backend/ ./ CMD ["npm", "test"] # ── Stage 6: development (API + Vite samen, hot reload) ─────────────────────── FROM node:22-alpine AS development WORKDIR /app ENV NODE_ENV=development COPY --from=backend-deps /app/node_modules ./node_modules COPY --from=frontend-deps /build/frontend/node_modules ./frontend/node_modules COPY backend/ ./ COPY frontend/ ./frontend/ COPY docker/dev-entrypoint.sh /usr/local/bin/dev-entrypoint.sh RUN chmod +x /usr/local/bin/dev-entrypoint.sh # 5173 = Vite dev-server (browser), 3001 = API EXPOSE 5173 3001 CMD ["/usr/local/bin/dev-entrypoint.sh"] # ── Stage 7: production (minimale image) ────────────────────────────────────── FROM node:22-alpine AS production LABEL maintainer="VibeFinance" ARG APP_VERSION=dev ENV NODE_ENV=production ENV APP_VERSION=$APP_VERSION # ── Aanvalsoppervlak beperken ───────────────────────────────────────────────── # Twee stappen die samen elke CVE uit de imagescan wegnemen. Ze staan hier en # niet in een andere stage: alleen dit image gaat naar de registry. # # 1. OS-pakketten bijwerken. Het node-image wordt minder vaak herbouwd dan # Alpine patches uitbrengt, dus libssl3 en libcrypto3 lopen er structureel # op achter — goed voor twintig CVE's in de laatste scan. Dit haalt ze op # het moment van bouwen alsnog bij. # # 2. De pakketbeheerders eruit. De container start met `node src/server.js` en # gebruikt npm, npx, corepack of yarn nooit; ze zitten er alleen in omdat # het base-image ze meelevert. Alle negentien Node-CVE's uit de scan zaten # in npm's eigen meegeleverde dependencies (tar, pacote, sigstore, # brace-expansion, picomatch, ip-address) en hadden niets met deze # applicatie te maken. Weg met de tool is weg met die hele categorie — # inclusief de volgende ronde, want ze komen steeds terug. RUN apk upgrade --no-cache \ && rm -rf /usr/local/lib/node_modules/npm \ /usr/local/lib/node_modules/corepack \ /usr/local/bin/npm /usr/local/bin/npx /usr/local/bin/corepack \ /usr/local/bin/yarn /usr/local/bin/yarnpkg \ /opt/yarn-v* RUN addgroup -S appgroup && adduser -S appuser -G appgroup WORKDIR /app COPY --from=backend-prod-deps /app/node_modules ./node_modules COPY backend/package.json ./ COPY backend/src/ ./src/ # De frontend-build wordt door de backend geserveerd (config.STATIC_DIR) COPY --from=frontend-build /build/frontend/dist ./public/ RUN chown -R appuser:appgroup /app USER appuser EXPOSE 3001 HEALTHCHECK --interval=30s --timeout=5s --start-period=15s --retries=3 \ CMD wget -qO- http://localhost:3001/api/health || exit 1 CMD ["node", "src/server.js"]