veza/VEZA_MVP_TODOLIST_TRACKING.md
2025-12-22 22:00:50 +01:00

730 lines
17 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** | 0 / 15 |
| **Phase actuelle** | PHASE-1 (Critical) |
| **Progression globale** | ░░░░░░░░░░ 0% |
| **Dernière mise à jour** | _non démarré_ |
### Progression par Phase
| Phase | Statut | Progression |
|-------|--------|-------------|
| PHASE-1 — Bloquants Critiques | 🔴 À faire | 0/5 |
| PHASE-2 — Alignement API | ⚪ En attente | 0/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** | ⬜ À faire |
**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** :
- [ ] Serveur refuse de démarrer si CORS vide en prod
- [ ] Message d'erreur clair et actionnable
- [ ] Documentation mise à jour
---
### MVP-002 — Unifier le Stockage des Tokens
| | |
|---|---|
| **Source** | INT-000002 |
| **Owner** | Frontend |
| **Effort** | ~4h |
| **Statut** | ⬜ À faire |
**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** :
- [ ] Seul `TokenStorage` gère les tokens
- [ ] Aucune référence token dans Zustand
- [ ] `token-manager.ts` supprimé
- [ ] Auth persiste après reload
---
### MVP-003 — Corriger le Type User.id (string partout)
| | |
|---|---|
| **Source** | INT-000003 |
| **Owner** | Frontend |
| **Effort** | ~3h |
| **Statut** | ⬜ À faire |
**Problème** : Backend envoie UUID (string) mais certains types frontend utilisent `number`.
**Fichiers à modifier** :
- [ ] `apps/web/src/features/auth/types/index.ts` (L8)
- [ ] `apps/web/src/types/api.ts` (vérifier)
- [ ] `apps/web/src/schemas/validation.ts`
**Étapes** :
```
1. [ ] Trouver tous les id: number :
grep -rn 'id:\s*number' apps/web/src/ --include='*.ts' --include='*.tsx'
2. [ ] Remplacer chaque occurrence par id: string
3. [ ] Mettre à jour les schemas Zod :
id: z.string().uuid()
4. [ ] Compiler TypeScript :
cd apps/web && npx tsc --noEmit
5. [ ] 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** :
- [ ] Tous les types User utilisent `id: string`
- [ ] Schemas Zod valident le format UUID
- [ ] TypeScript compile sans erreurs User.id
---
### MVP-004 — Supprimer ApiService Deprecated
| | |
|---|---|
| **Source** | INT-000004 |
| **Owner** | Frontend |
| **Effort** | ~4h |
| **Dépendances** | MVP-002 |
| **Statut** | ⬜ À faire |
**Problème** : `ApiService` deprecated attend un format de réponse différent du backend.
**Fichier à supprimer** :
- [ ] `apps/web/src/services/api.ts`**SUPPRIMER**
**Étapes** :
```
1. [ ] Trouver tous les usages :
grep -rn 'ApiService\|apiService' apps/web/src/
2. [ ] Migrer chaque usage :
┌─────────────────────┬─────────────────────┐
│ Ancien │ Nouveau │
├─────────────────────┼─────────────────────┤
│ apiService.login() │ authApi.login() │
│ apiService.register()│ authApi.register() │
│ apiService.getUser()│ apiClient.get() │
│ apiService.refresh()│ authApi.refresh() │
└─────────────────────┴─────────────────────┘
3. [ ] Mettre à jour les imports dans chaque fichier
4. [ ] Supprimer apps/web/src/services/api.ts
5. [ ] Vérifier qu'aucune référence ne reste :
grep -rn 'ApiService' apps/web/src/
```
**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** :
- [ ] Classe `ApiService` entièrement supprimée
- [ ] Tous les appels API utilisent `apiClient` ou modules typés
- [ ] Aucune régression sur auth/user
---
### MVP-005 — Implémenter la Protection CSRF
| | |
|---|---|
| **Source** | INT-000005 |
| **Owner** | Backend + Frontend |
| **Effort** | ~6h |
| **Dépendances** | MVP-001 |
| **Statut** | ⬜ À faire |
**Problème** : Aucune protection CSRF. Vulnérable aux attaques cross-site.
**Fichiers à créer/modifier** :
Backend :
- [ ] `veza-backend-api/internal/middleware/csrf.go`**CRÉER**
- [ ] `veza-backend-api/internal/handlers/csrf.go`**CRÉER**
- [ ] `veza-backend-api/internal/api/router.go`
Frontend :
- [ ] `apps/web/src/services/csrf.ts`
- [ ] `apps/web/src/services/api/client.ts`
**Étapes Backend** :
```
1. [ ] Créer middleware CSRF :
func CSRFMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
if c.Request.Method == "GET" || c.Request.Method == "OPTIONS" {
c.Next()
return
}
token := c.GetHeader("X-CSRF-Token")
sessionToken := getSessionCSRFToken(c)
if token == "" || token != sessionToken {
c.AbortWithStatusJSON(403, gin.H{"error": "Invalid CSRF token"})
return
}
c.Next()
}
}
2. [ ] Créer endpoint GET /api/v1/csrf-token
3. [ ] Appliquer middleware au router (exclure login/register)
```
**Étapes Frontend** :
```
4. [ ] Implémenter csrf.ts :
async refreshToken(): Promise<void> {
const response = await fetch('/api/v1/csrf-token', {
credentials: 'include'
});
const data = await response.json();
this.token = data.csrf_token;
}
5. [ ] Ajouter interceptor dans apiClient :
if (['POST', 'PUT', 'DELETE', 'PATCH'].includes(method)) {
config.headers['X-CSRF-Token'] = csrfService.getToken();
}
6. [ ] Fetch CSRF token à l'initialisation de l'app
```
**Tests manuels** :
- [ ] POST sans token CSRF → 403
- [ ] POST avec token CSRF valide → Succès
- [ ] GET fonctionne sans token CSRF
- [ ] Login/register fonctionnent (exclus du CSRF)
**Critères d'acceptation** :
- [ ] Endpoint CSRF retourne un token
- [ ] Tous les POST/PUT/DELETE incluent X-CSRF-Token
- [ ] Requêtes sans token valide rejetées (403)
- [ ] Login/register toujours fonctionnels
---
## 📋 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** | ⬜ À faire |
**Problème** : Mélange `VITE_API_BASE_URL` et `VITE_API_URL`.
**Fichiers** :
- [ ] `apps/web/scripts/check_backend.sh`
- [ ] `apps/web/Dockerfile`
- [ ] `apps/web/.env.example`
**Étapes** :
```
1. [ ] Trouver toutes les références :
grep -rn 'VITE_API_BASE_URL' apps/web/
2. [ ] Remplacer par VITE_API_URL
3. [ ] Mettre à jour .env.example
```
**Validation** :
```bash
grep -rn 'VITE_API_BASE_URL' apps/web/ # 0 résultats
```
---
### MVP-007 — Corriger les Paths du Profile
| | |
|---|---|
| **Source** | INT-000008 |
| **Owner** | Frontend |
| **Effort** | ~2h |
| **Statut** | ⬜ À faire |
**Problème** : Frontend appelle `/users/:userId/profile`, backend attend `/users/:id`.
**Fichier** :
- [ ] `apps/web/src/features/profile/services/profileService.ts`
**Changements** :
```
GET /api/v1/users/${userId}/profile → GET /api/v1/users/${userId}
PUT /api/v1/users/${userId}/profile → PUT /api/v1/users/${userId}
```
**Tests manuels** :
- [ ] Page profil se charge
- [ ] Modification profil sauvegarde
---
### 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
_Aucune entrée pour le moment. Commencer par MVP-001._
---
## 📚 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)