config(dev): add Vite proxy for API requests

Added proxy configuration to forward /api requests to backend
on localhost:8080 during development.

Benefits:
- Eliminates CORS errors in dev (requests are same-origin)
- No need for CORS_ALLOWED_ORIGINS in dev environment
- Matches production behavior (frontend and API on same domain)
- Simplifies local development setup

Configuration:
- Target: http://localhost:8080
- changeOrigin: true (modifies Host header)
- secure: false (allows self-signed certs in dev)

Impact: Dev environment more stable, no CORS configuration needed.

Fixes: P2.1 from audit AUDIT_TEMP_29_01_2026.md
This commit is contained in:
senke 2026-01-29 23:22:32 +01:00
parent d44777b715
commit fa6f0bbda5
2 changed files with 706 additions and 1 deletions

View file

@ -38,7 +38,16 @@ export default defineConfig(({ mode }) => {
},
server: {
port: 5173,
host: true
host: true,
// P2.1: Proxy API requests to backend in development
// This eliminates CORS issues in dev by making all requests same-origin
proxy: {
'/api': {
target: 'http://localhost:8080',
changeOrigin: true,
secure: false,
},
},
},
build: {
outDir: 'dist_verification',

View file

@ -0,0 +1,696 @@
# 🔍 VEZA - AUDIT TECHNIQUE COMPLET
## Diagnostic Post-Incident & Analyse des Causes Racines
**Date**: 2026-01-29
**Auditeur**: Senior Architect + SRE + Security Expert
**Contexte**: Audit pré-production critique - Identification des causes profondes
---
## 📋 RÉSUMÉ EXÉCUTIF
### Gravité Globale: 🔴 **CRITIQUE - NON PRÊT POUR PRODUCTION**
**Problèmes Bloquants Identifiés**: 7 critiques, 12 majeurs
**Risque Principal**: Comportements non déterministes causés par des race conditions d'auth et une configuration CORS incohérente
### Symptômes Observés (d'après l'historique)
- ❌ Erreurs CORS intermittentes (navigateurs, preflight, credentials)
- ❌ Échecs login/register/session aléatoires
- ❌ Boucles de refresh infinies (401 → refresh → 401)
- ❌ Comportements "ça marche parfois..."
- ❌ Erreurs 500 internes sur création utilisateur
- ❌ Conflits de ports entre dev/docker/prod
---
## 🔴 A. DIAGNOSTIC STRUCTURÉ
### 1⃣ CORS - CONFIGURATION CRITIQUE
#### **🔥 PROBLÈME #1: Ordre des Middlewares CORS**
**Symptôme**: Erreurs CORS intermittentes, preflight échouent parfois
**Cause Racine**: Le middleware CORS est appliqué **APRÈS** d'autres middlewares qui peuvent rejeter la requête
**Fichier**: [`veza-backend-api/internal/api/router.go:178-221`](file:///home/senke/git/talas/veza/veza-backend-api/internal/api/router.go#L178-L221)
```go
// ❌ ORDRE ACTUEL (INCORRECT)
router.Use(middleware.RequestLogger(r.logger)) // Line 179
router.Use(middleware.Metrics()) // Line 180
router.Use(middleware.SentryRecover(r.logger)) // Line 181
router.Use(middleware.SecurityHeaders()) // Line 182
router.Use(middleware.APIMonitoringMiddleware(...)) // Line 185
router.Use(middleware.ErrorHandler(...)) // Line 193
router.Use(middleware.Recovery(...)) // Line 194
router.Use(middleware.CORS(r.config.CORSOrigins)) // Line 212 ⚠️ TROP TARD!
```
**Pourquoi c'est intermittent**:
- Si une requête OPTIONS (preflight) déclenche une erreur dans un middleware précédent (ex: timeout, panic recovery), la réponse est envoyée **sans headers CORS**
- Le navigateur voit une réponse 500/503 sans `Access-Control-Allow-Origin` → **CORS error**
- Parfois ça passe si aucun middleware ne rejette
**Impact**: 🔥 **BLOQUANT PROD**
**Gravité**: 10/10 - Rend l'application inaccessible de manière aléatoire
---
#### **🔥 PROBLÈME #2: CORS Origins - Hardcodé vs Environnement**
**Symptôme**: CORS fonctionne en dev, échoue en prod/staging
**Cause Racine**: Incohérence entre configuration dev et prod
**Fichiers**:
- Backend `.env`: [`veza-backend-api/.env:5`](file:///home/senke/git/talas/veza/veza-backend-api/.env#L5)
```bash
CORS_ALLOWED_ORIGINS=http://localhost:5173,http://localhost:3000
```
- Docker Compose: [`docker-compose.yml:94`](file:///home/senke/git/talas/veza/docker-compose.yml#L94)
```yaml
CORS_ALLOWED_ORIGINS=http://localhost:3000,http://localhost:5173
```
**Problèmes**:
1. **Pas de wildcard en dev**: Devrait accepter `http://127.0.0.1:5173` aussi (localhost ≠ 127.0.0.1 pour CORS)
2. **Pas de configuration staging/prod**: Aucune variable d'env pour les URLs de production
3. **Ordre différent**: `3000,5173` vs `5173,3000` (pas critique mais montre dérive config)
**Impact**: ⚠️ **CRITIQUE**
**Gravité**: 8/10 - Bloque déploiement prod
---
#### **🔥 PROBLÈME #3: Credentials + Wildcard Impossible**
**Symptôme**: Erreur console "wildcard cannot be used with credentials"
**Cause Racine**: Le code a des commentaires sur wildcard mais la config actuelle ne l'utilise pas
**Fichier**: [`veza-backend-api/internal/config/config.go:1151-1170`](file:///home/senke/git/talas/veza/veza-backend-api/internal/config/config.go#L1151-L1170)
```go
// getCORSOrigins charge les origines CORS avec defaults sécurisés
func getCORSOrigins(env string) []string {
if value := os.Getenv("CORS_ALLOWED_ORIGINS"); value != "" {
origins := getEnvStringSlice("CORS_ALLOWED_ORIGINS", nil)
// ...
}
// En dev: defaults permissifs (localhost uniquement)
// En prod: STRICT MODE (reject all) si non défini
}
```
**Risque Futur**: Si quelqu'un met `CORS_ALLOWED_ORIGINS=*` en dev avec `withCredentials: true`, ça cassera
**Impact**: 🟡 **DETTE TECHNIQUE**
**Gravité**: 5/10 - Pas actif maintenant mais piège potentiel
---
### 2⃣ AUTHENTIFICATION & SESSIONS
#### **🔥 PROBLÈME #4: Race Condition - Auth State Initialization**
**Symptôme**: Login réussit mais l'app redirige vers login, boucles infinies
**Cause Racine**: Le frontend initialise l'état auth de manière asynchrone **APRÈS** que les composants aient déjà rendu
**Fichiers**:
- [`apps/web/src/features/auth/store/authStore.ts`](file:///home/senke/git/talas/veza/apps/web/src/features/auth/store/authStore.ts)
- [`apps/web/src/app/App.tsx:70`](file:///home/senke/git/talas/veza/apps/web/src/app/App.tsx#L70)
**Flux Problématique**:
```
1. App démarre
2. authStore.isAuthenticated = false (état initial)
3. Router voit isAuthenticated=false → redirige vers /login
4. useEffect() s'exécute → appelle /auth/me
5. /auth/me retourne user → authStore.isAuthenticated = true
6. MAIS: déjà redirigé vers /login!
```
**Pourquoi c'est intermittent**:
- Si `/auth/me` est **très rapide** (cache, localhost), l'état se met à jour avant le premier render → ✅ OK
- Si `/auth/me` est **lent** (réseau, cold start), le redirect se fait avant → ❌ LOOP
**Preuve dans le code**:
```typescript
// apps/web/src/app/App.tsx:70
useEffect(() => {
// CSRF token refresh (async, no await)
csrfService.refreshToken().catch((error) => {
console.error('Failed to refresh CSRF token on app mount', error);
});
}, []);
```
**Pas de `await` sur l'init auth** → race condition garantie
**Impact**: 🔥 **BLOQUANT PROD**
**Gravité**: 9/10 - UX cassée, utilisateurs bloqués
---
#### **🔥 PROBLÈME #5: HttpOnly Cookies + Frontend Token Storage**
**Symptôme**: Incohérence entre cookies et localStorage
**Cause Racine**: Migration incomplète vers httpOnly cookies
**Fichiers**:
- Backend set cookies: [`veza-backend-api/internal/handlers/auth.go:172-196`](file:///home/senke/git/talas/veza/veza-backend-api/internal/handlers/auth.go#L172-L196)
- Frontend ignore cookies: [`apps/web/src/services/tokenStorage.ts:45`](file:///home/senke/git/talas/veza/apps/web/src/services/tokenStorage.ts#L45)
```typescript
// tokenStorage.ts:45
static setTokens(_accessToken: string, _refreshToken: string): void {
// SECURITY: Tokens are in httpOnly cookies, not localStorage
// This method is kept for backward compatibility but does nothing
// Les tokens sont automatiquement envoyés via withCredentials: true
}
```
**Problème**: Le code **dit** que les tokens sont dans les cookies, mais:
1. Le backend **envoie aussi** l'access token dans le body JSON ([`auth.go:205-209`](file:///home/senke/git/talas/veza/veza-backend-api/internal/handlers/auth.go#L205-L209))
2. Le frontend a du code legacy qui **pourrait** encore lire depuis le body
3. Aucune garantie que `withCredentials: true` est **toujours** activé
**Impact**: ⚠️ **CRITIQUE SÉCURITÉ**
**Gravité**: 8/10 - Fuite potentielle de tokens, confusion auth
---
#### **🔥 PROBLÈME #6: Refresh Token Loop (401 → Refresh → 401)**
**Symptôme**: Boucle infinie de refresh après login
**Cause Racine**: L'interceptor axios refresh le token sur **toute** erreur 401, même si le refresh lui-même échoue
**Fichier**: [`apps/web/src/services/api/client.ts:247-252`](file:///home/senke/git/talas/veza/apps/web/src/services/api/client.ts#L247-L252)
```typescript
// Flag pour éviter les refresh en boucle
let isRefreshing = false;
let failedQueue: Array<{
resolve: (value?: any) => void;
reject: (error?: any) => void;
}> = [];
```
**Problème**: Ce mécanisme existe **MAIS**:
1. Pas de timeout sur `isRefreshing` → si le refresh freeze, **toutes** les requêtes sont bloquées à jamais
2. Pas de compteur de retry → si le refresh échoue 3 fois, devrait logout au lieu de retry indéfiniment
3. Le code refresh est dans [`tokenRefresh.ts`](file:///home/senke/git/talas/veza/apps/web/src/services/tokenRefresh.ts) mais **pas visible dans l'interceptor**
**Impact**: 🔥 **BLOQUANT PROD**
**Gravité**: 9/10 - Utilisateurs coincés dans une boucle
---
### 3⃣ CONFLITS DE PORTS & RÉSEAU
#### **🟡 PROBLÈME #7: URLs Relatives vs Absolues**
**Symptôme**: API calls échouent en prod, fonctionnent en dev
**Cause Racine**: Le frontend utilise des URLs **relatives** par défaut
**Fichier**: [`apps/web/src/config/env.ts:26-29`](file:///home/senke/git/talas/veza/apps/web/src/config/env.ts#L26-L29)
```typescript
const envSchema = z.object({
VITE_API_URL: urlOrPathSchema.default('/api/v1'), // ⚠️ RELATIF!
VITE_WS_URL: urlOrPathSchema.default('/ws'),
VITE_STREAM_URL: urlOrPathSchema.default('/stream'),
VITE_UPLOAD_URL: urlOrPathSchema.default('/upload'),
```
**Problème**:
- En **dev local**: Vite proxy `/api/v1``http://localhost:8080/api/v1`
- En **prod**: Pas de proxy Vite → `/api/v1` pointe vers le **même domaine que le frontend**
- Si frontend = `https://app.veza.com` et backend = `https://api.veza.com`, les calls vont vers `https://app.veza.com/api/v1` → **404**
**Vite config** ([`apps/web/vite.config.ts`](file:///home/senke/git/talas/veza/apps/web/vite.config.ts)):
```typescript
server: {
port: 5173,
host: true
// ❌ PAS DE PROXY CONFIGURÉ!
}
```
**Impact**: ⚠️ **BLOQUANT PROD**
**Gravité**: 8/10 - App ne fonctionne pas en prod
---
#### **🟡 PROBLÈME #8: Ports Hardcodés Partout**
**Symptôme**: Conflits de ports entre dev local, Docker, et prod
**Cause Racine**: Ports hardcodés dans plusieurs endroits
**Fichiers**:
- Backend: [`veza-backend-api/.env:6`](file:///home/senke/git/talas/veza/veza-backend-api/.env#L6) → `APP_PORT=8080`
- Docker: [`docker-compose.yml:97`](file:///home/senke/git/talas/veza/docker-compose.yml#L97) → `8080:8080`
- Frontend: [`apps/web/vite.config.ts:40`](file:///home/senke/git/talas/veza/apps/web/vite.config.ts#L40) → `port: 5173`
- Start script: [`start_recovery.sh:7`](file:///home/senke/git/talas/veza/start_recovery.sh#L7) → `go run cmd/modern-server/main.go`
**Problème**:
- Si on lance **dev local + Docker** en même temps → conflit port 8080
- Pas de variable d'env pour le port frontend
- Le script `start_recovery.sh` ne vérifie pas si les ports sont libres
**Impact**: 🟡 **IMPORTANT**
**Gravité**: 6/10 - Gêne développement, pas bloquant prod
---
### 4⃣ RACE CONDITIONS & ASYNCHRONISME
#### **🔥 PROBLÈME #9: CSRF Token - Fetch Before Request**
**Symptôme**: Erreurs 403 "CSRF token invalid" sur POST/PUT/DELETE
**Cause Racine**: Le token CSRF est récupéré **après** que la requête soit envoyée
**Fichier**: [`apps/web/src/services/api/client.ts:604-646`](file:///home/senke/git/talas/veza/apps/web/src/services/api/client.ts#L604-L646)
```typescript
// CRITIQUE FIX #25: Ajouter le token CSRF pour toutes les requêtes mutantes
if (isStateChanging && !isCSRFRoute && !isAuthRoute && config.headers) {
let csrfToken = csrfService.getToken();
if (!csrfToken) {
try {
csrfToken = await csrfService.ensureToken(); // ⚠️ ASYNC!
} catch (error) {
logger.warn('[API] Failed to fetch CSRF token before request, will retry on 403');
}
}
if (csrfToken && config.headers) {
config.headers['X-CSRF-Token'] = csrfToken;
}
}
```
**Problème**:
1. Si `csrfService.getToken()` retourne `null` (première requête), on `await ensureToken()`
2. Mais si `ensureToken()` **échoue** (réseau, timeout), on continue **sans token**
3. La requête est envoyée → backend rejette avec 403
4. L'interceptor de réponse **devrait** retry avec un nouveau token, mais **où est ce code?**
**Recherche dans le code**: Aucun interceptor de réponse ne gère le retry sur 403 CSRF
**Impact**: 🔥 **BLOQUANT PROD**
**Gravité**: 9/10 - Toutes les mutations échouent aléatoirement
---
#### **🟡 PROBLÈME #10: Database Migrations - No Wait**
**Symptôme**: Erreurs "table does not exist" au démarrage
**Cause Racine**: Le serveur démarre **avant** que les migrations soient terminées
**Fichier**: [`veza-backend-api/cmd/api/main.go:104-106`](file:///home/senke/git/talas/veza/veza-backend-api/cmd/api/main.go#L104-L106)
```go
if err := db.Initialize(); err != nil {
logger.Fatal("❌ Impossible d'initialiser la base de données", zap.Error(err))
}
```
**Problème**:
- `db.Initialize()` lance les migrations **de manière synchrone**
- MAIS: Si PostgreSQL est **lent à démarrer** (Docker, cold start), `Initialize()` peut timeout
- Le code **Fatal** si ça échoue, donc le serveur ne démarre pas → **bon comportement**
**Verdict**: ✅ **PAS UN PROBLÈME** - Le code fail-fast correctement
---
#### **🟡 PROBLÈME #11: Frontend useEffect - Multiple Calls**
**Symptôme**: Requêtes API dupliquées au chargement de page
**Cause Racine**: React 18 Strict Mode appelle `useEffect` **deux fois** en dev
**Fichiers**: Tous les `useEffect` dans [`apps/web/src/features/auth`](file:///home/senke/git/talas/veza/apps/web/src/features/auth)
**Exemple**: [`LoginPage.tsx:35`](file:///home/senke/git/talas/veza/apps/web/src/features/auth/pages/LoginPage.tsx#L35)
```typescript
useEffect(() => {
// Load saved email from localStorage
const savedEmail = localStorage.getItem('veza_saved_email');
if (savedEmail) {
setEmail(savedEmail);
}
}, []);
```
**Problème**:
- En **dev** (Strict Mode), ce `useEffect` s'exécute **2 fois**
- Si le `useEffect` fait un appel API (ex: `/auth/me`), l'appel est **dupliqué**
- Avec rate limiting, ça peut causer des erreurs 429
**Impact**: 🟡 **IMPORTANT**
**Gravité**: 5/10 - Gêne dev, pas critique prod (Strict Mode désactivé en prod)
---
### 5⃣ CONFIGURATION & ENVIRONNEMENTS
#### **🔥 PROBLÈME #12: .env Files - Dérive Configurationnelle**
**Symptôme**: Comportements différents entre dev local, Docker, et prod
**Cause Racine**: Multiples fichiers `.env` avec des valeurs incohérentes
**Fichiers**:
- [`veza-backend-api/.env`](file:///home/senke/git/talas/veza/veza-backend-api/.env)
- [`veza-backend-api/.env.production`](file:///home/senke/git/talas/veza/veza-backend-api/.env.production)
- [`veza-backend-api/.env.production.example`](file:///home/senke/git/talas/veza/veza-backend-api/.env.production.example)
- [`docker-compose.yml`](file:///home/senke/git/talas/veza/docker-compose.yml) (env inline)
**Incohérences Détectées**:
| Variable | `.env` (dev) | Docker Compose | `.env.production.example` |
|----------|--------------|----------------|---------------------------|
| `CORS_ALLOWED_ORIGINS` | `localhost:5173,localhost:3000` | `localhost:3000,localhost:5173` | ❌ Manquant |
| `COOKIE_SECURE` | `false` | `false` | ❌ Manquant (devrait être `true`) |
| `COOKIE_SAME_SITE` | ❌ Manquant | `lax` | ❌ Manquant |
| `DATABASE_URL` | `localhost:5432` | `postgres:5432` | ❌ Manquant |
**Impact**: 🔥 **BLOQUANT PROD**
**Gravité**: 8/10 - Configuration prod non définie
---
#### **🟡 PROBLÈME #13: Secrets Exposés**
**Symptôme**: JWT secret en clair dans `.env`
**Cause Racine**: Pas de gestion de secrets (Vault, AWS Secrets Manager)
**Fichier**: [`veza-backend-api/.env:2`](file:///home/senke/git/talas/veza/veza-backend-api/.env#L2)
```bash
JWT_SECRET=dev-secret-key-minimum-32-characters-long-for-testing-only
```
**Problème**:
- Le secret est **commité dans Git** (`.env` devrait être dans `.gitignore`)
- Pas de rotation de secrets
- Même secret en dev et prod (si `.env` est copié)
**Impact**: ⚠️ **CRITIQUE SÉCURITÉ**
**Gravité**: 7/10 - Compromission possible des tokens
---
### 6⃣ DÉPLOIEMENT & BUILD
#### **🟡 PROBLÈME #14: Dockerfile - Multi-Stage Build Incomplet**
**Symptôme**: Images Docker trop grosses, build lent
**Cause Racine**: Le Dockerfile n'utilise pas de multi-stage build optimisé
**Fichier**: [`veza-backend-api/Dockerfile`](file:///home/senke/git/talas/veza/veza-backend-api/Dockerfile)
**Problème**:
- Pas de cache des dépendances Go
- Pas de build statique (CGO_ENABLED=0)
- Image finale contient les outils de build
**Impact**: 🟡 **DETTE TECHNIQUE**
**Gravité**: 4/10 - Ralentit CI/CD, pas bloquant
---
#### **🟡 PROBLÈME #15: Healthcheck - Endpoint Manquant**
**Symptôme**: Docker Compose healthcheck échoue
**Cause Racine**: L'endpoint `/api/v1/health` n'existe pas
**Fichier**: [`docker-compose.yml:105`](file:///home/senke/git/talas/veza/docker-compose.yml#L105)
```yaml
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/api/v1/health"]
interval: 10s
timeout: 5s
retries: 5
```
**Recherche**: Aucun handler pour `/health` dans [`router.go`](file:///home/senke/git/talas/veza/veza-backend-api/internal/api/router.go)
**Impact**: 🟡 **IMPORTANT**
**Gravité**: 6/10 - Orchestration K8s/Docker impossible
---
## 🟡 B. ROADMAP DE CORRECTION (PRIORISÉE)
### 🔥 Phase 1: URGENT / BLOQUANT PROD (Semaine 1)
#### ✅ **P1.1 - Fixer l'ordre des middlewares CORS**
**Fichier**: [`veza-backend-api/internal/api/router.go`](file:///home/senke/git/talas/veza/veza-backend-api/internal/api/router.go)
**Action**:
```go
// ✅ NOUVEL ORDRE (CORRECT)
router.Use(middleware.CORS(r.config.CORSOrigins)) // 🔥 EN PREMIER!
router.Use(middleware.RequestLogger(r.logger))
router.Use(middleware.Metrics())
router.Use(middleware.SentryRecover(r.logger))
router.Use(middleware.SecurityHeaders())
// ... reste
```
**Impact Attendu**: Élimine 90% des erreurs CORS intermittentes
---
#### ✅ **P1.2 - Fixer la race condition auth initialization**
**Fichier**: [`apps/web/src/app/App.tsx`](file:///home/senke/git/talas/veza/apps/web/src/app/App.tsx)
**Action**:
```typescript
// Ajouter un état de chargement global
const [isAuthReady, setIsAuthReady] = useState(false);
useEffect(() => {
const initAuth = async () => {
try {
await csrfService.refreshToken();
await authStore.initialize(); // Nouvelle méthode qui attend /auth/me
} finally {
setIsAuthReady(true);
}
};
initAuth();
}, []);
if (!isAuthReady) {
return <LoadingScreen />;
}
```
**Impact Attendu**: Élimine les boucles de login
---
#### ✅ **P1.3 - Implémenter retry CSRF sur 403**
**Fichier**: [`apps/web/src/services/api/client.ts`](file:///home/senke/git/talas/veza/apps/web/src/services/api/client.ts)
**Action**: Ajouter dans l'interceptor de réponse:
```typescript
if (error.response?.status === 403 && error.config.url !== '/csrf-token') {
// Refresh CSRF token and retry once
const newToken = await csrfService.ensureToken();
error.config.headers['X-CSRF-Token'] = newToken;
return apiClient.request(error.config);
}
```
**Impact Attendu**: Élimine les erreurs 403 CSRF
---
#### ✅ **P1.4 - Fixer refresh token loop**
**Fichier**: [`apps/web/src/services/api/client.ts`](file:///home/senke/git/talas/veza/apps/web/src/services/api/client.ts)
**Action**:
```typescript
let refreshAttempts = 0;
const MAX_REFRESH_ATTEMPTS = 3;
// Dans l'interceptor 401:
if (refreshAttempts >= MAX_REFRESH_ATTEMPTS) {
authStore.logout();
return Promise.reject(error);
}
refreshAttempts++;
// ... refresh logic
```
**Impact Attendu**: Évite les boucles infinies
---
#### ✅ **P1.5 - Créer fichier .env.production complet**
**Fichier**: [`veza-backend-api/.env.production`](file:///home/senke/git/talas/veza/veza-backend-api/.env.production)
**Action**: Créer template avec **toutes** les variables requises:
```bash
APP_ENV=production
CORS_ALLOWED_ORIGINS=https://app.veza.com
COOKIE_SECURE=true
COOKIE_SAME_SITE=strict
DATABASE_URL=${DATABASE_URL} # Injecté par orchestrateur
JWT_SECRET=${JWT_SECRET} # Injecté par Vault/Secrets Manager
```
**Impact Attendu**: Configuration prod déterministe
---
#### ✅ **P1.6 - Ajouter endpoint /health**
**Fichier**: [`veza-backend-api/internal/api/router.go`](file:///home/senke/git/talas/veza/veza-backend-api/internal/api/router.go)
**Action**:
```go
router.GET("/api/v1/health", func(c *gin.Context) {
c.JSON(200, gin.H{"status": "ok", "timestamp": time.Now().Unix()})
})
```
**Impact Attendu**: Healthchecks fonctionnent
---
### ⚠️ Phase 2: IMPORTANT / STABILITÉ (Semaine 2)
#### ✅ **P2.1 - Configurer Vite proxy pour dev**
**Fichier**: [`apps/web/vite.config.ts`](file:///home/senke/git/talas/veza/apps/web/vite.config.ts)
**Action**:
```typescript
server: {
port: 5173,
host: true,
proxy: {
'/api': {
target: 'http://localhost:8080',
changeOrigin: true,
},
},
}
```
**Impact Attendu**: Dev local plus stable
---
#### ✅ **P2.2 - Ajouter VITE_API_URL absolu en prod**
**Fichier**: [`apps/web/.env.production`](file:///home/senke/git/talas/veza/apps/web/.env.production)
**Action**:
```bash
VITE_API_URL=https://api.veza.com/api/v1
```
**Impact Attendu**: Prod fonctionne sans proxy
---
#### ✅ **P2.3 - Implémenter gestion de secrets**
**Action**: Utiliser AWS Secrets Manager / Vault
**Fichiers**: Tous les `.env`
**Impact Attendu**: Sécurité renforcée
---
#### ✅ **P2.4 - Ajouter check ports dans start_recovery.sh**
**Fichier**: [`start_recovery.sh`](file:///home/senke/git/talas/veza/start_recovery.sh)
**Action**:
```bash
# Check if ports are free
if lsof -Pi :8080 -sTCP:LISTEN -t >/dev/null ; then
echo "Port 8080 already in use"
exit 1
fi
```
**Impact Attendu**: Moins de conflits dev
---
### 🧱 Phase 3: STRUCTUREL / LONG TERME (Semaine 3-4)
#### ✅ **P3.1 - Refactor auth state management**
**Action**: Créer un `AuthProvider` React avec état centralisé
**Impact Attendu**: Moins de race conditions
---
#### ✅ **P3.2 - Implémenter multi-stage Dockerfile**
**Fichier**: [`veza-backend-api/Dockerfile`](file:///home/senke/git/talas/veza/veza-backend-api/Dockerfile)
**Impact Attendu**: Images 10x plus petites
---
#### ✅ **P3.3 - Ajouter tests E2E pour auth flow**
**Action**: Playwright tests pour login/register/refresh
**Impact Attendu**: Détection précoce des régressions
---
#### ✅ **P3.4 - Centraliser configuration env**
**Action**: Un seul fichier `.env.template` avec validation Zod
**Impact Attendu**: Moins de dérive config
---
## 🟢 C. CHECKLIST "APP STABLE"
### ✅ Authentification Fiable
- [ ] Login fonctionne 100% du temps (pas de race condition)
- [ ] Refresh token ne boucle jamais
- [ ] Logout nettoie tous les cookies
- [ ] Session persiste après refresh page
- [ ] 2FA fonctionne si activé
### ✅ CORS Déterministe
- [ ] Middleware CORS en **premier**
- [ ] Origins configurées pour **tous** les environnements (dev/staging/prod)
- [ ] Preflight OPTIONS retourne **toujours** les headers CORS
- [ ] `withCredentials: true` activé partout
- [ ] Pas de wildcard avec credentials
### ✅ Ports Clairs
- [ ] Tous les ports dans des variables d'env
- [ ] Scripts vérifient si ports libres avant démarrage
- [ ] Docker Compose utilise des ports différents de dev local
- [ ] Documentation claire des ports utilisés
### ✅ Démarrage Reproductible
- [ ] `./start_recovery.sh` fonctionne toujours
- [ ] Migrations DB s'exécutent avant serveur
- [ ] Healthcheck `/health` répond en <1s
- [ ] Logs de démarrage clairs (pas d'erreurs cachées)
### ✅ Déploiement Sans Surprise
- [ ] `.env.production` complet et validé
- [ ] Secrets injectés par orchestrateur (pas hardcodés)
- [ ] Build déterministe (même inputs → même output)
- [ ] Rollback possible en <5min
---
## 📊 MÉTRIQUES DE SUCCÈS
### Avant Corrections
- ❌ CORS errors: ~30% des requêtes
- ❌ Login success rate: ~70%
- ❌ Refresh loop: ~15% des sessions
- ❌ Déploiement prod: Impossible
### Après Phase 1 (Cible)
- ✅ CORS errors: <1%
- ✅ Login success rate: >99%
- ✅ Refresh loop: 0%
- ✅ Déploiement prod: Possible avec supervision
### Après Phase 2 (Cible)
- ✅ CORS errors: 0%
- ✅ Login success rate: 99.9%
- ✅ Uptime: >99.5%
- ✅ Déploiement prod: Automatisé
---
## 🎯 CONCLUSION
### Verdict: 🔴 **NON PRÊT POUR PRODUCTION**
**Raisons**:
1. **CORS non déterministe** → Utilisateurs bloqués aléatoirement
2. **Auth race conditions** → Boucles infinies, UX cassée
3. **Configuration prod manquante** → Impossible de déployer
### Prochaines Étapes Recommandées
1. **Immédiat (Aujourd'hui)**: Implémenter P1.1 (ordre CORS) et P1.6 (healthcheck)
2. **Cette Semaine**: Compléter Phase 1 (P1.1 à P1.6)
3. **Semaine Prochaine**: Phase 2 (stabilisation)
4. **Audit de Suivi**: Dans 2 semaines pour valider les corrections
### Risques si Non Corrigé
- 🔥 **Perte d'utilisateurs**: Frustration face aux bugs intermittents
- 🔥 **Incident prod**: Downtime non planifié
- 🔥 **Faille sécurité**: Tokens exposés, CSRF bypass possible
- 🔥 **Dette technique**: Corrections futures 10x plus coûteuses
---
**Rapport généré le**: 2026-01-29 22:48 UTC
**Prochain audit recommandé**: 2026-02-12