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.jslit 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);
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.mdavec tableau Local/Production completREADME.mdmis à 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
*.sqldans.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
allowedOriginPatternscomme 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 fixcurl -vpour tester les headers CORS directementdocker exec <container> env | grep VARpour vérifier les variables injectées
Architecture finale propre¶
- Un seul point de config CORS (
CorsConfig.java) - Séparation AuthController / UserController claire
.gitattributesqui règle les problèmes LF une fois pour toutes.env.example/.env.productionbien 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
¶
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 .gitattributes
✅ Corriger 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."
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.