veza/VEZA_INTEGRATION_PERFECTION_TODOLIST_TEMPLATE.json

1121 lines
42 KiB
JSON

{
"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<T>",
"Utiliser dans tous les services qui retournent des listes"
],
"acceptance_criteria": [
"PaginatedResponse<T> 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": "todo",
"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": "todo",
"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": "todo",
"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": "todo",
"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": "todo",
"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": 27,
"in_progress": 0,
"todo": 5,
"blocked": 0,
"completion_percentage": 84,
"last_updated": "2025-01-27T18:15:00Z",
"estimated_completion_date": null,
"estimated_hours_remaining": 6.5
}
}