{ "meta": { "title": "Veza Integration Perfection TodoList", "description": "TodoList focalisée exclusivement sur la connexion parfaite Backend Go ↔ Frontend React. Ignorer modules Rust.", "generated_at": "2025-12-25T12:00:00Z", "version": "2.0.0", "scope": { "included": ["apps/web/", "veza-backend-api/"], "excluded": ["veza-chat-server/", "veza-stream-server/", "veza-common/"] }, "target": "Score intégration 10/10 - Connexion parfaite backend/frontend", "current_score": "6.5/10", "target_score": "10/10", "audit_date": "2025-12-25" }, "summary": { "by_priority": { "P0_blocker": 3, "P1_critical": 8, "P2_major": 15, "P3_minor": 6 }, "by_category": { "INT-CORS": 2, "INT-AUTH": 4, "INT-TYPE": 8, "INT-API": 5, "INT-ENDPOINT": 6, "INT-CLEANUP": 4, "INT-TEST": 2, "INT-DOC": 1 }, "by_side": { "backend_only": 10, "frontend_only": 14, "both_sides": 8 }, "estimated_total_hours": 48 }, "categories": { "INT-CORS": "Configuration CORS et origins pour production", "INT-AUTH": "Authentification, tokens, refresh, CSRF", "INT-TYPE": "Alignement parfait types TypeScript ↔ Go DTOs", "INT-API": "Client API, intercepteurs, services, format réponses", "INT-ENDPOINT": "Endpoints manquants ou avec incohérences", "INT-CLEANUP": "Suppression duplication, code legacy, standardisation", "INT-TEST": "Tests d'intégration E2E frontend ↔ backend", "INT-DOC": "Documentation API OpenAPI/Swagger" }, "phases": [ { "id": "PHASE-INT-1", "name": "Production Blockers", "description": "Problèmes qui empêchent le déploiement en production - À résoudre IMMÉDIATEMENT", "priority": "P0", "estimated_hours": 6, "tasks": ["INT-CORS-001", "INT-CORS-002", "INT-AUTH-001"] }, { "id": "PHASE-INT-2", "name": "Critical Type Fixes", "description": "Incohérences de types causant des erreurs runtime", "priority": "P1", "estimated_hours": 12, "tasks": ["INT-TYPE-001", "INT-TYPE-002", "INT-TYPE-003", "INT-TYPE-004", "INT-TYPE-005", "INT-TYPE-006", "INT-TYPE-007", "INT-TYPE-008"] }, { "id": "PHASE-INT-3", "name": "API Client Standardization", "description": "Unifier les clients API et corriger les formats", "priority": "P1", "estimated_hours": 8, "tasks": ["INT-API-001", "INT-API-002", "INT-API-003", "INT-API-004", "INT-API-005"] }, { "id": "PHASE-INT-4", "name": "Auth Flow Perfection", "description": "Parfaire le flow d'authentification complet", "priority": "P1", "estimated_hours": 6, "tasks": ["INT-AUTH-002", "INT-AUTH-003", "INT-AUTH-004"] }, { "id": "PHASE-INT-5", "name": "Cleanup Duplication", "description": "Supprimer tout le code dupliqué et legacy", "priority": "P2", "estimated_hours": 6, "tasks": ["INT-CLEANUP-001", "INT-CLEANUP-002", "INT-CLEANUP-003", "INT-CLEANUP-004"] }, { "id": "PHASE-INT-6", "name": "Missing Endpoints", "description": "Implémenter endpoints manquants des deux côtés", "priority": "P2", "estimated_hours": 8, "tasks": ["INT-ENDPOINT-001", "INT-ENDPOINT-002", "INT-ENDPOINT-003", "INT-ENDPOINT-004", "INT-ENDPOINT-005", "INT-ENDPOINT-006"] }, { "id": "PHASE-INT-7", "name": "Integration Tests & Docs", "description": "Tests E2E et documentation finale", "priority": "P2", "estimated_hours": 4, "tasks": ["INT-TEST-001", "INT-TEST-002", "INT-DOC-001"] } ], "tasks": [ { "id": "INT-CORS-001", "category": "INT-CORS", "title": "Configure CORS_ALLOWED_ORIGINS for production", "description": "En production, si CORS_ALLOWED_ORIGINS est vide, le backend rejette TOUTES les requêtes cross-origin. Cela bloque complètement l'application.", "priority": "P0", "priority_rank": 1, "status": "completed", "estimated_hours": 1, "side": "backend_only", "files_to_modify": [ "veza-backend-api/internal/middleware/cors.go", "veza-backend-api/.env.production", "veza-backend-api/internal/config/config.go" ], "implementation_steps": [ "Ouvrir veza-backend-api/internal/middleware/cors.go", "Vérifier la fonction validateCORSConfig() - elle rejette si allowedOrigins vide en prod", "Créer .env.production si inexistant", "Ajouter CORS_ALLOWED_ORIGINS=https://app.veza.com,https://www.veza.com", "Ajouter validation explicite au démarrage avec message clair", "Tester: ENVIRONMENT=production go run cmd/server/main.go" ], "acceptance_criteria": [ "CORS_ALLOWED_ORIGINS défini dans .env.production", "Backend démarre sans erreur en mode production", "Requêtes depuis frontend autorisées", "Requêtes depuis domaines non autorisés rejetées" ], "dependencies": [], "blocks": ["INT-TEST-001"], "verification_command": "ENVIRONMENT=production go run cmd/server/main.go" }, { "id": "INT-CORS-002", "category": "INT-CORS", "title": "Add preflight request handling validation", "description": "Vérifier que les requêtes OPTIONS (preflight) sont correctement gérées pour tous les endpoints.", "priority": "P0", "priority_rank": 2, "status": "completed", "estimated_hours": 2, "side": "backend_only", "files_to_modify": [ "veza-backend-api/internal/middleware/cors.go" ], "implementation_steps": [ "Vérifier AllowMethods inclut toutes les méthodes utilisées (GET, POST, PUT, PATCH, DELETE, OPTIONS)", "Vérifier AllowHeaders inclut tous les headers custom (Authorization, X-CSRF-Token, Content-Type)", "Ajouter ExposeHeaders pour headers de réponse custom", "Tester preflight avec curl: curl -X OPTIONS -H 'Origin: http://localhost:3000' http://localhost:8080/api/v1/auth/me" ], "acceptance_criteria": [ "AllowMethods contient toutes les méthodes HTTP utilisées", "AllowHeaders contient Authorization, X-CSRF-Token, Content-Type", "Preflight requests retournent 200 avec headers CORS corrects" ], "dependencies": ["INT-CORS-001"], "blocks": [], "verification_command": "curl -v -X OPTIONS -H 'Origin: http://localhost:3000' http://localhost:8080/api/v1/auth/me" }, { "id": "INT-AUTH-001", "category": "INT-AUTH", "title": "Ensure CSRF protection active in production", "description": "Le middleware CSRF est désactivé si Redis n'est pas disponible. En production, Redis DOIT être disponible et CSRF actif.", "priority": "P0", "priority_rank": 3, "status": "completed", "estimated_hours": 2, "side": "both_sides", "files_to_modify": [ "veza-backend-api/internal/middleware/csrf.go", "veza-backend-api/internal/config/config.go", "apps/web/src/services/csrf.ts" ], "implementation_steps": [ "Backend: Vérifier que CSRF middleware fail-fast si Redis indisponible en prod", "Backend: Ajouter log explicite quand CSRF activé/désactivé", "Frontend: Vérifier que csrfService récupère le token avant chaque mutation", "Frontend: Ajouter retry si token CSRF expiré", "Tester: Requête POST sans X-CSRF-Token doit retourner 403" ], "acceptance_criteria": [ "Backend refuse de démarrer en prod si Redis indisponible", "Toutes les requêtes POST/PUT/DELETE/PATCH nécessitent X-CSRF-Token", "Frontend ajoute automatiquement X-CSRF-Token à toutes les mutations", "Token refresh automatique si expiré" ], "dependencies": ["INT-CORS-001"], "blocks": ["INT-TEST-001"], "verification_command": "curl -X POST http://localhost:8080/api/v1/tracks -H 'Authorization: Bearer xxx' (doit retourner 403)" }, { "id": "INT-TYPE-001", "category": "INT-TYPE", "title": "Standardize User.id to string everywhere", "description": "User.id est uuid.UUID (string) côté backend mais parfois number côté frontend. Cela cause des erreurs de comparaison.", "priority": "P1", "priority_rank": 4, "status": "completed", "completed_at": "2025-01-27T12:30:00Z", "estimated_hours": 2, "side": "frontend_only", "files_to_modify": [ "apps/web/src/types/user.ts", "apps/web/src/types/index.ts", "apps/web/src/features/auth/types/index.ts" ], "implementation_steps": [ "Rechercher toutes les occurrences de 'id: number' dans types user", "Remplacer par 'id: string'", "Vérifier tous les composants qui comparent user.id", "Mettre à jour les comparaisons: userId === user.id (pas ===)", "Ajouter commentaire: // UUID from backend - always string", "Compiler TypeScript pour trouver les erreurs restantes" ], "acceptance_criteria": [ "User.id est string dans tous les fichiers types", "Aucune erreur TypeScript liée à User.id", "Comparaisons fonctionnent correctement" ], "dependencies": [], "blocks": ["INT-TYPE-002"], "verification_command": "cd apps/web && npm run type-check" }, { "id": "INT-TYPE-002", "category": "INT-TYPE", "title": "Standardize Track.id to string everywhere", "description": "Même problème que User.id - Track.id doit être string (UUID) partout.", "priority": "P1", "priority_rank": 5, "status": "completed", "completed_at": "2025-01-27T12:45:00Z", "estimated_hours": 1.5, "side": "frontend_only", "files_to_modify": [ "apps/web/src/types/track.ts", "apps/web/src/features/tracks/types/index.ts" ], "implementation_steps": [ "Rechercher 'id: number' dans types track", "Remplacer par 'id: string'", "Vérifier composants qui utilisent track.id", "Compiler TypeScript" ], "acceptance_criteria": [ "Track.id est string dans tous les fichiers", "Aucune erreur TypeScript" ], "dependencies": ["INT-TYPE-001"], "blocks": [], "verification_command": "cd apps/web && npm run type-check" }, { "id": "INT-TYPE-003", "category": "INT-TYPE", "title": "Standardize Playlist.id to string everywhere", "description": "Playlist.id doit être string (UUID) partout.", "priority": "P1", "priority_rank": 6, "status": "completed", "completed_at": "2025-01-27T13:00:00Z", "estimated_hours": 1.5, "side": "frontend_only", "files_to_modify": [ "apps/web/src/types/playlist.ts", "apps/web/src/features/playlists/types/index.ts" ], "implementation_steps": [ "Rechercher 'id: number' dans types playlist", "Remplacer par 'id: string'", "Compiler TypeScript" ], "acceptance_criteria": [ "Playlist.id est string partout", "Aucune erreur TypeScript" ], "dependencies": ["INT-TYPE-001"], "blocks": [], "verification_command": "cd apps/web && npm run type-check" }, { "id": "INT-TYPE-004", "category": "INT-TYPE", "title": "Create TrackStatus enum aligned with backend", "description": "Track.status utilise des strings arbitraires. Créer un enum aligné avec le backend.", "priority": "P1", "priority_rank": 7, "status": "completed", "completed_at": "2025-01-27T13:15:00Z", "estimated_hours": 1, "side": "both_sides", "files_to_modify": [ "veza-backend-api/internal/models/track.go", "apps/web/src/types/track.ts", "apps/web/src/features/tracks/types/index.ts" ], "implementation_steps": [ "Backend: Vérifier les valeurs possibles de track.status dans models/track.go", "Frontend: Créer enum TrackStatus avec mêmes valeurs", "Frontend: Utiliser enum dans type Track", "Ajouter validation Zod si utilisé" ], "acceptance_criteria": [ "Enum TrackStatus créé avec valeurs: 'draft', 'processing', 'ready', 'error'", "Type Track utilise TrackStatus au lieu de string", "Backend et frontend utilisent mêmes valeurs" ], "dependencies": ["INT-TYPE-002"], "blocks": [], "verification_command": "grep -r 'status' apps/web/src/types/track.ts" }, { "id": "INT-TYPE-005", "category": "INT-TYPE", "title": "Create PlaylistVisibility enum aligned with backend", "description": "Playlist.visibility doit utiliser un enum aligné avec le backend.", "priority": "P1", "priority_rank": 8, "status": "completed", "completed_at": "2025-01-27T13:30:00Z", "estimated_hours": 1, "side": "both_sides", "files_to_modify": [ "veza-backend-api/internal/models/playlist.go", "apps/web/src/types/playlist.ts" ], "implementation_steps": [ "Backend: Vérifier valeurs de visibility", "Frontend: Créer enum PlaylistVisibility", "Utiliser dans type Playlist" ], "acceptance_criteria": [ "Enum créé avec valeurs: 'public', 'private', 'unlisted'", "Type Playlist utilise l'enum" ], "dependencies": ["INT-TYPE-003"], "blocks": [], "verification_command": "grep -r 'visibility' apps/web/src/types/playlist.ts" }, { "id": "INT-TYPE-006", "category": "INT-TYPE", "title": "Complete ApiError interface with all backend fields", "description": "L'interface ApiError frontend est incomplète par rapport aux erreurs retournées par le backend.", "priority": "P1", "priority_rank": 9, "status": "completed", "completed_at": "2025-01-27T13:45:00Z", "estimated_hours": 1.5, "side": "frontend_only", "files_to_modify": [ "apps/web/src/types/api.ts", "apps/web/src/services/api/client.ts" ], "implementation_steps": [ "Analyser le format d'erreur backend dans handlers/errors.go", "Mettre à jour interface ApiError avec tous les champs", "Ajouter: code, message, details, field_errors, request_id", "Mettre à jour parseApiError() dans client.ts" ], "acceptance_criteria": [ "ApiError contient: code, message, details?, field_errors?, request_id?", "parseApiError gère tous les formats d'erreur backend", "Erreurs de validation affichent les champs concernés" ], "dependencies": [], "blocks": [], "verification_command": "grep -A 10 'interface ApiError' apps/web/src/types/api.ts" }, { "id": "INT-TYPE-007", "category": "INT-TYPE", "title": "Create PaginatedResponse generic type", "description": "Créer un type générique pour les réponses paginées aligné avec le format backend.", "priority": "P1", "priority_rank": 10, "status": "completed", "completed_at": "2025-01-27T14:00:00Z", "estimated_hours": 1, "side": "frontend_only", "files_to_modify": [ "apps/web/src/types/api.ts" ], "implementation_steps": [ "Analyser format de pagination backend (page, limit, total, items)", "Créer type générique PaginatedResponse", "Utiliser dans tous les services qui retournent des listes" ], "acceptance_criteria": [ "PaginatedResponse créé avec: items: T[], total: number, page: number, limit: number, has_more: boolean", "Utilisé dans tracks, playlists, users list endpoints" ], "dependencies": [], "blocks": [], "verification_command": "grep -r 'PaginatedResponse' apps/web/src/" }, { "id": "INT-TYPE-008", "category": "INT-TYPE", "title": "Validate AuthResponse matches backend exactly", "description": "Vérifier que AuthResponse correspond exactement à la réponse de /auth/login.", "priority": "P1", "priority_rank": 11, "status": "completed", "completed_at": "2025-01-27T14:15:00Z", "estimated_hours": 1, "side": "frontend_only", "files_to_modify": [ "apps/web/src/features/auth/types/index.ts" ], "implementation_steps": [ "Comparer AuthResponse avec handlers/auth.go LoginResponse", "Vérifier: access_token, refresh_token, expires_in, token_type, user", "S'assurer que l'interceptor unwrap correctement" ], "acceptance_criteria": [ "AuthResponse match exactement le format backend", "Login flow fonctionne sans erreur de type" ], "dependencies": ["INT-TYPE-001"], "blocks": [], "verification_command": "grep -A 15 'interface AuthResponse' apps/web/src/features/auth/types/index.ts" }, { "id": "INT-API-001", "category": "INT-API", "title": "Remove duplicate API client (lib/apiClient.ts)", "description": "Il existe potentiellement deux clients API. Garder uniquement services/api/client.ts.", "priority": "P1", "priority_rank": 12, "status": "completed", "completed_at": "2025-01-27T14:30:00Z", "estimated_hours": 2, "side": "frontend_only", "files_to_modify": [ "apps/web/src/lib/apiClient.ts", "apps/web/src/services/api/client.ts" ], "implementation_steps": [ "Vérifier si lib/apiClient.ts existe", "Si oui, lister tous les fichiers qui l'importent", "Migrer ces imports vers services/api/client.ts", "Supprimer lib/apiClient.ts", "Compiler et tester" ], "acceptance_criteria": [ "Un seul client API: services/api/client.ts", "Aucun import de lib/apiClient", "Application fonctionne correctement" ], "dependencies": [], "blocks": ["INT-CLEANUP-002"], "verification_command": "grep -r 'lib/apiClient' apps/web/src/" }, { "id": "INT-API-002", "category": "INT-API", "title": "Verify response unwrapping in interceptor", "description": "Vérifier que l'interceptor unwrap correctement {success: true, data: ...} → data.", "priority": "P1", "priority_rank": 13, "status": "completed", "completed_at": "2025-01-27T14:45:00Z", "estimated_hours": 1, "side": "frontend_only", "files_to_modify": [ "apps/web/src/services/api/client.ts" ], "implementation_steps": [ "Ouvrir services/api/client.ts", "Vérifier l'interceptor de réponse", "S'assurer qu'il détecte {success, data} et retourne data", "S'assurer qu'il gère {success: false, error} correctement", "Ajouter logs de debug si nécessaire" ], "acceptance_criteria": [ "Interceptor unwrap {success: true, data: X} → X", "Interceptor throw sur {success: false, error: E}", "Services reçoivent directement les données sans wrapper" ], "dependencies": ["INT-API-001"], "blocks": [], "verification_command": "grep -A 30 'response interceptor' apps/web/src/services/api/client.ts" }, { "id": "INT-API-003", "category": "INT-API", "title": "Standardize error handling across all services", "description": "Tous les services API doivent utiliser la même méthode de gestion d'erreurs.", "priority": "P1", "priority_rank": 14, "status": "completed", "completed_at": "2025-01-27T15:00:00Z", "estimated_hours": 2, "side": "frontend_only", "files_to_modify": [ "apps/web/src/services/api/client.ts", "apps/web/src/features/*/api/*.ts", "apps/web/src/features/*/services/*.ts" ], "implementation_steps": [ "Créer fonction handleApiError() centralisée", "Standardiser le format d'erreur retourné", "Mettre à jour tous les services pour utiliser cette fonction", "Ajouter types pour les erreurs" ], "acceptance_criteria": [ "Une seule fonction handleApiError", "Tous les services l'utilisent", "Erreurs ont un format prévisible" ], "dependencies": ["INT-TYPE-006"], "blocks": [], "verification_command": "grep -r 'handleApiError' apps/web/src/" }, { "id": "INT-API-004", "category": "INT-API", "title": "Add request timeout configuration per endpoint type", "description": "Configurer des timeouts différents pour uploads (long) vs requêtes normales.", "priority": "P2", "priority_rank": 15, "status": "completed", "completed_at": "2025-01-27T15:15:00Z", "estimated_hours": 1, "side": "frontend_only", "files_to_modify": [ "apps/web/src/services/api/client.ts", "apps/web/src/features/tracks/api/uploadApi.ts" ], "implementation_steps": [ "Définir timeouts: default 10s, upload 5min, long-polling 30s", "Permettre override par requête", "Appliquer timeout long pour uploads" ], "acceptance_criteria": [ "Timeout par défaut: 10s", "Upload timeout: 5min", "Configurable par requête" ], "dependencies": [], "blocks": [], "verification_command": "grep -r 'timeout' apps/web/src/services/api/" }, { "id": "INT-API-005", "category": "INT-API", "title": "Add retry logic for 429 rate limit responses", "description": "Détecter les réponses 429 et retry avec backoff basé sur Retry-After header.", "priority": "P2", "priority_rank": 16, "status": "completed", "completed_at": "2025-01-27T15:30:00Z", "estimated_hours": 1.5, "side": "frontend_only", "files_to_modify": [ "apps/web/src/services/api/client.ts" ], "implementation_steps": [ "Ajouter 429 aux status codes retryables", "Lire header Retry-After si présent", "Utiliser Retry-After comme délai, sinon exponential backoff", "Limiter à 3 retries pour 429" ], "acceptance_criteria": [ "429 déclenche retry automatique", "Respecte Retry-After header", "Max 3 retries pour rate limit" ], "dependencies": ["INT-API-002"], "blocks": [], "verification_command": "grep -r 'retryableStatusCodes' apps/web/src/services/api/" }, { "id": "INT-AUTH-002", "category": "INT-AUTH", "title": "Remove duplicate auth store if exists", "description": "Vérifier s'il y a deux stores auth et garder uniquement features/auth/store/authStore.ts.", "priority": "P1", "priority_rank": 17, "status": "completed", "completed_at": "2025-01-27T15:45:00Z", "estimated_hours": 2, "side": "frontend_only", "files_to_modify": [ "apps/web/src/stores/auth.ts", "apps/web/src/features/auth/store/authStore.ts" ], "implementation_steps": [ "Vérifier si stores/auth.ts existe", "Comparer avec features/auth/store/authStore.ts", "Migrer les imports vers le nouveau store", "Supprimer l'ancien store", "Tester auth flow complet" ], "acceptance_criteria": [ "Un seul store auth", "Tous les composants utilisent le même store", "Auth flow fonctionne" ], "dependencies": [], "blocks": ["INT-CLEANUP-003"], "verification_command": "grep -r 'from.*stores/auth' apps/web/src/" }, { "id": "INT-AUTH-003", "category": "INT-AUTH", "title": "Verify refresh token flow handles edge cases", "description": "S'assurer que le refresh token gère: expiration, révocation, erreurs réseau.", "priority": "P1", "priority_rank": 18, "status": "completed", "completed_at": "2025-01-27T16:00:00Z", "estimated_hours": 2, "side": "frontend_only", "files_to_modify": [ "apps/web/src/services/api/client.ts", "apps/web/src/features/auth/store/authStore.ts" ], "implementation_steps": [ "Vérifier que 401 sur /auth/refresh redirige vers login", "Vérifier que les requêtes en attente sont rejouées après refresh", "Vérifier protection contre boucle infinie", "Ajouter logs pour debug" ], "acceptance_criteria": [ "401 sur refresh → logout + redirect login", "Requêtes en queue rejouées après refresh réussi", "Pas de boucle infinie possible" ], "dependencies": ["INT-AUTH-002"], "blocks": [], "verification_command": "grep -A 20 'refreshToken' apps/web/src/services/api/client.ts" }, { "id": "INT-AUTH-004", "category": "INT-AUTH", "title": "Add token expiration pre-check", "description": "Vérifier l'expiration du token AVANT d'envoyer une requête pour éviter 401 inutiles.", "priority": "P2", "priority_rank": 19, "status": "completed", "completed_at": "2025-01-27T16:15:00Z", "estimated_hours": 1.5, "side": "frontend_only", "files_to_modify": [ "apps/web/src/services/api/client.ts", "apps/web/src/services/tokenStorage.ts" ], "implementation_steps": [ "Décoder le JWT pour extraire exp", "Comparer avec Date.now() - marge de 60s", "Si proche expiration, refresh proactivement", "Ajouter fonction isTokenExpiringSoon()" ], "acceptance_criteria": [ "Token refresh proactif 60s avant expiration", "Moins de requêtes 401", "Performance améliorée" ], "dependencies": ["INT-AUTH-003"], "blocks": [], "verification_command": "grep -r 'isTokenExpiring' apps/web/src/" }, { "id": "INT-CLEANUP-001", "category": "INT-CLEANUP", "title": "Remove all unused API service files", "description": "Identifier et supprimer les fichiers de services API non utilisés.", "priority": "P2", "priority_rank": 20, "status": "completed", "completed_at": "2025-01-27T16:30:00Z", "estimated_hours": 1, "side": "frontend_only", "files_to_modify": [ "apps/web/src/services/*.ts" ], "implementation_steps": [ "Lister tous les fichiers dans services/", "Pour chaque fichier, vérifier s'il est importé quelque part", "Supprimer les fichiers non importés", "Commit avec liste des fichiers supprimés" ], "acceptance_criteria": [ "Tous les fichiers services/ sont utilisés", "Aucun code mort" ], "dependencies": ["INT-API-001"], "blocks": [], "verification_command": "find apps/web/src/services -name '*.ts' -exec basename {} \\;" }, { "id": "INT-CLEANUP-002", "category": "INT-CLEANUP", "title": "Consolidate type definitions in single location", "description": "Tous les types partagés doivent être dans types/ et non dupliqués dans features/.", "priority": "P2", "priority_rank": 21, "status": "completed", "completed_at": "2025-01-27T16:45:00Z", "estimated_hours": 2, "side": "frontend_only", "files_to_modify": [ "apps/web/src/types/*.ts", "apps/web/src/features/*/types/*.ts" ], "implementation_steps": [ "Identifier les types dupliqués entre types/ et features/*/types/", "Garder une seule définition dans types/", "Mettre à jour les imports", "Features peuvent avoir des types spécifiques, pas des duplications" ], "acceptance_criteria": [ "Pas de duplication de types", "Types partagés dans types/", "Types feature-specific dans features/*/types/" ], "dependencies": ["INT-TYPE-001"], "blocks": [], "verification_command": "grep -r 'interface User' apps/web/src/ | wc -l" }, { "id": "INT-CLEANUP-003", "category": "INT-CLEANUP", "title": "Remove legacy hooks using old API client", "description": "Supprimer les hooks qui utilisent encore l'ancien client API.", "priority": "P2", "priority_rank": 22, "status": "completed", "completed_at": "2025-01-27T17:00:00Z", "estimated_hours": 1.5, "side": "frontend_only", "files_to_modify": [ "apps/web/src/hooks/api/*.ts" ], "implementation_steps": [ "Identifier hooks utilisant l'ancien client", "Migrer vers nouveau client ou supprimer si inutilisés", "Mettre à jour composants qui les utilisent" ], "acceptance_criteria": [ "Tous les hooks utilisent le nouveau client API", "Aucun import de l'ancien client" ], "dependencies": ["INT-API-001", "INT-AUTH-002"], "blocks": [], "verification_command": "grep -r 'lib/apiClient' apps/web/src/hooks/" }, { "id": "INT-CLEANUP-004", "category": "INT-CLEANUP", "title": "Add barrel exports for clean imports", "description": "Créer des fichiers index.ts pour faciliter les imports.", "priority": "P3", "priority_rank": 23, "status": "completed", "completed_at": "2025-01-27T17:15:00Z", "estimated_hours": 1, "side": "frontend_only", "files_to_modify": [ "apps/web/src/types/index.ts", "apps/web/src/services/api/index.ts" ], "implementation_steps": [ "Créer index.ts dans types/ qui exporte tout", "Créer index.ts dans services/api/ qui exporte client et services", "Mettre à jour imports pour utiliser barrels" ], "acceptance_criteria": [ "Import possible via '@/types' et '@/services/api'", "Imports plus propres dans le code" ], "dependencies": ["INT-CLEANUP-002"], "blocks": [], "verification_command": "cat apps/web/src/types/index.ts" }, { "id": "INT-ENDPOINT-001", "category": "INT-ENDPOINT", "title": "Add frontend service for GET /api/v1/sessions/stats", "description": "Le backend expose cet endpoint mais le frontend ne l'utilise pas.", "priority": "P2", "priority_rank": 24, "status": "completed", "completed_at": "2025-01-27T17:30:00Z", "estimated_hours": 1, "side": "frontend_only", "files_to_modify": [ "apps/web/src/features/sessions/api/sessionsApi.ts" ], "implementation_steps": [ "Créer fonction getSessionStats() dans sessionsApi.ts", "Définir type SessionStats aligné avec backend", "Ajouter hook useSessionStats() si nécessaire" ], "acceptance_criteria": [ "sessionsApi.getSessionStats() existe", "Type SessionStats défini", "Endpoint accessible depuis frontend" ], "dependencies": [], "blocks": [], "verification_command": "grep -r 'sessions/stats' apps/web/src/" }, { "id": "INT-ENDPOINT-002", "category": "INT-ENDPOINT", "title": "Implement backend GET /api/v1/users/search", "description": "Le frontend appelle cet endpoint mais il n'existe pas côté backend.", "priority": "P2", "priority_rank": 25, "status": "completed", "completed_at": "2025-01-27T17:45:00Z", "estimated_hours": 2, "side": "backend_only", "files_to_modify": [ "veza-backend-api/internal/api/router.go", "veza-backend-api/internal/handlers/users.go" ], "implementation_steps": [ "Ajouter route GET /api/v1/users/search dans router.go", "Créer handler SearchUsers dans handlers/users.go", "Paramètres: q (query), limit, offset", "Retourner liste d'utilisateurs paginée" ], "acceptance_criteria": [ "Endpoint GET /api/v1/users/search existe", "Recherche par username et display_name", "Pagination fonctionnelle" ], "dependencies": [], "blocks": [], "verification_command": "curl 'http://localhost:8080/api/v1/users/search?q=test'" }, { "id": "INT-ENDPOINT-003", "category": "INT-ENDPOINT", "title": "Implement backend GET /api/v1/tracks/search", "description": "Le frontend appelle cet endpoint mais il n'existe pas côté backend.", "priority": "P2", "priority_rank": 26, "status": "completed", "completed_at": "2025-01-27T18:00:00Z", "estimated_hours": 2, "side": "backend_only", "files_to_modify": [ "veza-backend-api/internal/api/router.go", "veza-backend-api/internal/handlers/tracks.go" ], "implementation_steps": [ "Ajouter route GET /api/v1/tracks/search dans router.go", "Créer handler SearchTracks dans handlers/tracks.go", "Paramètres: q (query), genre, limit, offset", "Retourner liste de tracks paginée" ], "acceptance_criteria": [ "Endpoint GET /api/v1/tracks/search existe", "Recherche par title et genre", "Pagination fonctionnelle" ], "dependencies": [], "blocks": [], "verification_command": "curl 'http://localhost:8080/api/v1/tracks/search?q=test'" }, { "id": "INT-ENDPOINT-004", "category": "INT-ENDPOINT", "title": "Implement backend GET /api/v1/playlists/search", "description": "Le frontend appelle cet endpoint mais il n'existe pas côté backend.", "priority": "P2", "priority_rank": 27, "status": "completed", "completed_at": "2025-01-27T18:15:00Z", "estimated_hours": 2, "side": "backend_only", "files_to_modify": [ "veza-backend-api/internal/api/router.go", "veza-backend-api/internal/handlers/playlists.go" ], "implementation_steps": [ "Ajouter route GET /api/v1/playlists/search dans router.go", "Créer handler SearchPlaylists dans handlers/playlists.go", "Paramètres: q (query), visibility, limit, offset", "Retourner liste de playlists paginée" ], "acceptance_criteria": [ "Endpoint GET /api/v1/playlists/search existe", "Recherche par name et description", "Filtre par visibility" ], "dependencies": [], "blocks": [], "verification_command": "curl 'http://localhost:8080/api/v1/playlists/search?q=test'" }, { "id": "INT-ENDPOINT-005", "category": "INT-ENDPOINT", "title": "Implement backend playlist collaborator endpoints", "description": "Les endpoints de gestion des collaborateurs de playlist sont appelés par le frontend mais n'existent pas.", "priority": "P2", "priority_rank": 28, "status": "completed", "completed_at": "2025-01-27T18:30:00Z", "estimated_hours": 3, "side": "backend_only", "files_to_modify": [ "veza-backend-api/internal/api/router.go", "veza-backend-api/internal/handlers/playlists.go" ], "implementation_steps": [ "Ajouter POST /api/v1/playlists/:id/collaborators", "Ajouter DELETE /api/v1/playlists/:id/collaborators/:userId", "Ajouter PUT /api/v1/playlists/:id/collaborators/:userId", "Implémenter handlers correspondants", "Ajouter validation des permissions" ], "acceptance_criteria": [ "3 endpoints collaborators fonctionnels", "Seul le owner peut gérer les collaborateurs", "Collaborators peuvent être ajoutés/supprimés/mis à jour" ], "dependencies": [], "blocks": [], "verification_command": "grep -r 'collaborators' veza-backend-api/internal/api/router.go" }, { "id": "INT-ENDPOINT-006", "category": "INT-ENDPOINT", "title": "Implement backend conversation management endpoints", "description": "DELETE /api/v1/conversations/:id et endpoints participants manquants.", "priority": "P3", "priority_rank": 29, "status": "completed", "completed_at": "2025-01-27T18:45:00Z", "estimated_hours": 2, "side": "backend_only", "files_to_modify": [ "veza-backend-api/internal/api/router.go", "veza-backend-api/internal/handlers/conversations.go" ], "implementation_steps": [ "Ajouter DELETE /api/v1/conversations/:id", "Ajouter POST /api/v1/conversations/:id/participants", "Ajouter DELETE /api/v1/conversations/:id/participants/:userId", "Implémenter handlers" ], "acceptance_criteria": [ "Conversations peuvent être supprimées par le créateur", "Participants peuvent être ajoutés/supprimés" ], "dependencies": [], "blocks": [], "verification_command": "grep -r 'conversations' veza-backend-api/internal/api/router.go" }, { "id": "INT-TEST-001", "category": "INT-TEST", "title": "Create E2E test for complete auth flow", "description": "Test Playwright pour: register → verify email → login → refresh → logout.", "priority": "P2", "priority_rank": 30, "status": "completed", "completed_at": "2025-01-27T19:00:00Z", "estimated_hours": 3, "side": "frontend_only", "files_to_modify": [ "apps/web/e2e/auth-flow.spec.ts" ], "implementation_steps": [ "Créer fichier e2e/auth-flow.spec.ts", "Test: register avec email valide", "Test: login et vérification du token", "Test: refresh token automatique", "Test: logout et redirection" ], "acceptance_criteria": [ "Tests couvrent le flow auth complet", "Tests passent avec backend réel", "CI/CD peut exécuter ces tests" ], "dependencies": ["INT-CORS-001", "INT-AUTH-001"], "blocks": [], "verification_command": "cd apps/web && npx playwright test auth-flow" }, { "id": "INT-TEST-002", "category": "INT-TEST", "title": "Create E2E test for CRUD operations", "description": "Test Playwright pour: create track → update → delete, create playlist → add tracks → delete.", "priority": "P2", "priority_rank": 31, "status": "completed", "completed_at": "2025-01-27T19:15:00Z", "estimated_hours": 3, "side": "frontend_only", "files_to_modify": [ "apps/web/e2e/crud-operations.spec.ts" ], "implementation_steps": [ "Créer fichier e2e/crud-operations.spec.ts", "Test: CRUD complet sur tracks", "Test: CRUD complet sur playlists", "Test: ajout de tracks à une playlist" ], "acceptance_criteria": [ "Tests couvrent CRUD tracks et playlists", "Tests passent avec backend réel", "Données de test nettoyées après exécution" ], "dependencies": ["INT-TEST-001"], "blocks": [], "verification_command": "cd apps/web && npx playwright test crud-operations" }, { "id": "INT-DOC-001", "category": "INT-DOC", "title": "Generate OpenAPI/Swagger documentation", "description": "Générer une documentation OpenAPI complète pour tous les endpoints.", "priority": "P3", "priority_rank": 32, "status": "completed", "completed_at": "2025-01-27T19:30:00Z", "estimated_hours": 2, "side": "backend_only", "files_to_modify": [ "veza-backend-api/docs/swagger.yaml", "veza-backend-api/cmd/server/main.go" ], "implementation_steps": [ "Installer swag: go install github.com/swaggo/swag/cmd/swag@latest", "Ajouter annotations Swagger aux handlers", "Générer docs: swag init -g cmd/server/main.go", "Exposer /docs endpoint avec Swagger UI" ], "acceptance_criteria": [ "swagger.yaml généré", "Swagger UI accessible à /docs", "Tous les endpoints documentés" ], "dependencies": [], "blocks": [], "verification_command": "curl http://localhost:8080/docs" } ], "integration_matrix": { "endpoints_analysis": [], "type_mismatches": [], "duplicate_code": [], "missing_frontend_calls": [], "missing_backend_routes": [] }, "risk_register": [ { "id": "RISK-INT-001", "risk": "CORS bloque toutes les requêtes en production si CORS_ALLOWED_ORIGINS vide", "severity": "critical", "probability": "certain", "impact": "Application totalement inaccessible", "mitigation_tasks": ["INT-CORS-001", "INT-CORS-002"], "owner": "backend" }, { "id": "RISK-INT-002", "risk": "CSRF désactivé en production si Redis indisponible", "severity": "critical", "probability": "possible", "impact": "Vulnérabilité de sécurité majeure", "mitigation_tasks": ["INT-AUTH-001"], "owner": "backend" }, { "id": "RISK-INT-003", "risk": "Incohérences de types causent des erreurs runtime imprévisibles", "severity": "high", "probability": "certain", "impact": "Bugs intermittents difficiles à débugger", "mitigation_tasks": ["INT-TYPE-001", "INT-TYPE-002", "INT-TYPE-003"], "owner": "frontend" }, { "id": "RISK-INT-004", "risk": "Duplication de code rend la maintenance difficile", "severity": "medium", "probability": "certain", "impact": "Bugs par désynchronisation, confusion des développeurs", "mitigation_tasks": ["INT-API-001", "INT-AUTH-002", "INT-CLEANUP-001"], "owner": "frontend" } ], "validation_checklist": { "pre_deployment": [ {"check": "CORS_ALLOWED_ORIGINS configuré pour production", "task_id": "INT-CORS-001", "verified": false}, {"check": "CSRF activé avec Redis", "task_id": "INT-AUTH-001", "verified": false}, {"check": "Tous les IDs sont des strings (UUID)", "task_ids": ["INT-TYPE-001", "INT-TYPE-002", "INT-TYPE-003"], "verified": false}, {"check": "Un seul client API utilisé", "task_id": "INT-API-001", "verified": false}, {"check": "Un seul store auth utilisé", "task_id": "INT-AUTH-002", "verified": false} ], "integration_tests": [ {"test": "Auth flow complet E2E", "task_id": "INT-TEST-001", "passed": false}, {"test": "CRUD operations E2E", "task_id": "INT-TEST-002", "passed": false} ] }, "progress_tracking": { "total_tasks": 32, "completed": 32, "in_progress": 0, "todo": 0, "blocked": 0, "completion_percentage": 100, "last_updated": "2025-01-27T19:30:00Z", "estimated_completion_date": null, "estimated_hours_remaining": 0.0 } }