From 1b09106aed09fc4527846104a1c1f6193b5fcfde Mon Sep 17 00:00:00 2001 From: antopoid Date: Sun, 22 Feb 2026 20:04:08 +0100 Subject: [PATCH] feat: Docker multi-stage build (Node build + nginx static serve) --- .dockerignore | 8 ++++++++ Dockerfile | 33 +++++++++++++++++++++++++++++++++ docker-entrypoint.sh | 22 ++++++++++++++++++++++ nginx.conf | 35 +++++++++++++++++++++++++++++++++++ 4 files changed, 98 insertions(+) create mode 100644 .dockerignore create mode 100644 Dockerfile create mode 100644 docker-entrypoint.sh create mode 100644 nginx.conf diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..70df20a --- /dev/null +++ b/.dockerignore @@ -0,0 +1,8 @@ +node_modules +.next +out +.git +.gitignore +*.md +*.xlsx +.gitea diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..7b156e6 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,33 @@ +# ── Stage 1: Build ──────────────────────────────────────────────────────────── +FROM node:18-alpine AS builder + +WORKDIR /app + +# Installer les dépendances d'abord (cache Docker) +COPY package*.json ./ +RUN npm ci + +# Copier le code source et builder +COPY . . +RUN npm run build + +# ── Stage 2: Production (nginx pour servir le static export) ───────────────── +FROM nginx:alpine + +# Copier la config nginx personnalisée +COPY nginx.conf /etc/nginx/conf.d/default.conf + +# Copier le build statique depuis le stage builder +COPY --from=builder /app/out /usr/share/nginx/html + +# Script d'entrée +COPY docker-entrypoint.sh / +RUN chmod +x /docker-entrypoint.sh + +EXPOSE 3000 + +HEALTHCHECK --interval=30s --timeout=10s --start-period=10s --retries=3 \ + CMD wget -qO- http://localhost:3000/ || exit 1 + +ENTRYPOINT ["/docker-entrypoint.sh"] +CMD ["nginx", "-g", "daemon off;"] diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh new file mode 100644 index 0000000..639aa1b --- /dev/null +++ b/docker-entrypoint.sh @@ -0,0 +1,22 @@ +#!/bin/sh +set -e + +# Vérifier que le build statique est présent +if [ ! -f "/usr/share/nginx/html/index.html" ]; then + echo "ERREUR: Build statique non trouvé (index.html manquant) !" + exit 1 +fi + +# Compter les fichiers servis +FILE_COUNT=$(find /usr/share/nginx/html -type f | wc -l) +echo "──────────────────────────────────────────" +echo " SimO — Simulateur de prêt immobilier" +echo "──────────────────────────────────────────" +echo " Fichiers statiques : ${FILE_COUNT}" +echo " Port : 3000" +echo " Serveur : nginx" +echo "──────────────────────────────────────────" +echo "Démarrage du serveur..." + +# Exécuter la commande par défaut (nginx) +exec "$@" diff --git a/nginx.conf b/nginx.conf new file mode 100644 index 0000000..8f4f955 --- /dev/null +++ b/nginx.conf @@ -0,0 +1,35 @@ +server { + listen 3000; + server_name _; + + root /usr/share/nginx/html; + index index.html; + + # Compression gzip + gzip on; + gzip_vary on; + gzip_proxied any; + gzip_comp_level 6; + gzip_types text/plain text/css application/json application/javascript text/xml application/xml text/javascript image/svg+xml; + + # Cache statique long (JS/CSS hachés par Next.js) + location /_next/static/ { + expires 1y; + add_header Cache-Control "public, immutable"; + } + + # Cache modéré pour les autres assets + location ~* \.(ico|png|jpg|jpeg|gif|svg|webp|woff2?|ttf|eot)$ { + expires 30d; + add_header Cache-Control "public"; + } + + # SPA fallback : toutes les routes renvoient index.html + location / { + try_files $uri $uri/ /index.html; + } + + # Pas de log pour favicon/robots + location = /favicon.ico { log_not_found off; access_log off; } + location = /robots.txt { log_not_found off; access_log off; } +}