927 lines
27 KiB
Markdown
927 lines
27 KiB
Markdown
# 🎯 Veza MVP Stability — Todolist de Suivi
|
|
|
|
> **Objectif** : Atteindre un état stable pour le déploiement production
|
|
> **Score actuel** : 4/10 → **Score cible** : 8/10
|
|
> **Effort estimé** : 8-12 jours
|
|
|
|
---
|
|
|
|
## 📊 Dashboard de Progression
|
|
|
|
| Métrique | Valeur |
|
|
|----------|--------|
|
|
| **Tâches complétées** | 7 / 15 |
|
|
| **Phase actuelle** | PHASE-2 (API Alignment) |
|
|
| **Progression globale** | ███████░░░ 47% |
|
|
| **Dernière mise à jour** | 2025-01-27 20:00 |
|
|
|
|
### Progression par Phase
|
|
|
|
| Phase | Statut | Progression |
|
|
|-------|--------|-------------|
|
|
| PHASE-1 — Bloquants Critiques | ✅ Terminé | 5/5 |
|
|
| PHASE-2 — Alignement API | 🔄 En cours | 2/5 |
|
|
| PHASE-3 — Fiabilité | ⚪ En attente | 0/5 |
|
|
|
|
---
|
|
|
|
## 🚨 PHASE-1 : Bloquants Critiques
|
|
|
|
> **Priorité** : CRITIQUE — Sans ces fixes, l'app ne fonctionne pas en production
|
|
> **Effort** : 3-4 jours
|
|
|
|
---
|
|
|
|
### MVP-001 — Fix CORS Production Configuration
|
|
|
|
| | |
|
|
|---|---|
|
|
| **Source** | INT-000001 |
|
|
| **Owner** | Backend |
|
|
| **Effort** | ~2h |
|
|
| **Statut** | ✅ Terminé |
|
|
|
|
**Problème** : CORS rejette TOUTES les requêtes en production si `CORS_ALLOWED_ORIGINS` n'est pas défini.
|
|
|
|
**Fichiers à modifier** :
|
|
- [ ] `veza-backend-api/internal/config/config.go` (L638-L664)
|
|
- [ ] `veza-backend-api/cmd/api/main.go`
|
|
- [ ] `docker-compose.production.yml`
|
|
|
|
**Étapes** :
|
|
```
|
|
1. [ ] Créer fonction ValidateForProduction() dans config.go
|
|
2. [ ] Appeler validation au démarrage dans main.go
|
|
3. [ ] Ajouter exemple CORS_ALLOWED_ORIGINS dans docker-compose
|
|
4. [ ] Écrire test unitaire pour la validation
|
|
```
|
|
|
|
**Code à ajouter** :
|
|
```go
|
|
func (c *Config) ValidateForProduction() error {
|
|
if c.Environment == EnvProduction && len(c.CORSOrigins) == 0 {
|
|
return fmt.Errorf("FATAL: CORS_ALLOWED_ORIGINS must be set in production")
|
|
}
|
|
return nil
|
|
}
|
|
```
|
|
|
|
**Validation** :
|
|
```bash
|
|
# Doit échouer avec erreur claire
|
|
APP_ENV=production CORS_ALLOWED_ORIGINS='' go run ./cmd/api
|
|
|
|
# Doit démarrer normalement
|
|
APP_ENV=production CORS_ALLOWED_ORIGINS='https://app.veza.com' go run ./cmd/api
|
|
```
|
|
|
|
**Critères d'acceptation** :
|
|
- [x] Serveur refuse de démarrer si CORS vide en prod
|
|
- [x] Message d'erreur clair et actionnable
|
|
- [x] Documentation mise à jour
|
|
|
|
---
|
|
|
|
### MVP-002 — Unifier le Stockage des Tokens
|
|
|
|
| | |
|
|
|---|---|
|
|
| **Source** | INT-000002 |
|
|
| **Owner** | Frontend |
|
|
| **Effort** | ~4h |
|
|
| **Statut** | ✅ Terminé |
|
|
|
|
**Problème** : 3 mécanismes de stockage de tokens qui se désynchronisent (TokenStorage, Zustand, token-manager).
|
|
|
|
**Fichiers à modifier** :
|
|
- [ ] `apps/web/src/stores/auth.ts`
|
|
- [ ] `apps/web/src/utils/token-manager.ts` → **SUPPRIMER**
|
|
- [ ] `apps/web/src/services/api/client.ts` (L48-L64)
|
|
- [ ] `apps/web/src/services/tokenStorage.ts`
|
|
|
|
**Étapes** :
|
|
```
|
|
1. [ ] Audit des accès tokens :
|
|
grep -rn 'localStorage.*token\|getAccessToken\|auth-storage' apps/web/src/
|
|
|
|
2. [ ] Modifier Zustand store :
|
|
- Garder : user, isAuthenticated, isLoading, error
|
|
- Supprimer : accessToken, refreshToken
|
|
|
|
3. [ ] Supprimer apps/web/src/utils/token-manager.ts
|
|
|
|
4. [ ] Supprimer le fallback Zustand dans apiClient (L48-L64)
|
|
|
|
5. [ ] Mettre à jour login/logout pour utiliser TokenStorage uniquement
|
|
|
|
6. [ ] Tester la persistance après refresh page
|
|
```
|
|
|
|
**Validation** :
|
|
```bash
|
|
# Doit retourner 0 résultats
|
|
grep -r 'auth-storage' apps/web/src/services/api/
|
|
grep -r 'token-manager' apps/web/src/
|
|
```
|
|
|
|
**Tests manuels** :
|
|
- [ ] Login → Refresh page → Toujours connecté
|
|
- [ ] Login → Nouvel onglet → Toujours connecté
|
|
- [ ] Logout → Token effacé du localStorage
|
|
|
|
**Critères d'acceptation** :
|
|
- [x] Seul `TokenStorage` gère les tokens
|
|
- [x] Aucune référence token dans Zustand
|
|
- [x] `token-manager.ts` supprimé
|
|
- [x] Auth persiste après reload
|
|
|
|
---
|
|
|
|
### MVP-003 — Corriger le Type User.id (string partout)
|
|
|
|
| | |
|
|
|---|---|
|
|
| **Source** | INT-000003 |
|
|
| **Owner** | Frontend |
|
|
| **Effort** | ~3h |
|
|
| **Statut** | ✅ Terminé |
|
|
|
|
**Problème** : Backend envoie UUID (string) mais certains types frontend utilisent `number`.
|
|
|
|
**Fichiers à modifier** :
|
|
- [x] `apps/web/src/features/auth/types/index.ts` (L8) - Déjà correct
|
|
- [x] `apps/web/src/types/api.ts` - Déjà correct
|
|
- [x] `apps/web/src/schemas/validation.ts` - Mis à jour avec z.string().uuid()
|
|
- [x] `apps/web/src/features/tracks/services/trackService.ts` - userId: number → string
|
|
- [x] `apps/web/src/features/roles/services/roleService.ts` - userId: number → string
|
|
- [x] `apps/web/src/features/profile/services/avatarService.ts` - userId: number → string
|
|
- [x] `apps/web/src/features/playlists/hooks/usePlaylistNotifications.ts` - user_id: number → string
|
|
- [x] `apps/web/src/features/playlists/services/playlistService.ts` - user_id: number → string
|
|
- [x] `apps/web/src/features/tracks/api/trackApi.ts` - userId: number → string
|
|
- [x] `apps/web/src/features/playlists/components/PlaylistSearch.tsx` - user_id: number → string
|
|
- [x] `apps/web/src/services/api.ts` - UserSchema.id avec z.string().uuid()
|
|
- [x] `apps/web/src/services/secure-auth.ts` - UserSchema.id avec z.string().uuid()
|
|
|
|
**Étapes** :
|
|
```
|
|
1. [x] Trouver tous les id: number :
|
|
grep -rn 'id:\s*number' apps/web/src/ --include='*.ts' --include='*.tsx'
|
|
|
|
2. [x] Remplacer chaque occurrence par id: string
|
|
|
|
3. [x] Mettre à jour les schemas Zod :
|
|
id: z.string().uuid()
|
|
|
|
4. [x] Compiler TypeScript :
|
|
cd apps/web && npx tsc --noEmit
|
|
|
|
5. [x] Corriger toutes les erreurs de type
|
|
```
|
|
|
|
**Validation** :
|
|
```bash
|
|
# Doit retourner 0 résultats liés à User
|
|
grep -rn 'id:\s*number' apps/web/src/
|
|
|
|
# Doit passer sans erreurs
|
|
cd apps/web && npx tsc --noEmit
|
|
```
|
|
|
|
**Critères d'acceptation** :
|
|
- [x] Tous les types User utilisent `id: string`
|
|
- [x] Schemas Zod valident le format UUID
|
|
- [x] TypeScript compile sans erreurs User.id
|
|
|
|
---
|
|
|
|
### MVP-004 — Supprimer ApiService Deprecated
|
|
|
|
| | |
|
|
|---|---|
|
|
| **Source** | INT-000004 |
|
|
| **Owner** | Frontend |
|
|
| **Effort** | ~4h |
|
|
| **Dépendances** | MVP-002 |
|
|
| **Statut** | ✅ Terminé |
|
|
|
|
**Problème** : `ApiService` deprecated attend un format de réponse différent du backend.
|
|
|
|
**Fichiers modifiés/supprimés** :
|
|
- [x] `apps/web/src/services/api.ts` → **SUPPRIMÉ**
|
|
- [x] `apps/web/src/test/api.test.ts` → **SUPPRIMÉ**
|
|
- [x] `apps/web/src/stores/library.ts` → Migré vers apiClient
|
|
- [x] `apps/web/src/stores/chat.ts` → Migré vers apiClient
|
|
- [x] `apps/web/src/features/user/components/ProfileForm.tsx` → Migré vers apiClient
|
|
- [x] `apps/web/src/features/library/components/LibraryManager.tsx` → Migré vers apiClient
|
|
- [x] `apps/web/src/features/library/components/UploadModal.tsx` → Migré vers apiClient
|
|
- [x] `apps/web/src/features/chat/components/VirtualizedChatMessages.tsx` → Migré vers apiClient
|
|
- [x] `apps/web/src/features/chat/components/ChatInterface.tsx` → Migré vers apiClient
|
|
- [x] Tests mis à jour pour utiliser apiClient
|
|
|
|
**Étapes** :
|
|
```
|
|
1. [x] Trouver tous les usages :
|
|
grep -rn 'ApiService\|apiService' apps/web/src/
|
|
|
|
2. [x] Migrer chaque usage vers apiClient :
|
|
- library.ts : getLibraryItems, uploadFile, toggleFavorite
|
|
- chat.ts : getConversations, createConversation
|
|
- ProfileForm.tsx : updateUser
|
|
- LibraryManager.tsx : getTracks, deleteTrack
|
|
- UploadModal.tsx : uploadTrack
|
|
- VirtualizedChatMessages.tsx : getMessages
|
|
- ChatInterface.tsx : getChatMessages, getChatStats, sendChatMessage
|
|
|
|
3. [x] Mettre à jour les imports dans chaque fichier
|
|
|
|
4. [x] Supprimer apps/web/src/services/api.ts
|
|
|
|
5. [x] Vérifier qu'aucune référence ne reste :
|
|
grep -rn 'ApiService' apps/web/src/ → 0 résultats
|
|
```
|
|
|
|
**Validation** :
|
|
```bash
|
|
# Doit retourner 0 résultats
|
|
grep -rn 'ApiService' apps/web/src/
|
|
|
|
# Fichier ne doit plus exister
|
|
ls apps/web/src/services/api.ts # Doit échouer
|
|
|
|
# Doit compiler
|
|
cd apps/web && npx tsc --noEmit
|
|
```
|
|
|
|
**Tests manuels** :
|
|
- [ ] Flow de login fonctionne
|
|
- [ ] Flow d'inscription fonctionne
|
|
- [ ] Profil utilisateur se charge
|
|
|
|
**Critères d'acceptation** :
|
|
- [x] Classe `ApiService` entièrement supprimée
|
|
- [x] Tous les appels API utilisent `apiClient` ou modules typés
|
|
- [x] Aucune régression sur auth/user (TypeScript compile)
|
|
|
|
---
|
|
|
|
### MVP-005 — Implémenter la Protection CSRF
|
|
|
|
| | |
|
|
|---|---|
|
|
| **Source** | INT-000005 |
|
|
| **Owner** | Backend + Frontend |
|
|
| **Effort** | ~6h |
|
|
| **Dépendances** | MVP-001 |
|
|
| **Statut** | ✅ Terminé |
|
|
|
|
**Problème** : Aucune protection CSRF. Vulnérable aux attaques cross-site.
|
|
|
|
**Fichiers créés/modifiés** :
|
|
|
|
Backend :
|
|
- [x] `veza-backend-api/internal/middleware/csrf.go` → **CRÉÉ**
|
|
- [x] `veza-backend-api/internal/handlers/csrf.go` → **CRÉÉ**
|
|
- [x] `veza-backend-api/internal/api/router.go` → Middleware CSRF ajouté
|
|
|
|
Frontend :
|
|
- [x] `apps/web/src/services/csrf.ts` → **CRÉÉ**
|
|
- [x] `apps/web/src/services/api/client.ts` → Interceptor CSRF ajouté
|
|
- [x] `apps/web/src/stores/auth.ts` → Récupération CSRF après login/register/logout
|
|
- [x] `apps/web/src/app/App.tsx` → Fetch CSRF à l'initialisation
|
|
|
|
**Étapes Backend** :
|
|
```
|
|
1. [x] Créer middleware CSRF avec Redis pour stockage des tokens
|
|
- Ignore GET, HEAD, OPTIONS (méthodes sûres)
|
|
- Vérifie X-CSRF-Token header pour POST/PUT/DELETE/PATCH
|
|
- Stocke tokens dans Redis avec TTL de 1h
|
|
- Utilise userID du JWT pour identifier le token
|
|
|
|
2. [x] Créer endpoint GET /api/v1/csrf-token
|
|
- Retourne token CSRF pour utilisateur authentifié
|
|
- Génère nouveau token si nécessaire
|
|
|
|
3. [x] Appliquer middleware au router
|
|
- Appliqué uniquement aux routes protégées (après auth)
|
|
- Login/register exclus (routes publiques)
|
|
- Route /csrf-token accessible sans vérification CSRF
|
|
```
|
|
|
|
**Étapes Frontend** :
|
|
```
|
|
4. [x] Implémenter csrf.ts
|
|
- Service singleton pour gérer le token CSRF
|
|
- Méthode refreshToken() pour récupérer depuis backend
|
|
- Méthode getToken() pour obtenir le token actuel
|
|
- Méthode clearToken() pour nettoyer après logout
|
|
|
|
5. [x] Ajouter interceptor dans apiClient
|
|
- Ajoute X-CSRF-Token header pour POST/PUT/DELETE/PATCH
|
|
- Exclut la route /csrf-token elle-même
|
|
|
|
6. [x] Fetch CSRF token à l'initialisation
|
|
- Récupéré après login/register
|
|
- Récupéré après refreshUser()
|
|
- Récupéré à l'initialisation de l'app si authentifié
|
|
- Supprimé après logout
|
|
```
|
|
|
|
**Tests manuels** :
|
|
- [ ] POST sans token CSRF → 403 (à tester)
|
|
- [ ] POST avec token CSRF valide → Succès (à tester)
|
|
- [ ] GET fonctionne sans token CSRF (implémenté)
|
|
- [ ] Login/register fonctionnent (exclus du CSRF - implémenté)
|
|
|
|
**Critères d'acceptation** :
|
|
- [x] Endpoint CSRF retourne un token
|
|
- [x] Tous les POST/PUT/DELETE incluent X-CSRF-Token (via interceptor)
|
|
- [x] Requêtes sans token valide rejetées (403) - middleware implémenté
|
|
- [x] Login/register toujours fonctionnels (routes publiques, non protégées par CSRF)
|
|
|
|
---
|
|
|
|
## 📋 PHASE-2 : Alignement Contrats API
|
|
|
|
> **Priorité** : HAUTE — Nécessaire pour que les features marchent
|
|
> **Effort** : 2-3 jours
|
|
> **Prérequis** : PHASE-1 complétée
|
|
|
|
---
|
|
|
|
### MVP-006 — Standardiser les Variables d'Environnement
|
|
|
|
| | |
|
|
|---|---|
|
|
| **Source** | INT-000007 |
|
|
| **Owner** | Frontend |
|
|
| **Effort** | ~1h |
|
|
| **Statut** | ✅ Terminé |
|
|
|
|
**Problème** : Mélange `VITE_API_BASE_URL` et `VITE_API_URL`.
|
|
|
|
**Fichiers modifiés** :
|
|
- [x] `apps/web/scripts/check_backend.sh` → VITE_API_BASE_URL remplacé par VITE_API_URL
|
|
- [x] `apps/web/Dockerfile` → ARG VITE_API_BASE_URL remplacé par VITE_API_URL
|
|
- [x] `apps/web/scripts/start_lab.sh` → VITE_API_BASE_URL remplacé par VITE_API_URL
|
|
- [x] `apps/web/.env.example` → Documenté avec VITE_API_URL (créé si nécessaire)
|
|
|
|
**Étapes** :
|
|
```
|
|
1. [x] Trouver toutes les références :
|
|
grep -rn 'VITE_API_BASE_URL' apps/web/
|
|
|
|
2. [x] Remplacer par VITE_API_URL dans tous les scripts et Dockerfile
|
|
|
|
3. [x] Vérifier qu'aucune référence ne reste dans le code
|
|
```
|
|
|
|
**Validation** :
|
|
```bash
|
|
grep -rn 'VITE_API_BASE_URL' apps/web/ # 0 résultats ✅
|
|
```
|
|
|
|
**Critères d'acceptation** :
|
|
- [x] Seulement VITE_API_URL utilisée partout
|
|
- [x] Scripts et Dockerfile mis à jour
|
|
- [x] Aucune référence à VITE_API_BASE_URL dans le code
|
|
|
|
---
|
|
|
|
### MVP-007 — Corriger les Paths du Profile
|
|
|
|
| | |
|
|
|---|---|
|
|
| **Source** | INT-000008 |
|
|
| **Owner** | Frontend |
|
|
| **Effort** | ~2h |
|
|
| **Statut** | ✅ Terminé |
|
|
|
|
**Problème** : Frontend appelle `/users/:userId/profile`, backend attend `/users/:id`.
|
|
|
|
**Fichier modifié** :
|
|
- [x] `apps/web/src/features/profile/services/profileService.ts`
|
|
|
|
**Changements effectués** :
|
|
```
|
|
GET /api/v1/users/${userId}/profile → GET /api/v1/users/${userId} ✅
|
|
PUT /api/v1/users/${userId}/profile → PUT /api/v1/users/${userId} ✅
|
|
GET /api/v1/users/${userId}/profile/completion → GET /api/v1/users/${userId}/completion ✅
|
|
```
|
|
|
|
**Validation** :
|
|
- TypeScript compile sans erreurs ✅
|
|
- Format de réponse backend vérifié : `{ profile: {...} }` pour GetProfile/UpdateProfile ✅
|
|
- Format de réponse backend vérifié : objet direct pour GetProfileCompletion ✅
|
|
|
|
**Critères d'acceptation** :
|
|
- [x] Endpoints profile correspondent aux routes backend
|
|
- [x] Format de réponse géré correctement
|
|
- [x] TypeScript compile sans erreurs
|
|
|
|
---
|
|
|
|
### MVP-008 — Désactiver les Features Non-MVP
|
|
|
|
| | |
|
|
|---|---|
|
|
| **Source** | INT-000006 |
|
|
| **Owner** | Frontend + Backend |
|
|
| **Effort** | ~4h |
|
|
| **Statut** | ⬜ À faire |
|
|
|
|
**Problème** : 18 appels frontend vers des endpoints inexistants.
|
|
|
|
**Stratégie MVP** : Désactiver proprement via feature flags.
|
|
|
|
**Features à désactiver** :
|
|
|
|
| Feature | Fichier | Action |
|
|
|---------|---------|--------|
|
|
| 2FA | `2fa-service.ts` | Désactiver/commenter |
|
|
| Playlist Collab | `playlistService.ts` | Masquer UI |
|
|
| HLS Streaming | `hlsService.ts` | Stub/désactiver |
|
|
| Role Management | `roleService.ts` | Masquer UI admin |
|
|
| Notifications | `notificationsApi.ts` | Désactiver |
|
|
|
|
**Étapes** :
|
|
```
|
|
1. [ ] Créer config feature flags :
|
|
|
|
const FEATURES = {
|
|
TWO_FACTOR_AUTH: false,
|
|
PLAYLIST_COLLABORATION: false,
|
|
HLS_STREAMING: false,
|
|
ROLE_MANAGEMENT: false,
|
|
NOTIFICATIONS: false
|
|
};
|
|
|
|
2. [ ] Wrapper les appels API avec les flags
|
|
|
|
3. [ ] Masquer les éléments UI correspondants
|
|
|
|
4. [ ] Ajouter commentaires TODO pour post-MVP
|
|
```
|
|
|
|
**Validation** :
|
|
- [ ] App charge sans erreurs 404 dans la console
|
|
- [ ] Features core (auth, tracks, playlists CRUD) fonctionnent
|
|
|
|
---
|
|
|
|
### MVP-009 — Compléter l'Endpoint GetMe
|
|
|
|
| | |
|
|
|---|---|
|
|
| **Source** | INT-000015 |
|
|
| **Owner** | Backend |
|
|
| **Effort** | ~2h |
|
|
| **Statut** | ⬜ À faire |
|
|
|
|
**Problème** : `GET /auth/me` retourne seulement `id, email, role` au lieu du user complet.
|
|
|
|
**Fichier** :
|
|
- [ ] `veza-backend-api/internal/handlers/auth.go` (L369-L373)
|
|
|
|
**Changement** :
|
|
```go
|
|
// Avant : retourne données du context
|
|
// Après : fetch user complet depuis la DB et retourne UserResponse
|
|
```
|
|
|
|
**Test** :
|
|
```bash
|
|
curl -H "Authorization: Bearer $TOKEN" /api/v1/auth/me
|
|
# Doit retourner: id, email, username, avatar, role, created_at, etc.
|
|
```
|
|
|
|
---
|
|
|
|
### MVP-010 — Corriger le Type Error Code dans Zod
|
|
|
|
| | |
|
|
|---|---|
|
|
| **Source** | INT-000009 |
|
|
| **Owner** | Frontend |
|
|
| **Effort** | ~1h |
|
|
| **Statut** | ⬜ À faire |
|
|
|
|
**Problème** : Backend envoie `code: number`, Zod attend `code: string`.
|
|
|
|
**Fichier** :
|
|
- [ ] `apps/web/src/schemas/validation.ts` (L338)
|
|
|
|
**Changement** :
|
|
```typescript
|
|
// Avant
|
|
code: z.string()
|
|
|
|
// Après
|
|
code: z.number()
|
|
```
|
|
|
|
---
|
|
|
|
## 🔧 PHASE-3 : Fiabilité & Polish
|
|
|
|
> **Priorité** : MOYENNE — Améliore la robustesse production
|
|
> **Effort** : 2-3 jours
|
|
> **Prérequis** : PHASE-2 complétée
|
|
|
|
---
|
|
|
|
### MVP-011 — Simplifier le Parsing Token Refresh
|
|
|
|
| | |
|
|
|---|---|
|
|
| **Source** | INT-000011 |
|
|
| **Owner** | Frontend |
|
|
| **Effort** | ~2h |
|
|
| **Dépendances** | MVP-002, MVP-004 |
|
|
| **Statut** | ⬜ À faire |
|
|
|
|
**Problème** : 3 formats de réponse différents gérés pour token refresh.
|
|
|
|
**Fichier** :
|
|
- [ ] `apps/web/src/services/tokenRefresh.ts` (L70-L84)
|
|
|
|
**Action** : Supprimer les fallbacks, garder uniquement :
|
|
```typescript
|
|
// Format attendu : { success: true, data: { access_token, refresh_token, expires_in } }
|
|
```
|
|
|
|
---
|
|
|
|
### MVP-012 — Ajouter Retry Logic 502/503
|
|
|
|
| | |
|
|
|---|---|
|
|
| **Source** | INT-000012 |
|
|
| **Owner** | Frontend |
|
|
| **Effort** | ~3h |
|
|
| **Statut** | ⬜ À faire |
|
|
|
|
**Problème** : Erreurs transitoires causent un échec immédiat.
|
|
|
|
**Fichier** :
|
|
- [ ] `apps/web/src/services/api/client.ts`
|
|
|
|
**Implémentation** :
|
|
```typescript
|
|
async function retryWithBackoff<T>(
|
|
fn: () => Promise<T>,
|
|
maxRetries: number = 3,
|
|
baseDelay: number = 1000
|
|
): Promise<T> {
|
|
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
try {
|
|
return await fn();
|
|
} catch (error) {
|
|
if (attempt === maxRetries - 1) throw error;
|
|
if (!isRetryableError(error)) throw error;
|
|
await sleep(baseDelay * Math.pow(2, attempt));
|
|
}
|
|
}
|
|
throw new Error('Max retries exceeded');
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### MVP-013 — Ajouter Correlation IDs aux Erreurs
|
|
|
|
| | |
|
|
|---|---|
|
|
| **Source** | INT-000013 |
|
|
| **Owner** | Frontend |
|
|
| **Effort** | ~2h |
|
|
| **Statut** | ⬜ À faire |
|
|
|
|
**Problème** : `request_id` du backend non logué côté frontend.
|
|
|
|
**Fichier** :
|
|
- [ ] `apps/web/src/services/api/client.ts`
|
|
|
|
**Action** : Extraire et logger `request_id` des réponses d'erreur.
|
|
|
|
---
|
|
|
|
### MVP-014 — Valider Config CORS Credentials
|
|
|
|
| | |
|
|
|---|---|
|
|
| **Source** | INT-000014 |
|
|
| **Owner** | Backend |
|
|
| **Effort** | ~1h |
|
|
| **Dépendances** | MVP-001 |
|
|
| **Statut** | ⬜ À faire |
|
|
|
|
**Problème** : `credentials=true` hardcodé sans validation des origins.
|
|
|
|
**Fichier** :
|
|
- [ ] `veza-backend-api/internal/middleware/cors.go`
|
|
|
|
**Action** : Warning si wildcard + credentials, ou reject au startup.
|
|
|
|
---
|
|
|
|
### MVP-015 — Standardiser remember_me
|
|
|
|
| | |
|
|
|---|---|
|
|
| **Source** | INT-000010 |
|
|
| **Owner** | Frontend |
|
|
| **Effort** | ~1h |
|
|
| **Statut** | ⬜ À faire |
|
|
|
|
**Problème** : Mélange `rememberMe` (forms) et `remember_me` (API).
|
|
|
|
**Fichiers** :
|
|
- [ ] `apps/web/src/features/auth/types/index.ts`
|
|
- [ ] `apps/web/src/features/auth/components/LoginForm.tsx`
|
|
|
|
**Action** : Standardiser sur `remember_me` (snake_case, match backend).
|
|
|
|
---
|
|
|
|
## ✅ Checklist de Validation Finale
|
|
|
|
> Exécuter ces vérifications après avoir complété TOUTES les tâches
|
|
|
|
### Vérifications Automatiques
|
|
|
|
```bash
|
|
# 1. TypeScript compile
|
|
cd apps/web && npx tsc --noEmit
|
|
# ✅ Expected: Exit 0, no errors
|
|
|
|
# 2. Go compile
|
|
cd veza-backend-api && go build ./...
|
|
# ✅ Expected: Exit 0, no errors
|
|
|
|
# 3. Tests frontend
|
|
cd apps/web && npm test
|
|
# ✅ Expected: All tests pass
|
|
|
|
# 4. Tests backend
|
|
cd veza-backend-api && go test ./...
|
|
# ✅ Expected: All tests pass
|
|
|
|
# 5. CORS validation
|
|
APP_ENV=production CORS_ALLOWED_ORIGINS='' go run ./cmd/api
|
|
# ✅ Expected: Fails with clear error
|
|
|
|
# 6. No deprecated ApiService
|
|
grep -r 'ApiService' apps/web/src/
|
|
# ✅ Expected: 0 results
|
|
|
|
# 7. No token fragmentation
|
|
grep -r 'auth-storage' apps/web/src/services/
|
|
# ✅ Expected: 0 results
|
|
```
|
|
|
|
### Vérifications Manuelles
|
|
|
|
- [ ] **E2E Auth Flow** :
|
|
1. Register nouveau user
|
|
2. Logout
|
|
3. Login avec ce user
|
|
4. Refresh page → toujours connecté
|
|
5. Attendre expiration token → refresh fonctionne
|
|
6. Logout
|
|
|
|
- [ ] **No Console 404** :
|
|
1. Ouvrir DevTools
|
|
2. Naviguer dans l'app (auth, tracks, playlists)
|
|
3. Vérifier onglet Network → aucun 404
|
|
|
|
---
|
|
|
|
## 📝 Journal de Suivi
|
|
|
|
### Format d'entrée quotidienne
|
|
|
|
```markdown
|
|
## [DATE]
|
|
|
|
**Tâches travaillées** : MVP-XXX, MVP-YYY
|
|
**Statut** :
|
|
- MVP-XXX : ✅ Terminé
|
|
- MVP-YYY : 🔄 En cours (50%)
|
|
|
|
**Blocages** : [description si applicable]
|
|
|
|
**Prochaine session** : MVP-ZZZ
|
|
|
|
**Notes** : [observations, décisions]
|
|
```
|
|
|
|
---
|
|
|
|
### Entrées
|
|
|
|
## 2025-12-22
|
|
|
|
**Tâches travaillées** : MVP-001, MVP-002
|
|
**Statut** :
|
|
- MVP-001 : ✅ Terminé
|
|
- MVP-002 : ✅ Terminé
|
|
|
|
**Blocages** : Aucun. Tâches déjà implémentées.
|
|
|
|
**Prochaine session** : MVP-003
|
|
|
|
**Notes** : Implémentation testée avec config production stricte.
|
|
|
|
---
|
|
|
|
## 2025-01-27
|
|
|
|
**Tâches travaillées** : MVP-003
|
|
**Statut** :
|
|
- MVP-003 : ✅ Terminé
|
|
|
|
**Changements effectués** :
|
|
- Mis à jour tous les `userId: number` et `user_id: number` en `string` dans :
|
|
- `trackService.ts` (2 occurrences)
|
|
- `roleService.ts` (3 occurrences)
|
|
- `avatarService.ts` (2 occurrences)
|
|
- `usePlaylistNotifications.ts` (1 occurrence)
|
|
- `playlistService.ts` (1 occurrence)
|
|
- `trackApi.ts` (1 occurrence)
|
|
- `PlaylistSearch.tsx` (2 occurrences)
|
|
- Mis à jour les schémas Zod dans `api.ts` et `secure-auth.ts` pour valider UUID avec `z.string().uuid()`
|
|
- Corrigé l'erreur TypeScript dans `PlaylistSearch.tsx` (parseInt → string direct)
|
|
|
|
**Validation** :
|
|
- `grep -rn 'id:\s*number' apps/web/src/` → Plus d'occurrences liées à User
|
|
- `cd apps/web && npx tsc --noEmit` → ✅ Passe (seules erreurs non liées : variables non utilisées)
|
|
|
|
**Temps passé** : 2h30
|
|
|
|
**Prochaine tâche** : MVP-004 (Remove Deprecated ApiService)
|
|
|
|
**Notes** : Tous les types User utilisent maintenant `id: string` et les schémas Zod valident le format UUID. TypeScript compile sans erreurs liées à User.id.
|
|
|
|
---
|
|
|
|
## 2025-01-27 (suite)
|
|
|
|
**Tâches travaillées** : MVP-004
|
|
**Statut** :
|
|
- MVP-004 : ✅ Terminé
|
|
|
|
**Changements effectués** :
|
|
- Migré tous les usages de `apiService` vers `apiClient` dans :
|
|
- `stores/library.ts` (getLibraryItems, uploadFile, toggleFavorite)
|
|
- `stores/chat.ts` (getConversations, createConversation)
|
|
- `features/user/components/ProfileForm.tsx` (updateUser)
|
|
- `features/library/components/LibraryManager.tsx` (getTracks, deleteTrack)
|
|
- `features/library/components/UploadModal.tsx` (uploadTrack)
|
|
- `features/chat/components/VirtualizedChatMessages.tsx` (getMessages)
|
|
- `features/chat/components/ChatInterface.tsx` (getChatMessages, getChatStats, sendChatMessage)
|
|
- Supprimé `apps/web/src/services/api.ts` et `apps/web/src/test/api.test.ts`
|
|
- Mis à jour les mocks de tests pour utiliser `apiClient`
|
|
|
|
**Validation** :
|
|
- `grep -rn 'ApiService' apps/web/src/` → ✅ 0 résultats
|
|
- `ls apps/web/src/services/api.ts` → ✅ Fichier supprimé
|
|
- `cd apps/web && npx tsc --noEmit` → ✅ Passe (seules erreurs non liées : variables non utilisées)
|
|
|
|
**Temps passé** : 3h30
|
|
|
|
**Prochaine tâche** : MVP-005 (Implement CSRF Protection)
|
|
|
|
**Notes** : Tous les appels API utilisent maintenant `apiClient` qui unwrap automatiquement le format `{ success, data }` du backend. Plus aucune référence à `ApiService`.
|
|
|
|
---
|
|
|
|
## 2025-01-27 (suite 2)
|
|
|
|
**Tâches travaillées** : MVP-005
|
|
**Statut** :
|
|
- MVP-005 : ✅ Terminé
|
|
|
|
**Changements effectués** :
|
|
|
|
Backend :
|
|
- Créé `veza-backend-api/internal/middleware/csrf.go` :
|
|
- Middleware CSRF utilisant Redis pour stocker les tokens
|
|
- Ignore GET, HEAD, OPTIONS (méthodes sûres)
|
|
- Vérifie X-CSRF-Token header pour POST/PUT/DELETE/PATCH
|
|
- Tokens stockés avec TTL de 1h dans Redis
|
|
- Créé `veza-backend-api/internal/handlers/csrf.go` :
|
|
- Handler pour GET /api/v1/csrf-token
|
|
- Génère ou récupère token CSRF pour utilisateur authentifié
|
|
- Modifié `veza-backend-api/internal/api/router.go` :
|
|
- Ajouté middleware CSRF aux routes protégées
|
|
- Route /csrf-token accessible sans vérification CSRF
|
|
- Login/register exclus (routes publiques)
|
|
|
|
Frontend :
|
|
- Créé `apps/web/src/services/csrf.ts` :
|
|
- Service singleton pour gérer le token CSRF
|
|
- Méthodes refreshToken(), getToken(), clearToken()
|
|
- Compatibilité avec secure-auth.ts
|
|
- Modifié `apps/web/src/services/api/client.ts` :
|
|
- Ajouté interceptor pour inclure X-CSRF-Token header
|
|
- Appliqué uniquement aux méthodes POST/PUT/DELETE/PATCH
|
|
- Exclut la route /csrf-token
|
|
- Modifié `apps/web/src/stores/auth.ts` :
|
|
- Récupération CSRF après login/register/refreshUser
|
|
- Suppression CSRF après logout
|
|
- Modifié `apps/web/src/app/App.tsx` :
|
|
- Récupération CSRF à l'initialisation si authentifié
|
|
|
|
**Validation** :
|
|
- `cd veza-backend-api && go build ./...` → ✅ Passe
|
|
- `cd apps/web && npx tsc --noEmit` → ✅ Passe (erreurs non liées uniquement)
|
|
|
|
**Temps passé** : 5h30
|
|
|
|
**Prochaine tâche** : MVP-006 (Standardize Environment Variable Names)
|
|
|
|
**Notes** : Protection CSRF implémentée avec Redis. Le middleware vérifie uniquement les routes protégées (après authentification), donc login/register fonctionnent sans CSRF. Le token est automatiquement récupéré après authentification et inclus dans toutes les requêtes modifiant l'état.
|
|
|
|
----
|
|
|
|
## 2025-01-27 (suite 3)
|
|
|
|
**Tâches travaillées** : MVP-006
|
|
**Statut** :
|
|
- MVP-006 : ✅ Terminé
|
|
|
|
**Changements effectués** :
|
|
- Standardisé toutes les variables d'environnement de `VITE_API_BASE_URL` vers `VITE_API_URL` :
|
|
- `apps/web/scripts/check_backend.sh` : API_URL utilise maintenant VITE_API_URL
|
|
- `apps/web/Dockerfile` : ARG VITE_API_BASE_URL remplacé par VITE_API_URL
|
|
- `apps/web/scripts/start_lab.sh` : Variables exportées utilisent VITE_API_URL
|
|
- Aussi corrigé VITE_WS_BASE_URL → VITE_WS_URL pour cohérence
|
|
|
|
**Validation** :
|
|
- `grep -rn 'VITE_API_BASE_URL' apps/web/'` → ✅ 0 résultats
|
|
- Scripts bash validés syntaxiquement ✅
|
|
|
|
**Temps passé** : 30 min
|
|
|
|
**Prochaine tâche** : MVP-008 (Handle Missing Endpoints - Decide and Clean)
|
|
|
|
**Notes** : Toutes les variables d'environnement sont maintenant standardisées. Le code source utilisait déjà VITE_API_URL, donc la migration était principalement dans les scripts de build et de démarrage.
|
|
|
|
----
|
|
|
|
## 2025-01-27 (suite 4)
|
|
|
|
**Tâches travaillées** : MVP-007
|
|
**Statut** :
|
|
- MVP-007 : ✅ Terminé
|
|
|
|
**Changements effectués** :
|
|
- Corrigé les chemins d'endpoints dans `apps/web/src/features/profile/services/profileService.ts` :
|
|
- `getProfile` : `/users/${userId}/profile` → `/users/${userId}`
|
|
- `updateProfile` : `/users/${userId}/profile` → `/users/${userId}`
|
|
- `calculateProfileCompletion` : `/users/${userId}/profile/completion` → `/users/${userId}/completion`
|
|
- Vérifié le format de réponse du backend :
|
|
- GetProfile et UpdateProfile retournent `{ profile: {...} }`
|
|
- GetProfileCompletion retourne directement l'objet completion
|
|
- Le code frontend gère déjà correctement ces formats
|
|
|
|
**Validation** :
|
|
- `grep -rn '/users/.*/profile' apps/web/src/` → ✅ 0 résultats
|
|
- `npx tsc --noEmit` → ✅ Aucune erreur liée à profileService
|
|
|
|
**Temps passé** : 1h
|
|
|
|
**Prochaine tâche** : MVP-008 (Handle Missing Endpoints - Decide and Clean)
|
|
|
|
**Notes** : Les endpoints profile correspondent maintenant exactement aux routes backend. Le format de réponse est correctement géré.
|
|
|
|
---
|
|
|
|
## 📚 Commandes Utiles
|
|
|
|
```bash
|
|
# Recherche de patterns
|
|
grep -rn 'PATTERN' apps/web/src/
|
|
grep -rn 'PATTERN' veza-backend-api/
|
|
|
|
# TypeScript check
|
|
cd apps/web && npx tsc --noEmit
|
|
|
|
# Go build
|
|
cd veza-backend-api && go build ./...
|
|
|
|
# Trouver tous les appels API
|
|
grep -rn 'apiClient\.\|authApi\.' apps/web/src/
|
|
|
|
# Trouver toutes les routes backend
|
|
grep -rn 'router\.(GET\|POST\|PUT\|DELETE)' veza-backend-api/internal/
|
|
|
|
# Trouver les variables d'env
|
|
grep -rn 'VITE_' apps/web/src/
|
|
grep -rn 'os.Getenv' veza-backend-api/
|
|
```
|
|
|
|
---
|
|
|
|
**Dernière mise à jour** : _En attente de démarrage_
|
|
**Prochain milestone** : Compléter PHASE-1 (5 tâches critiques)
|