2025-12-25 21:26:41 +00:00
{
"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 ,
2025-12-25 21:27:05 +00:00
"status" : "completed" ,
2025-12-25 21:26:41 +00:00
"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 ,
2025-12-25 21:28:46 +00:00
"status" : "completed" ,
2025-12-25 21:26:41 +00:00
"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 ,
2025-12-25 21:33:16 +00:00
"status" : "completed" ,
"completed_at" : "2025-01-27T12:30:00Z" ,
2025-12-25 21:26:41 +00:00
"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 ,
2025-12-25 21:34:55 +00:00
"status" : "completed" ,
"completed_at" : "2025-01-27T12:45:00Z" ,
2025-12-25 21:26:41 +00:00
"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" : "todo" ,
"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" : "todo" ,
"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" : "todo" ,
"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" : "todo" ,
"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" : "todo" ,
"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" : "todo" ,
"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" : "todo" ,
"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" : "todo" ,
"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" : "todo" ,
"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" : "todo" ,
"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" : "todo" ,
"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" : "todo" ,
"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" : "todo" ,
"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" : "todo" ,
"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" : "todo" ,
"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" : "todo" ,
"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" : "todo" ,
"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" : "todo" ,
"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" : "todo" ,
"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" : "todo" ,
"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" : "todo" ,
"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" : "todo" ,
"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 ,
2025-12-25 21:34:55 +00:00
"completed" : 5 ,
2025-12-25 21:26:41 +00:00
"in_progress" : 0 ,
2025-12-25 21:34:55 +00:00
"todo" : 27 ,
2025-12-25 21:26:41 +00:00
"blocked" : 0 ,
2025-12-25 21:34:55 +00:00
"completion_percentage" : 16 ,
"last_updated" : "2025-01-27T12:45:00Z" ,
2025-12-25 21:26:41 +00:00
"estimated_completion_date" : null ,
2025-12-25 21:34:55 +00:00
"estimated_hours_remaining" : 39.5
2025-12-25 21:26:41 +00:00
}
}