--- phase: 03-pages-ship plan: 04 type: execute wave: 3 depends_on: ["03-02", "03-03"] files_modified: - Dockerfile - docker-compose.yml - nuxt.config.ts autonomous: true requirements: - INFRA-01 - INFRA-04 must_haves: truths: - "docker build -t portfolio . reussit sans erreur" - "docker run -p 3000:3000 portfolio sert l'app SSR sur port 3000" - "GA4 est actif uniquement en production" - "app/pages/formation.vue n'existe pas, /formation retourne 404" artifacts: - path: "Dockerfile" provides: "Multi-stage SSR build node:22-alpine" - path: "docker-compose.yml" provides: "Config Traefik avec port 3000" key_links: - from: "Dockerfile" to: ".output/server/index.mjs" via: "node .output/server/index.mjs" pattern: "node.*\\.output/server/index\\.mjs" - from: "docker-compose.yml" to: "Traefik" via: "loadbalancer.server.port=3000" pattern: "port=3000" --- Finaliser l'infrastructure de deploiement : Dockerfile SSR multi-stage, config GA4 production-only, mise a jour docker-compose Traefik, gestion de la page formation supprimee, et nettoyage legacy. Purpose: Rend le portfolio deployable en production via Docker + Traefik avec analytics. Output: Dockerfile SSR fonctionnel, GA4 configure, docker-compose mis a jour. @C:\Users\minit\.claude\get-shit-done\workflows\execute-plan.md @C:\Users\minit\.claude\get-shit-done\templates\summary.md @.planning/PROJECT.md @.planning/ROADMAP.md @.planning/phases/03-pages-ship/03-CONTEXT.md @.planning/phases/03-pages-ship/03-RESEARCH.md @.planning/phases/03-pages-ship/03-02-SUMMARY.md @.planning/phases/03-pages-ship/03-03-SUMMARY.md @Dockerfile @docker-compose.yml @nuxt.config.ts Task 1: Dockerfile SSR multi-stage + docker-compose Traefik port 3000 Dockerfile, docker-compose.yml **Dockerfile** (per D-12, D-13, INFRA-01) : Reecrire completement le Dockerfile existant (qui copie dist/ vers nginx). Implementation exacte du RESEARCH.md Pattern 6 : ```dockerfile # Stage 1: Build FROM node:22-alpine AS builder WORKDIR /app COPY package*.json ./ RUN npm ci COPY . . RUN npm run build # Stage 2: Runtime FROM node:22-alpine AS runner ENV NODE_ENV=production ENV HOST=0.0.0.0 ENV PORT=3000 WORKDIR /app COPY --from=builder /app/.output /app/.output EXPOSE 3000 CMD ["node", "/app/.output/server/index.mjs"] ``` IMPORTANT : Copie `.output/` PAS `dist/` (per RESEARCH.md Pitfall 3). Pas de nginx. Node sert directement. Ajouter un `.dockerignore` s'il n'existe pas : ``` node_modules .nuxt .output dist src .git *.md .planning ``` **docker-compose.yml** (per D-14) : Modifier la ligne port Traefik : ```yaml - 'traefik.http.services.portfolio.loadbalancer.server.port=3000' # was 80 ``` Changer uniquement cette ligne. Conserver tout le reste intact (labels Traefik TLS, routeurs, redirections www). Ajouter les variables d'environnement SMTP dans la section `environment` du service portfolio : ```yaml environment: - TZ=Europe/Paris - NUXT_SMTP_HOST=${NUXT_SMTP_HOST} - NUXT_SMTP_USER=${NUXT_SMTP_USER} - NUXT_SMTP_PASS=${NUXT_SMTP_PASS} - NUXT_SMTP_TO=${NUXT_SMTP_TO} - NUXT_PUBLIC_GTAG_ID=${NUXT_PUBLIC_GTAG_ID} ``` cd C:/Users/minit/Desktop/portfolio/portfolio && grep -q ".output/server/index.mjs" Dockerfile && grep -q "node:22-alpine" Dockerfile && grep -q "port=3000" docker-compose.yml && echo "PASS" Dockerfile SSR multi-stage node:22-alpine avec .output/, docker-compose port 3000, variables env SMTP/GA4 Task 2: GA4 production-only + legacy cleanup nuxt.config.ts **GA4 nuxt-gtag** (per D-15, INFRA-04) : Verifier/mettre a jour `nuxt.config.ts` pour que nuxt-gtag soit configure correctement. Le config existant a deja : ```typescript gtag: { id: '', enabled: import.meta.env.NODE_ENV === 'production', }, ``` Verifier que `runtimeConfig.public.gtag.id` est bien present (deja fait en Plan 01 pour SMTP). Le `NUXT_PUBLIC_GTAG_ID` sera injecte au runtime sans rebuild (per D-13). Rien a changer si deja correct — juste verifier. **Formation** (per D-19) : Completement supprimee. Si `app/pages/formation.vue` existe, le supprimer. Pas de redirection, pas de routeRules — /formation retourne 404 naturellement. **Nettoyage complet legacy :** Supprimer le dossier `src/`, `old/`, `nginx.conf`, `index.html`, `eslint.config.ts`, `env.d.ts` — tout le legacy de l'ancien SPA Vue. Le repo doit etre propre apres cette phase. cd C:/Users/minit/Desktop/portfolio/portfolio && grep -q "production" nuxt.config.ts && ! test -f app/pages/formation.vue && ! test -d src && echo "PASS" GA4 nuxt-gtag actif en production via runtimeConfig, formation completement supprimee, legacy src/ et fichiers SPA supprimes ## Trust Boundaries | Boundary | Description | |----------|-------------| | Docker env vars -> runtimeConfig | Variables SMTP passees au container via docker-compose | ## STRIDE Threat Register | Threat ID | Category | Component | Disposition | Mitigation Plan | |-----------|----------|-----------|-------------|-----------------| | T-03-07 | Information Disclosure | docker-compose.yml | mitigate | Variables SMTP referencent ${VAR} pas de valeurs hardcodees — .env non commite | | T-03-08 | Information Disclosure | Dockerfile | mitigate | .dockerignore exclut .planning, .git, src, node_modules | - `docker build -t portfolio .` complete sans erreur - `docker run --rm -p 3000:3000 portfolio` sert l'app sur http://localhost:3000 - `curl http://localhost:3000/` retourne du HTML complet SSR - L'image Docker finale est < 300MB (node:22-alpine + .output seulement) - `/formation` retourne 404 (page supprimee per D-19) - Dockerfile utilise node:22-alpine en 2 stages, copie .output/, lance node server/index.mjs (per D-12) - docker-compose port Traefik = 3000 (per D-14) - Variables env SMTP + GA4 passees via docker-compose environment - nuxt-gtag actif uniquement en production (per D-15) - /formation retourne 404 (D-19), legacy src/ et fichiers SPA supprimes - .dockerignore exclut node_modules, .nuxt, .output, src, .git After completion, create `.planning/phases/03-pages-ship/03-04-SUMMARY.md`