Aller au contenu

Session 2 — Variabilisation complète & Résolution CORS

Durée : ~10 heures (réparties sur 6 conversations Claude)
Objectif initial : Rendre PerfShop déployable en local sur Docker Desktop Windows
Objectif final : Stack complète fonctionnelle en local ET en production, CORS résolu, documentation mise à jour


🎯 Réalisations

1. Variabilisation complète des URLs (3 couches)

Temps : ~2h
Difficulté : ⭐⭐⭐

Couche 1 — Frontend (env-inject.sh)

  • Script shell qui substitue les URLs dans le bundle JS au démarrage du container
  • 6 fichiers React patchés : api.js, chaos-agent.js, Checkout.jsx, Profile.jsx, MyOrders.jsx, ProductDetail.jsx
  • Variables : PUBLIC_API_URL, PUBLIC_MONITORING_URL

Couche 2 — Monitoring (window.CONFIG)

  • server.js lit les variables d'environnement et les injecte dans le HTML
  • 4 URLs configurables au runtime sans rebuild

Couche 3 — Admin & Chaos Admin (env-inject-nginx.sh)

  • Script équivalent pour les frontends HTML statiques

Fichiers de config

  • .env.example — valeurs locales ports 90xx
  • .env.production — URLs production NAS

2. Résolution ports Windows (80xx → 90xx)

Temps : ~30 min
Difficulté : ⭐⭐

Windows réserve les ports 80xx pour Hyper-V et le système. Tous les ports ont été migrés :

Service Avant Après
Backend API 8080 9080
Frontend 8090 9091
Documentation 8085 9085

3. Correction .gitignore migrations SQL

Temps : ~15 min
Difficulté : ⭐

Le .gitignore contenait *.sql — les 9 migrations Flyway n'étaient jamais commitées. Résultat : Schema-validation: missing table [cart_items] au premier lancement.

sed -i '/^\*\.sql$/d' .gitignore
git add backend/src/main/resources/db/migration/*.sql
git push -uf origin main

4. Résolution conflits Spring Boot controllers

Temps : ~1h
Difficulté : ⭐⭐⭐⭐

AuthController et UserController mappaient tous les deux /api/auth/login et /api/auth/logout. Spring Boot refuse de démarrer avec des routes ambiguës.

Solution architecturale propre : - AuthController → login, logout uniquement - UserController → /me, /status, updateProfile uniquement - Suppression des @CrossOrigin(origins = "*") sur tous les controllers

5. Résolution CORS (le plus complexe)

Temps : ~3h
Difficulté : ⭐⭐⭐⭐⭐

C'est le problème le plus long et le plus tordu de la session.

Chronologie des obstacles

Obstacle 1 : wildcard '*' with credentials mode 'include'
Le frontend envoie credentials: 'include' (sessions HTTP) mais le backend répondait *.
Le navigateur refuse catégoriquement ce combo.

Obstacle 2 : spring.web.cors dans application.yml ignoré
Ajout de CORS_ALLOWED_ORIGINS dans application.yml — Spring ne l'appliquait pas car CorsConfig.java prenait le dessus.

Obstacle 3 : CorsConfig.java ignoré
CorsConfig.java lu ${cors.allowed.origins} (points) mais la variable d'env s'appelait CORS_ALLOWED_ORIGINS (underscores). Spring ne convertit pas automatiquement dans ce cas.

Obstacle 4 : ActuatorCorsConfig.java en doublon
Un deuxième bean WebMvcConfigurer avec allowedOrigins("*") écrasait tout.

Obstacle 5 : WebConfig.java aussi avec CORS
Un troisième addCorsMappings avec * dans WebConfig.

Obstacle 6 : @CrossOrigin(origins = "*") sur les 8 controllers
Chaque controller avait sa propre annotation qui surchargeait la config globale.

Obstacle 7 : allowedOrigins vs allowedOriginPatterns
Spring exige allowedOriginPatterns (pas allowedOrigins) quand allowCredentials=true.
Sinon : IllegalArgumentException: When allowCredentials is true, allowedOrigins cannot contain "*".

Solution finale :

registry.addMapping("/**")
    .allowedOriginPatterns(origins)  // pas allowedOrigins !
    .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
    .allowedHeaders("*")
    .allowCredentials(true)
    .maxAge(3600);
- Suppression ActuatorCorsConfig.java - Suppression bloc CORS dans WebConfig.java - Suppression @CrossOrigin sur tous les controllers - CorsConfig.java seul responsable du CORS

6. Correction fins de ligne Windows (CRLF → LF)

Temps : ~20 min
Difficulté : ⭐⭐

Nginx refusait d'exécuter env-inject.sh avec not found (exit code 127). Cause : Git sur Windows convertit les \n en \r\n → le shebang #!/bin/sh\r est invalide.

Solution définitive : .gitattributes avec *.sh text eol=lf — plus jamais de sed manuel.

7. Documentation complète mise à jour

Temps : ~2h
Difficulté : ⭐⭐

  • 9 fichiers MkDocs mis à jour (ports, URLs, architecture)
  • Section "À classer" supprimée — contenu intégré dans les bonnes catégories
  • Nouvelle section GitLab créée
  • index.md avec tableau Local/Production complet
  • README.md mis à jour

🎢 Chronologie Détaillée

Phase 1 (~4h) : Variabilisation propre 🟢

  • Analyse des URLs en dur dans les 6 fichiers frontend
  • Génération des scripts env-inject
  • Mise en place .env.example / .env.production
  • Ressenti : 🟢 Travail propre, architecture claire

Phase 2 (~6h) : Ports Windows & Migrations 🟡

  • Diagnostic ports 80xx bloqués sur Windows
  • Migration systématique 80xx → 90xx
  • Découverte du *.sql dans .gitignore — volume MySQL à recréer
  • Conflits controllers AuthController/UserController
  • Ressenti : 🟡 Problèmes en cascade, mais chaque fix bien identifié

Phase 3 (~3h) : CORS 🔴

  • Diagnostic Failed to fetch → CORS wildcard + credentials
  • 7 obstacles successifs avant la solution finale
  • Suppression progressive de tous les beans CORS parasites
  • allowedOriginPatterns comme clé finale
  • Ressenti : 🔴 Très frustrant — chaque fix révélait un nouveau problème caché

Phase 4 (~2h) : Documentation 🟢

  • Mise à jour doc MkDocs
  • Suppression section "À classer"
  • Bilan de session
  • Ressenti : 🟢 Satisfaction, tout est propre et documenté

🧠 Moments Clés d'Apprentissage

1. Spring CORS : 4 endroits possibles de configuration

application.yml, CorsConfig.java, WebConfig.java, ActuatorCorsConfig.java — tous peuvent définir des règles CORS qui s'écrasent mutuellement.
Règle : Un seul bean WebMvcConfigurer pour le CORS, supprimer tous les autres.

2. allowedOrigins("*") + allowCredentials(true) = IllegalArgumentException

Spring refuse explicitement cette combinaison depuis Spring 5.3.
Solution : Toujours utiliser allowedOriginPatterns avec credentials.

3. @CrossOrigin sur les controllers prend la priorité

Même avec une config globale parfaite, @CrossOrigin sur chaque controller la surcharge.
Règle : Supprimer toutes les annotations @CrossOrigin si une config globale est en place.

4. Variables d'environnement Spring : underscores ≠ points

${cors.allowed.origins} ne lit pas CORS_ALLOWED_ORIGINS automatiquement dans tous les cas.
Solution : Utiliser directement ${CORS_ALLOWED_ORIGINS} dans @Value.

5. Git sur Windows et CRLF

Sans .gitattributes, tous les scripts shell clonés sur Windows ont des CRLF → Nginx les refuse.
Solution définitive : .gitattributes avec *.sh text eol=lf une fois pour toutes.

6. Flyway et .gitignore

Ne jamais mettre *.sql dans .gitignore si le projet utilise Flyway.
Les checksums des migrations doivent être stables — toute modification d'un fichier déjà appliqué fait crasher le backend.


📊 Métriques de la Session

Métrique Valeur
Durée totale ~10 heures
Conversations Claude 6
Fichiers créés/modifiés ~30
Controllers Java corrigés 8 (suppression @CrossOrigin) + 2 (AuthController, UserController)
Beans CORS supprimés 3 (ActuatorCorsConfig, WebConfig.cors, application.yml.cors)
Migrations SQL ajoutées 9 (V1 à V10)
Ports migrés 3 (8080→9080, 8090→9091, 8085→9085)
Pages MkDocs mises à jour 9
Obstacles CORS successifs 7
Bugs corrigés ~12

🎯 Difficultés Rencontrées

🔴 Très Difficile (⭐⭐⭐⭐⭐)

Problème : CORS — 7 obstacles en cascade
Temps perdu : ~3h
Solution : allowedOriginPatterns + suppression de tous les beans CORS parasites
Leçon : En Spring, le CORS doit être configuré en un seul endroit. Avoir plusieurs beans qui définissent des règles CORS crée des conflits imprévisibles.

🟡 Difficile (⭐⭐⭐⭐)

Problème : Conflits controllers login/logout en doublon
Temps perdu : ~1h
Solution : Séparation claire des responsabilités AuthController / UserController
Leçon : Toujours vérifier les routes ambiguës avant de supprimer un controller — Spring ne démarre pas si deux méthodes mappent la même route.

🟢 Moyen (⭐⭐⭐)

Problème : *.sql dans .gitignore — migrations jamais commitées
Temps perdu : ~30 min (diagnostic + force push)
Solution : sed -i '/^\*\.sql$/d' .gitignore + git push -uf origin main
Leçon : Vérifier le .gitignore avant le premier déploiement sur une nouvelle machine.


✅ Ce Qui a Bien Fonctionné

Diagnostic méthodique

  • docker logs <container> systématiquement avant chaque fix
  • curl -v pour tester les headers CORS directement
  • docker exec <container> env | grep VAR pour vérifier les variables injectées

Architecture finale propre

  • Un seul point de config CORS (CorsConfig.java)
  • Séparation AuthController / UserController claire
  • .gitattributes qui règle les problèmes LF une fois pour toutes
  • .env.example / .env.production bien séparés

Commande de démarrage en 4 lignes

git clone https://perfshop-gitlab.perfshop.io/perf/perfshop.git
cd perfshop
cp .env.example .env
docker compose -f docker-compose.build.yml up -d --build

🎓 Conclusion

Cette session de ~10 heures en vibe coding a permis de :

Rendre PerfShop déployable en local — variabilisation complète sur 3 couches (frontend, monitoring, admin) ✅ Résoudre CORS — 7 obstacles successifs, solution finale avec allowedOriginPatterns + un seul bean de config ✅ Fixer la compatibilité Windows — ports 80xx → 90xx, CRLF → LF via .gitattributesCorriger le .gitignore — migrations Flyway enfin commitées ✅ Documenter l'architecture — 9 pages MkDocs mises à jour, README complet

Ratio temps productif / temps total : ~70% Objectifs atteints : 100% (stack locale fonctionnelle, déployable en 4 commandes)

Le vibe coding a particulièrement souffert sur le CORS : 7 obstacles en cascade parce que la config était dispersée en 4 endroits différents. La leçon structurelle : toujours commencer par un grep -r CORS src/ avant de toucher quoi que ce soit — une minute d'audit pour économiser 2 heures de debugging.

La leçon centrale de cette session : dans un projet Spring Boot réel, la config ne vit jamais à un seul endroit. Toujours cartographier avant de modifier.


🤖 Erreurs imputables à Claude AI — Session 2


❌ Erreur S2-1 — Traitement CORS obstacle par obstacle sans audit global

Ce que j'ai proposé : Corriger chaque erreur CORS au fur et à mesure qu'elle apparaissait — d'abord fixer le wildcard, puis application.yml, puis CorsConfig.java, puis ActuatorCorsConfig.java, etc.

Ce qui s'est passé : 7 obstacles successifs sur ~3 heures. Chaque fix résolvait un symptôme mais révélait le suivant, car plusieurs beans CORS coexistaient et se court-circuitaient.

Pourquoi c'était une erreur : Face à un problème de configuration Spring CORS, la bonne démarche est un audit global en premier — recenser tous les endroits où du CORS est configuré avant de toucher quoi que ce soit. J'aurais dû commencer par : "Montrez-moi tous les fichiers qui mentionnent CORS dans ce projet."

grep -r "CorsMapping\|CrossOrigin\|cors" src/

Temps perdu : ~2h | Sévérité : ⭐⭐⭐⭐⭐


❌ Erreur S2-2 — allowedOrigins avec allowCredentials(true)

Ce que j'ai proposé : allowedOrigins(origins) combiné à allowCredentials(true).

Ce qui s'est passé : Spring lève une IllegalArgumentException au démarrage — contrainte explicite depuis Spring 5.3.

Pourquoi c'était une erreur : La règle allowedOriginPatterns obligatoire avec allowCredentials=true est documentée depuis 2020. Règle mnémotechnique : credentials=true → toujours allowedOriginPatterns, jamais allowedOrigins.

Temps perdu : ~20 min | Sévérité : ⭐⭐⭐


❌ Erreur S2-3 — ${cors.allowed.origins} au lieu de ${CORS_ALLOWED_ORIGINS}

Ce que j'ai proposé : @Value("${cors.allowed.origins}") dans CorsConfig.java.

Ce qui s'est passé : La variable d'env Docker CORS_ALLOWED_ORIGINS (underscores) n'était pas lue. Spring Relaxed Binding ne convertit pas automatiquement dans ce cas avec @Value.

Pourquoi c'était une erreur : Toujours utiliser le nom exact de la variable d'env dans @Value. Le Relaxed Binding n'est fiable qu'avec @ConfigurationProperties.

Temps perdu : ~15 min | Sévérité : ⭐⭐


📊 Récapitulatif Session 2

# Erreur Temps perdu Sévérité
S2-1 CORS fix symptôme par symptôme sans audit global ~2h ⭐⭐⭐⭐⭐
S2-2 allowedOrigins + allowCredentials=true ~20 min ⭐⭐⭐
S2-3 ${cors.allowed.origins} au lieu de ${CORS_ALLOWED_ORIGINS} ~15 min ⭐⭐
TOTAL ~2h35

Pattern : Fix symptomatique sans inventaire global — répondre aux erreurs une par une comme un médecin qui traite les symptômes sans lire le dossier patient.