veza/apps/web/AUDIT_TECHNIQUE_EXHAUSTIF.md

1547 lines
43 KiB
Markdown

# Audit Technique Exhaustif — Module Frontend Veza (`apps/web`)
**Date**: 2025-12-12
**Auditeur**: Auto (Cursor AI)
**Module**: Frontend React (Vite + TypeScript)
**Version**: 1.0.0
---
## Table des matières
1. [Module Overview + Runbook](#module-overview--runbook)
2. [Health/Build/Test Status](#healthbuildtest-status)
3. [Security Findings](#security-findings)
4. [Observability & Prod Readiness Gaps](#observability--prod-readiness-gaps)
5. [Performance Notes](#performance-notes)
6. [Issue List Priorisée (P0→P3)](#issue-list-priorisée-p0p3)
7. [Execution Plan](#execution-plan)
---
# Module Overview + Runbook
## But du module
Le module `apps/web` est l'application frontend React de la plateforme Veza, une plateforme audio collaborative avec streaming, chat en temps réel, bibliothèque musicale, playlists collaboratives, et marketplace.
**Rôle exact** : Interface utilisateur web (SPA) pour :
- Authentification (JWT, 2FA, OAuth)
- Streaming audio (HLS.js, WebRTC)
- Chat en temps réel (WebSocket)
- Gestion de bibliothèque et playlists
- Marketplace de tracks
- Profils utilisateurs et collaboration
## Entrées / Sorties
### APIs exposées (consommées)
**HTTP REST** :
- Base URL : `VITE_API_BASE_URL` (défaut: `http://localhost:8080/api/v1`)
- Client : Axios (`src/services/api.ts`, `src/services/api/client.ts`)
- Endpoints principaux :
- `/auth/*` : login, register, refresh, logout, me
- `/users/*` : CRUD utilisateurs
- `/tracks/*` : CRUD tracks, upload, download
- `/library/*` : bibliothèque utilisateur
- `/conversations/*`, `/messages/*` : chat
- `/playlists/*` : playlists
**WebSocket** :
- URL : `VITE_WS_BASE_URL` (défaut: `ws://localhost:8081/ws`)
- Service : `src/services/websocket.ts`
- Protocole : JSON messages avec types (`join_conversation`, `send_message`, `typing`, etc.)
**Streaming** :
- URL : `VITE_STREAM_URL` (défaut: `http://localhost:8082`)
- Protocole : HLS.js pour audio streaming
### Formats
- **JSON** : Toutes les APIs REST et WebSocket
- **FormData** : Upload de fichiers (tracks, avatars)
- **Schémas Zod** : Validation côté client (`src/services/api.ts`)
## Dépendances internes
- **Stores Zustand** : `src/features/auth/store/authStore.ts` (moved from stores/auth.ts), `src/features/chat/store/chatStore.ts` (moved from stores/chat.ts), `src/stores/library.ts`, `src/stores/ui.ts`, `src/stores/cartStore.ts`
- **Types partagés** : `src/types/api.ts`, `src/types/index.ts`
- **Utils** : `src/utils/*` (sanitize, token-manager, validation, etc.)
- **Hooks** : `src/hooks/*` (useAuth, useDebounce, usePWA, etc.)
## Dépendances externes
- **Base de données** : Aucune (consomme API backend Go)
- **Redis** : Aucune (consomme via backend)
- **Storage** : `localStorage` (tokens), `sessionStorage` (refresh tokens), IndexedDB (PWA cache)
- **Services externes** : Pas de dépendances directes (tout via backend)
## Exécution
### Commandes de build/run/dev
```bash
# Dev
npm run dev # Vite dev server (port 3000)
npm run dev:lab # Dev avec vraie BDD (via scripts/start_lab.sh)
npm run dev:mocks # Dev avec MSW mocks
# Build
npm run build # Production build → dist/
# Tests
npm test # Vitest (unit)
npm run test:e2e # Playwright E2E
npm run test:e2e:mocks # E2E avec MSW
# Qualité
npm run typecheck # TypeScript check
npm run lint # ESLint
npm run format # Prettier
```
### Configuration
**Variables d'environnement** (validées via Zod dans `src/config/env.ts`) :
- `VITE_API_BASE_URL` : URL API REST
- `VITE_WS_BASE_URL` : URL WebSocket
- `VITE_STREAM_URL` : URL streaming
- `VITE_UPLOAD_URL` : URL upload
- `VITE_APP_NAME` : Nom de l'app
- `VITE_DEBUG` : Mode debug (boolean)
- `VITE_USE_MSW` : Activer MSW mocks
- `VITE_FCM_VAPID_KEY` : Push notifications (optionnel)
**Fichiers de config** :
- `vite.config.ts` : Configuration Vite (aliases, plugins, CSP)
- `tsconfig.json` : TypeScript config
- `vitest.config.ts` : Tests config
- `eslint.config.js` : ESLint config
- `nginx.conf` : Config production (Docker)
- `Dockerfile` : Build production
### Docker
```bash
docker build -t veza-frontend .
docker run -p 80:80 veza-frontend
```
## Points d'intégration
### Contrats d'API
**Backend Go** (`veza-backend-api`) :
- Format réponse : `{ success: true, data: {...} }` ou `{ error: "...", message: "..." }`
- Auth : JWT dans header `Authorization: Bearer <token>`
- IDs : Backend retourne `int64`, frontend attend `string` (conversion nécessaire)
**Chat Server Rust** (`veza-chat-server`) :
- WebSocket sur `/ws`
- Token JWT en query param : `?token=<access_token>`
- Messages JSON : `{ type: string, data: any }`
**Stream Server Rust** (`veza-stream-server`) :
- HLS streaming via HTTP
- WebSocket pour synchronisation playback
### Auth
- **JWT** : Access token (15min) + Refresh token (30/90 jours selon `remember_me`)
- **Storage** : `localStorage` (access), `sessionStorage`/cookie (refresh)
- **Refresh automatique** : Intercepteur Axios (`src/services/api/client.ts`)
- **2FA** : Supporté via `src/services/2fa-service.ts`
- **OAuth** : Supporté via `src/features/auth/hooks/useOAuthCallback.ts`
### Headers / CORS
- CORS géré par backend
- Headers de sécurité : CSP, X-Frame-Options, etc. (via `vite.config.ts` et `nginx.conf`)
### Schéma DB / UUID
- **IDs** : Backend retourne `int64`, frontend utilise `string` (conversion dans `src/services/api.ts`)
- **UUID** : Pas utilisé (IDs numériques)
---
# Health/Build/Test Status
## Build Status
### TypeScript Errors
**Total** : ~100+ erreurs TypeScript (voir `ts_real_errors.log`)
**Catégories principales** :
1. **Modules manquants** (P0) :
- `@/hooks/use-toast` : utilisé mais non exporté/existant
- `@/stores/player` : utilisé mais non existant
- `@/components/ui/progress` : utilisé mais non existant
- `@/hooks/useIntersectionObserver` : utilisé mais non existant
- `@/features/settings/pages/SettingsPage` : importé mais chemin incorrect
- `@/features/profile/pages/UserProfilePage` : importé mais chemin incorrect
- `@/features/roles/pages/RolesPage` : importé mais chemin incorrect
- `@/features/tracks/pages/TrackDetailPage` : importé mais chemin incorrect
- `@/features/playlists/routes` : importé mais chemin incorrect
- `@/pages/marketplace/MarketplaceHome` : importé mais chemin incorrect
2. **Types incorrects** (P1) :
- `Track` : propriétés manquantes (`url`, `cover_url`, `album`, `file_size`, `is_playing`, `progress`)
- `User` : propriétés manquantes (`userId`, `username`)
- `PaginatedResponse<Track>` : propriété `tracks` manquante
- `ChatPage` : export par défaut manquant (lazy loading)
3. **React Query v5 incompatibilité** (P1) :
- `onSuccess` déprécié (remplacé par `useEffect` + `data`)
- `keepPreviousData` déprécié (remplacé par `placeholderData: keepPreviousData`)
4. **Types implicites `any`** (P2) : ~20 occurrences
### ESLint Warnings
**Total** : ~50+ warnings
**Principaux** :
- `no-console` : 121 occurrences (devrait être supprimé en prod)
- `@typescript-eslint/no-explicit-any` : ~30 occurrences
- `@typescript-eslint/no-unused-vars` : 2 erreurs (variables non utilisées)
- `react-hooks/exhaustive-deps` : 1 warning
## Test Status
### Coverage
**Configuré** : 80% threshold (branches, functions, lines, statements)
**État actuel** : Non mesuré (pas de rapport de coverage visible)
### Tests unitaires (Vitest)
**Total** : 189 fichiers de test (`.test.ts`, `.test.tsx`)
**Résultats** :
- ✅ La plupart des tests passent
-**2 tests échouent** :
1. `src/features/playlists/hooks/usePlaylistTrack.test.tsx` : `useAddTrackToPlaylist` — argument manquant
2. `src/features/playlists/hooks/usePlaylistTrack.test.tsx` : `useRemoveTrackFromPlaylist` — fonction non exportée
**Problèmes identifiés** :
- MSW handlers manquants pour certains endpoints WebSocket
- Mocks incomplets pour certains services
### Tests E2E (Playwright)
**Config** : `playwright.config.ts`
**Tests** : `e2e/qa-audit.spec.ts`
**État** : Non exécuté dans cet audit
## Gestion des erreurs
### Points positifs
- ErrorBoundary React (`src/components/ErrorBoundary.tsx`)
- Intercepteurs Axios pour refresh token automatique
- Validation Zod pour les réponses API
### Points à améliorer
- Pas de centralisation des erreurs (chaque composant gère ses erreurs)
- Pas de logging structuré (console.log partout)
- Pas de retry automatique pour les requêtes échouées (sauf refresh token)
## Cohérence des conventions
### Naming
- ✅ Cohérent : PascalCase pour composants, camelCase pour fonctions
- ⚠️ Incohérences :
- `useAuthStore` vs `useAuth` (deux hooks différents)
- `apiService` vs `apiClient` (deux clients API différents)
- `websocketService` vs `wsService` (aliases, confusion)
### Structure dossiers
- ✅ Feature-based : `src/features/*`
- ✅ Séparation claire : components, hooks, services, stores
- ⚠️ Duplication : `src/services/api.ts` et `src/services/api/client.ts` (deux implémentations)
### Séparation couches
- ✅ Services séparés des composants
- ✅ Stores Zustand pour state management
- ⚠️ Logique métier parfois dans les composants (ex: `src/features/library/components/LibraryManager.tsx`)
---
# Security Findings
## Top 10 Risques Sécurité
### P0 — Critiques (Exploitables immédiatement)
#### 1. **Tokens JWT stockés en localStorage (XSS vulnérable)**
**Fichiers** :
- `src/services/api.ts` : lignes 105-121
- `src/services/api/auth.ts` : lignes 64-68, 124-126
- `src/utils/token-manager.ts` : lignes 15-34, 40-54
**Problème** :
```typescript
// src/services/api.ts:105-106
private getAccessToken(): string | null {
return localStorage.getItem('access_token');
}
```
Les tokens JWT sont stockés dans `localStorage`, vulnérables aux attaques XSS. Un script malveillant injecté peut voler les tokens.
**Impact** : Vol de session, accès non autorisé aux comptes utilisateurs
**Fix minimal** :
- Option 1 : Utiliser `httpOnly` cookies (nécessite backend)
- Option 2 : Utiliser `sessionStorage` + rotation fréquente des tokens
- Option 3 : Implémenter un service worker pour isoler les tokens
**Plan de validation** :
- Test XSS : Injecter `<script>alert(localStorage.getItem('access_token'))</script>`
- Vérifier que les tokens ne sont pas accessibles via XSS
**Effet de bord** : Nécessite modification backend pour cookies httpOnly
**Effort** : M (2-4h)
---
#### 2. **CSP non-strict en production (`unsafe-inline`, `unsafe-eval`)**
**Fichiers** :
- `vite.config.ts` : lignes 62-75
**Problème** :
```typescript
'script-src': [
"'self'",
"'unsafe-inline' 'unsafe-eval' blob:", // ⚠️ Dangereux
],
'style-src': [
"'self'",
"'unsafe-inline'", // ⚠️ Nécessaire pour Tailwind mais risqué
],
```
La CSP en dev contient `unsafe-inline` et `unsafe-eval`, permettant l'exécution de scripts inline (XSS).
**Impact** : Attaques XSS facilitées
**Fix minimal** :
- Utiliser des nonces CSP pour les scripts inline (déjà partiellement implémenté dans `src/utils/csp.ts`)
- Supprimer `unsafe-eval` en production
- Utiliser `nonce-` pour les styles inline si possible
**Plan de validation** :
- Tester avec CSP strict en production
- Vérifier que tous les scripts ont des nonces
**Effet de bord** : Peut casser certains composants si nonces mal gérés
**Effort** : S (1-2h)
---
#### 3. **Utilisation de `dangerouslySetInnerHTML` sans sanitisation complète**
**Fichiers** :
- `src/features/chat/components/ChatMessages.tsx` : ligne 102
- `src/features/chat/components/VirtualizedChatMessages.tsx` : ligne 75
**Problème** :
```typescript
// src/features/chat/components/ChatMessages.tsx:102
dangerouslySetInnerHTML={{
__html: sanitizeChatMessage(message.content)
}}
```
Bien que `sanitizeChatMessage` soit utilisé, la fonction `sanitizeHTML` dans `src/utils/sanitize.ts` peut avoir des failles (regex-based, pas de parser HTML complet).
**Impact** : XSS via messages de chat
**Fix minimal** :
- Utiliser `DOMPurify` (déjà dans `package.json` : `dompurify@^3.3.0`) au lieu de la sanitisation custom
- Vérifier que `DOMPurify` est bien utilisé partout
**Plan de validation** :
- Test XSS : Envoyer `<img src=x onerror=alert(1)>` dans un message
- Vérifier que le script ne s'exécute pas
**Effet de bord** : Aucun (DOMPurify est déjà une dépendance)
**Effort** : S (1h)
---
#### 4. **CSRF Token non implémenté**
**Fichiers** :
- `src/services/csrf.ts` : lignes 1-34
**Problème** :
```typescript
// src/services/csrf.ts:14-17
public async refreshCsrfToken(): Promise<void> {
// Placeholder: fetch from backend if needed
// this.csrfToken = ...
}
```
Le service CSRF existe mais n'est pas implémenté. Aucune protection CSRF pour les requêtes POST/PUT/DELETE.
**Impact** : Attaques CSRF possibles (actions non autorisées)
**Fix minimal** :
- Implémenter `refreshCsrfToken()` pour récupérer le token depuis le backend
- Ajouter le header `X-CSRF-Token` dans tous les intercepteurs Axios
- Backend doit générer et valider les tokens CSRF
**Plan de validation** :
- Test CSRF : Créer une requête POST depuis un site externe
- Vérifier que la requête est rejetée sans token CSRF valide
**Effet de bord** : Nécessite modification backend
**Effort** : M (2-3h)
---
#### 5. **WebSocket token en query param (exposé dans logs/URLs)**
**Fichiers** :
- `src/services/api.ts` : ligne 380
- `src/services/websocket.ts` : ligne 45
**Problème** :
```typescript
// src/services/api.ts:378-381
getWebSocketUrl(): string {
const token = this.getAccessToken();
return `${WS_BASE_URL}?token=${token}`;
}
```
Le token JWT est passé en query param, exposé dans :
- Logs serveur
- Historique navigateur
- URLs partagées
**Impact** : Fuite de token via logs/historique
**Fix minimal** :
- Utiliser le header `Authorization` si le serveur WebSocket le supporte
- Sinon, utiliser un token temporaire (one-time token) généré par le backend
**Plan de validation** :
- Vérifier que le token n'apparaît pas dans les logs serveur
- Vérifier que l'historique navigateur ne contient pas le token
**Effet de bord** : Nécessite modification serveur WebSocket Rust
**Effort** : M (2-3h)
---
### P1 — Haute priorité (Bugs fréquents / Dette bloquante)
#### 6. **Validation JWT côté client absente (expiration non vérifiée)**
**Fichiers** :
- `src/services/api/client.ts` : lignes 11-78
- `src/utils/token-manager.ts` : lignes 40-54
**Problème** :
Aucune vérification de l'expiration du token JWT côté client. Le token peut être expiré mais toujours utilisé jusqu'à ce que le backend renvoie 401.
**Impact** : Requêtes inutiles avec token expiré, UX dégradée
**Fix minimal** :
- Décoder le JWT (base64) et vérifier `exp` avant chaque requête
- Refresh automatique si expiration < 5min
**Plan de validation** :
- Test : Créer un token expiré, vérifier qu'il est refresh automatiquement
**Effet de bord** : Aucun
**Effort** : S (1h)
---
#### 7. **Pas de rate limiting côté client**
**Fichiers** :
- `src/services/api.ts` : toutes les méthodes
- `src/services/websocket.ts` : méthode `send()`
**Problème** :
Aucune protection contre le spam de requêtes (rate limiting). Un utilisateur malveillant peut spammer l'API.
**Impact** : DoS côté client, surcharge serveur
**Fix minimal** :
- Implémenter un rate limiter simple (ex: max 10 requêtes/seconde)
- Utiliser un debounce pour les requêtes répétées
**Plan de validation** :
- Test : Envoyer 100 requêtes simultanées, vérifier que seules 10 passent
**Effet de bord** : Peut ralentir certaines opérations légitimes
**Effort** : S (1-2h)
---
#### 8. **Secrets potentiels dans le code (VITE_FCM_VAPID_KEY)**
**Fichiers** :
- `src/config/env.ts` : ligne 18
- `vite.config.ts` : pas de validation
**Problème** :
`VITE_FCM_VAPID_KEY` est une clé publique (VAPID), mais elle est chargée côté client. Si elle est mal configurée, elle pourrait exposer une clé privée.
**Impact** : Fuite de clé si erreur de configuration
**Fix minimal** :
- Documenter que `VITE_FCM_VAPID_KEY` doit être la clé **publique** uniquement
- Ajouter une validation pour s'assurer que ce n'est pas une clé privée
**Plan de validation** :
- Vérifier que la clé dans `.env` est bien publique (commence par `BK` ou similaire)
**Effet de bord** : Aucun
**Effort** : S (30min)
---
#### 9. **Pas de validation de taille de fichier upload côté client**
**Fichiers** :
- `src/features/library/components/UploadModal.tsx`
- `src/features/tracks/components/TrackUpload.tsx`
**Problème** :
Aucune validation de la taille de fichier avant upload. Un utilisateur peut tenter d'uploader un fichier de 10GB.
**Impact** : Surcharge serveur, DoS
**Fix minimal** :
- Valider la taille max (ex: 100MB) avant upload
- Afficher une erreur claire si dépassement
**Plan de validation** :
- Test : Tenter d'uploader un fichier > 100MB, vérifier que c'est rejeté
**Effet de bord** : Aucun
**Effort** : S (1h)
---
#### 10. **CSP middleware non utilisé (créé mais jamais appelé)**
**Fichiers** :
- `src/utils/csp.ts` : lignes 185-202
**Problème** :
```typescript
// src/utils/csp.ts:185-202
export function createCSPMiddleware() {
return (_req: any, res: any, next: any) => {
// ... génère CSP header
}
}
```
Le middleware CSP est créé mais jamais utilisé (c'est du code Node.js dans un frontend React). La CSP est gérée via `vite.config.ts` uniquement.
**Impact** : CSP peut être inconsistante entre dev et prod
**Fix minimal** :
- Supprimer le middleware inutile (code mort)
- S'assurer que la CSP dans `vite.config.ts` est correcte pour prod
**Plan de validation** :
- Vérifier que la CSP en prod est stricte (pas d'`unsafe-inline`)
**Effet de bord** : Aucun (code mort)
**Effort** : S (30min)
---
## Autres problèmes sécurité (P2/P3)
### P2 — Moyenne priorité
- **Pas de validation de format de fichier upload** : Seule la validation backend existe
- **Pas de protection contre clickjacking** : `X-Frame-Options: SAMEORIGIN` au lieu de `DENY` dans `nginx.conf`
- **Pas de HSTS** : Header `Strict-Transport-Security` manquant dans `nginx.conf`
- **Logs console en production** : 121 occurrences de `console.log/error/warn` (peuvent exposer des infos sensibles)
### P3 — Basse priorité
- **Pas de validation de rate limiting pour WebSocket** : Spam de messages possible
- **Pas de chiffrement des données sensibles en localStorage** : Tokens non chiffrés (mais localStorage est déjà vulnérable)
---
# Observability & Prod Readiness Gaps
## Logs structurés
### État actuel
**Aucun logging structuré** :
- Utilisation de `console.log/error/warn` partout (121 occurrences)
- Pas de corrélation (pas de `request_id` / `trace_id`)
- Pas de niveaux de log cohérents
- Logs en production (devrait être désactivé)
**Fichiers concernés** :
- `src/services/websocket.ts` : 6 occurrences
- `src/features/streaming/services/playbackAnalyticsService.ts` : 11 occurrences
- `src/app/App.tsx` : 1 occurrence
- Et 50+ autres fichiers
### Fix recommandé
- Implémenter un logger structuré (ex: `pino` ou `winston` côté client)
- Ajouter `request_id` pour chaque requête API
- Désactiver les logs en production (`if (import.meta.env.DEV)`)
**Effort** : M (2-3h)
---
## Metrics
### État actuel
**Aucune métrique** :
- Pas de Prometheus / StatsD
- Pas de métriques custom (erreurs, latence, etc.)
- Pas de healthcheck détaillé
### Healthcheck
**Basique** : `/health` endpoint dans `nginx.conf` (ligne 26-30)
- Retourne simplement `200 "healthy\n"`
- Pas de vérification de dépendances (API, WebSocket, etc.)
### Fix recommandé
- Ajouter un healthcheck frontend qui vérifie :
- Connectivité API
- Connectivité WebSocket
- État du storage (localStorage disponible)
- Exposer des métriques custom (optionnel, via API backend)
**Effort** : S (1-2h)
---
## Timeouts / Retries
### État actuel
⚠️ **Partiel** :
- Timeout Axios : 10s (`src/services/api.ts:58`)
- Retry : Seulement pour refresh token (intercepteur)
- Pas de retry pour autres erreurs réseau
- Pas de circuit breaker
### Fix recommandé
- Implémenter un retry avec backoff exponentiel pour les erreurs réseau (5xx, timeout)
- Ajouter un circuit breaker pour éviter de spammer un serveur down
- Configurer des timeouts différents selon le type de requête (upload: 60s, API: 10s)
**Effort** : M (2-3h)
---
## Gestion de charge
### État actuel
⚠️ **Basique** :
- Pas de pool de connexions (Axios gère automatiquement)
- Pas de limite de connexions WebSocket simultanées
- Pas de chunking pour les uploads volumineux
### Uploads
**Chunked upload** : Implémenté dans `src/features/tracks/services/chunkedUploadService.ts`
- Upload par chunks pour fichiers volumineux
- Retry automatique en cas d'échec
### WebSocket
⚠️ **Pas de backpressure** :
- Pas de limite de messages en queue
- Pas de gestion de reconnexion intelligente (backoff exponentiel partiel)
**Fix recommandé** :
- Limiter la queue de messages WebSocket (max 100 messages)
- Implémenter un backpressure (pause si queue > 50)
**Effort** : S (1-2h)
---
## Migrations / Compatibilité
### État actuel
⚠️ **Conversion IDs** :
- Backend retourne `int64`, frontend attend `string`
- Conversion manuelle dans `src/services/api.ts` (lignes 339-344, 361-374)
**Problème** : Risque d'erreur si conversion oubliée
**Fix recommandé** :
- Créer un utilitaire centralisé pour la conversion
- Utiliser des types TypeScript stricts pour forcer la conversion
**Effort** : S (1h)
---
# Performance Notes
## Hotspots évidents
### 1. **Bundle size**
**État** : Non mesuré (bundle analyzer présent mais non exécuté)
**Fichiers** :
- `vite.config.ts` : lignes 34-39 (bundle analyzer configuré)
- `dist/bundle-analysis.html` : présent mais non analysé
**Recommandations** :
- Analyser le bundle (`npm run build` puis ouvrir `dist/bundle-analysis.html`)
- Identifier les dépendances lourdes
- Code splitting par route (déjà fait via lazy loading)
**Effort** : S (30min pour analyse)
---
### 2. **Re-renders inutiles**
**Problèmes identifiés** :
- `src/components/data/Table.tsx` : ligne 102 — `useCallback` avec dépendance manquante
- Pas de `React.memo` sur les composants lourds (liste de tracks, messages chat)
**Fix recommandé** :
- Ajouter `React.memo` sur les composants de liste
- Corriger les dépendances de `useCallback`/`useMemo`
**Effort** : M (2-3h)
---
### 3. **JSON parsing répétitif**
**Fichiers** :
- `src/services/websocket.ts` : ligne 59 — `JSON.parse` à chaque message
**Impact** : Faible (WebSocket messages sont petits)
**Fix recommandé** : Aucun (optimisation prématurée)
---
### 4. **N+1 queries potentielles**
**Fichiers** :
- `src/features/library/components/LibraryManager.tsx` : chargement de tracks un par un possible
**Impact** : Moyen (si beaucoup de tracks)
**Fix recommandé** :
- Utiliser la pagination côté API
- Charger les tracks par batch
**Effort** : S (1h)
---
## Streaming
### HLS.js
**Bien configuré** : Utilisation de `hls.js@^1.6.14`
- Buffering automatique
- Adaptation de bitrate (si backend supporte)
**Pas de problème identifié**
---
## React Query
### Cache
**Bien configuré** : React Query v5 avec cache automatique
- `staleTime` : défaut (0, toujours stale)
- `cacheTime` : défaut (5min)
**Recommandation** :
- Augmenter `staleTime` pour les données peu changeantes (profils, tracks)
- Configurer `staleTime: 5 * 60 * 1000` (5min) pour les tracks
**Effort** : S (30min)
---
## WebSocket
### Reconnexion
**Bien implémenté** : Backoff exponentiel dans `src/services/websocket.ts` (lignes 76-98)
- Max 5 tentatives
- Délai exponentiel : `3000 * 2^(attempt-1)`
**Pas de problème identifié**
---
# Issue List Priorisée (P0→P3)
## Format des issues
Chaque issue suit le format :
- **ID** : `WEB-<P0|P1|P2|P3>-###`
- **Titre**
- **Impact** (concret, scénarios)
- **Preuve** : fichiers + symboles + extrait court
- **Cause racine**
- **Fix minimal** (patch-level, pas de refonte totale)
- **Plan de validation** (tests + commande + cas limites)
- **Effet de bord / risque de régression**
- **Effort estimé** (S/M/L) + dépendances
---
## P0 — Build Breakers / Sécurité Critique
### WEB-P0-001 : Modules manquants (build cassé)
**Impact** : Build TypeScript échoue, application ne compile pas
**Preuve** :
- `src/components/player/AudioPlayer.tsx:6` : `Cannot find module '@/hooks/use-toast'`
- `src/components/player/AudioPlayer.tsx:2` : `Cannot find module '@/stores/player'`
- `src/components/ui/optimized-image.tsx:2` : `Cannot find module '@/hooks/useIntersectionObserver'`
- `src/components/ui/LazyComponent.tsx:39` : `Cannot find module '@/features/settings/pages/SettingsPage'`
- Et 5+ autres modules manquants
**Cause racine** :
- Fichiers supprimés/déplacés sans mise à jour des imports
- Exports manquants dans les modules
**Fix minimal** :
1. Créer les modules manquants ou corriger les imports
2. Vérifier que tous les exports sont corrects
**Plan de validation** :
```bash
npm run typecheck # Doit passer sans erreur
npm run build # Doit compiler
```
**Effet de bord** : Aucun (fix nécessaire pour build)
**Effort** : M (2-3h)
---
### WEB-P0-002 : Tokens JWT en localStorage (XSS vulnérable)
**Voir [Security Finding #1](#1-tokens-jwt-stockés-en-localstorage-xss-vulnérable)**
**Effort** : M (2-4h)
---
### WEB-P0-003 : CSP non-strict (`unsafe-inline`, `unsafe-eval`)
**Voir [Security Finding #2](#2-csp-non-strict-en-production-unsafe-inline-unsafe-eval)**
**Effort** : S (1-2h)
---
### WEB-P0-004 : `dangerouslySetInnerHTML` sans sanitisation complète
**Voir [Security Finding #3](#3-utilisation-de-dangerouslysetinnerhtml-sans-sanitisation-complète)**
**Effort** : S (1h)
---
### WEB-P0-005 : CSRF Token non implémenté
**Voir [Security Finding #4](#4-csrf-token-non-implémenté)**
**Effort** : M (2-3h)
---
## P1 — Bugs fréquents / Dette bloquante
### WEB-P1-001 : Types Track/User incomplets (100+ erreurs TypeScript)
**Impact** : Erreurs TypeScript partout, développement ralenti, bugs runtime possibles
**Preuve** :
- `src/components/player/AudioPlayer.tsx:90` : `Property 'url' does not exist on type 'Track'`
- `src/components/player/AudioPlayer.tsx:179` : `Property 'cover_url' does not exist on type 'Track'`
- `src/features/library/components/VirtualizedTrackList.tsx:56` : `Property 'album' does not exist on type 'Track'`
- `src/features/chat/hooks/useChat.ts:9` : `Property 'userId' does not exist on type 'AuthState'`
**Cause racine** :
- Types `Track` et `User` dans `src/types/api.ts` ne correspondent pas à l'usage réel
- Backend retourne plus de champs que définis dans les types
**Fix minimal** :
1. Mettre à jour `src/types/api.ts` avec tous les champs utilisés
2. Vérifier la cohérence avec le backend Go
**Plan de validation** :
```bash
npm run typecheck # Doit passer sans erreur
```
**Effet de bord** : Aucun (correction de types)
**Effort** : M (2-3h)
---
### WEB-P1-002 : React Query v5 incompatibilité (`onSuccess`, `keepPreviousData`)
**Impact** : Warnings React Query, comportement incorrect possible
**Preuve** :
- `src/features/chat/pages/ChatPage.tsx:28` : `'onSuccess' does not exist`
- `src/features/library/hooks/useMyTracks.ts:17` : `'keepPreviousData' does not exist`
**Cause racine** : Migration React Query v4 → v5 incomplète
**Fix minimal** :
1. Remplacer `onSuccess` par `useEffect` avec dépendance sur `data`
2. Remplacer `keepPreviousData` par `placeholderData: keepPreviousData`
**Plan de validation** :
```bash
npm run typecheck # Doit passer sans erreur
npm test # Tests doivent passer
```
**Effet de bord** : Aucun (migration standard)
**Effort** : S (1-2h)
---
### WEB-P1-003 : Tests échouent (usePlaylistTrack)
**Impact** : CI/CD peut échouer, confiance réduite dans les tests
**Preuve** :
- `src/features/playlists/hooks/usePlaylistTrack.test.tsx` : 2 tests échouent
- `useAddTrackToPlaylist` : argument manquant
- `useRemoveTrackFromPlaylist` : fonction non exportée
**Cause racine** :
- Signature de fonction changée sans mise à jour des tests
- Export manquant
**Fix minimal** :
1. Corriger les signatures de fonction
2. Ajouter les exports manquants
3. Mettre à jour les tests
**Plan de validation** :
```bash
npm test -- usePlaylistTrack # Doit passer
```
**Effet de bord** : Aucun
**Effort** : S (1h)
---
### WEB-P1-004 : Validation JWT côté client absente
**Voir [Security Finding #6](#6-validation-jwt-côté-client-absente-expiration-non-vérifiée)**
**Effort** : S (1h)
---
### WEB-P1-005 : Duplication de clients API (`apiService` vs `apiClient`)
**Impact** : Confusion, maintenance difficile, bugs possibles
**Preuve** :
- `src/services/api.ts` : classe `ApiService` (singleton `apiService`)
- `src/services/api/client.ts` : `apiClient` (Axios instance)
- `src/lib/apiClient.ts` : autre `apiClient` (Axios instance)
**Cause racine** : Refactoring incomplet, plusieurs implémentations coexistent
**Fix minimal** :
1. Unifier en un seul client API
2. Migrer tous les usages vers le client unifié
3. Supprimer les clients dupliqués
**Plan de validation** :
```bash
grep -r "apiService\|apiClient" src/ # Vérifier qu'un seul client est utilisé
npm run typecheck # Doit passer
npm test # Tests doivent passer
```
**Effet de bord** : Risque de régression si migration incomplète
**Effort** : M (3-4h)
---
### WEB-P1-006 : WebSocket token en query param
**Voir [Security Finding #5](#5-websocket-token-en-query-param-exposé-dans-logsurls)**
**Effort** : M (2-3h)
---
### WEB-P1-007 : Conversion IDs int64 → string manuelle (risque d'erreur)
**Impact** : Bugs possibles si conversion oubliée, maintenance difficile
**Preuve** :
- `src/services/api.ts:339-344` : conversion manuelle `String(conv.id)`
- `src/services/api.ts:361-374` : conversion manuelle `String(data.id)`
**Cause racine** : Backend retourne `int64`, frontend attend `string`
**Fix minimal** :
1. Créer un utilitaire `convertBackendId(id: number | string): string`
2. Utiliser partout où nécessaire
3. Ajouter des types stricts pour forcer la conversion
**Plan de validation** :
```bash
grep -r "String(.*\.id)" src/ # Vérifier que toutes les conversions utilisent l'utilitaire
npm run typecheck # Doit passer
```
**Effet de bord** : Aucun
**Effort** : S (1h)
---
## P2 — Qualité, maintenabilité, perf non critique
### WEB-P2-001 : 121 occurrences de `console.log/error/warn` (logs en production)
**Impact** : Performance légère, exposition d'infos sensibles possible, pollution console
**Preuve** :
```bash
grep -r "console\." src/ | wc -l # 121 occurrences
```
**Cause racine** : Pas de logger structuré, logs de debug laissés
**Fix minimal** :
1. Créer un logger wrapper (`src/utils/logger.ts`)
2. Remplacer tous les `console.*` par le logger
3. Désactiver les logs en production
**Plan de validation** :
```bash
grep -r "console\." src/ | wc -l # Doit être 0
npm run build && npm run preview # Vérifier que pas de logs en prod
```
**Effet de bord** : Aucun
**Effort** : M (2-3h)
---
### WEB-P2-002 : Pas de rate limiting côté client
**Voir [Security Finding #7](#7-pas-de-rate-limiting-côté-client)**
**Effort** : S (1-2h)
---
### WEB-P2-003 : Re-renders inutiles (pas de `React.memo`)
**Impact** : Performance dégradée sur les listes longues
**Preuve** :
- `src/components/data/Table.tsx:102` : `useCallback` avec dépendance manquante
- Pas de `React.memo` sur `TrackList`, `ChatMessages`, etc.
**Cause racine** : Optimisation non faite
**Fix minimal** :
1. Ajouter `React.memo` sur les composants de liste
2. Corriger les dépendances de `useCallback`
**Plan de validation** :
- Profiler avec React DevTools
- Vérifier que les re-renders sont réduits
**Effet de bord** : Aucun
**Effort** : M (2-3h)
---
### WEB-P2-004 : Pas de validation de taille de fichier upload
**Voir [Security Finding #9](#9-pas-de-validation-de-taille-de-fichier-upload-côté-client)**
**Effort** : S (1h)
---
### WEB-P2-005 : Pas de logging structuré
**Voir [Observability - Logs structurés](#logs-structurés)**
**Effort** : M (2-3h)
---
### WEB-P2-006 : Healthcheck basique (pas de vérification dépendances)
**Voir [Observability - Healthcheck](#healthcheck)**
**Effort** : S (1-2h)
---
### WEB-P2-007 : Pas de retry automatique (sauf refresh token)
**Voir [Observability - Timeouts / Retries](#timeouts--retries)**
**Effort** : M (2-3h)
---
### WEB-P2-008 : Bundle size non analysé
**Voir [Performance - Bundle size](#1-bundle-size)**
**Effort** : S (30min)
---
### WEB-P2-009 : CSP middleware inutile (code mort)
**Voir [Security Finding #10](#10-csp-middleware-non-utilisé-créé-mais-jamais-appelé)**
**Effort** : S (30min)
---
### WEB-P2-010 : Naming incohérent (`useAuthStore` vs `useAuth`, `apiService` vs `apiClient`)
**Impact** : Confusion, maintenance difficile
**Preuve** :
- `src/features/auth/store/authStore.ts` : exporte `useAuthStore` (moved from stores/auth.ts)
- `src/features/auth/hooks/useAuth.ts` : exporte `useAuth` (utilise `useAuthStore` en interne)
- `src/services/api.ts` : `apiService`
- `src/services/api/client.ts` : `apiClient`
**Cause racine** : Refactoring incomplet, conventions non suivies
**Fix minimal** :
1. Documenter les conventions de naming
2. Unifier progressivement (dans le cadre de P1-005)
**Plan de validation** :
- Revue de code pour vérifier cohérence
**Effet de bord** : Aucun
**Effort** : S (1h pour documentation)
---
## P3 — Cosmétique, refactors non urgents
### WEB-P3-001 : 30+ occurrences de `any` (types implicites)
**Impact** : Perte de type safety, bugs possibles
**Preuve** :
```bash
grep -r ": any\|as any" src/ | wc -l # ~30 occurrences
```
**Cause racine** : Types non définis, migration TypeScript incomplète
**Fix minimal** :
1. Remplacer progressivement les `any` par des types stricts
2. Utiliser `unknown` si type vraiment inconnu
**Plan de validation** :
```bash
npm run lint # Doit passer sans warnings @typescript-eslint/no-explicit-any
```
**Effet de bord** : Aucun
**Effort** : L (4-6h, peut être fait progressivement)
---
### WEB-P3-002 : Pas de HSTS header
**Impact** : Sécurité légèrement réduite (manque protection HTTPS forcé)
**Preuve** :
- `nginx.conf` : pas de header `Strict-Transport-Security`
**Fix minimal** :
```nginx
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
```
**Plan de validation** :
- Vérifier avec `curl -I` que le header est présent
**Effet de bord** : Aucun
**Effort** : S (5min)
---
### WEB-P3-003 : `X-Frame-Options: SAMEORIGIN` au lieu de `DENY`
**Impact** : Protection clickjacking partielle
**Preuve** :
- `nginx.conf:14` : `X-Frame-Options "SAMEORIGIN"`
**Fix minimal** :
```nginx
add_header X-Frame-Options "DENY" always;
```
**Plan de validation** :
- Vérifier avec `curl -I`
**Effet de bord** : Aucun (pas d'iframe dans l'app)
**Effort** : S (5min)
---
### WEB-P3-004 : Pas de validation de format de fichier upload
**Voir [Security - Autres problèmes P2](#p2--moyenne-priorité)**
**Effort** : S (1h)
---
### WEB-P3-005 : TODO/FIXME dans le code (38 occurrences)
**Impact** : Dette technique, fonctionnalités incomplètes
**Preuve** :
```bash
grep -ri "TODO\|FIXME\|XXX\|HACK" src/ | wc -l # 38 occurrences
```
**Exemples** :
- `src/features/chat/hooks/useChat.ts:114` : `// TODO: Add fetchHistory function`
- `src/features/library/components/LibraryManager.tsx:247` : `isLiked={() => false} // Todo`
**Fix minimal** :
1. Créer des issues GitHub pour chaque TODO
2. Supprimer les TODOs résolus
3. Documenter les TODOs non urgents
**Plan de validation** :
- Revue manuelle des TODOs
**Effet de bord** : Aucun
**Effort** : S (1h pour trier et documenter)
---
# Execution Plan
## Checklist P0 (ordre strict)
### Phase 1 : Build Fixes (Critique — 2-3h)
1.**WEB-P0-001** : Créer/corriger les modules manquants
- Créer `src/hooks/use-toast.ts` (ou corriger import)
- Créer `src/stores/player.ts` (ou corriger import)
- Créer `src/hooks/useIntersectionObserver.ts` (ou corriger import)
- Corriger les imports dans `src/components/ui/LazyComponent.tsx`
- Valider : `npm run typecheck` passe
2.**WEB-P1-001** : Corriger les types Track/User
- Mettre à jour `src/types/api.ts` avec tous les champs
- Valider : `npm run typecheck` passe
### Phase 2 : Sécurité Critique (4-6h)
3.**WEB-P0-004** : Utiliser DOMPurify pour sanitisation
- Remplacer `sanitizeChatMessage` par `DOMPurify.sanitize()`
- Valider : Test XSS avec `<img src=x onerror=alert(1)>`
4.**WEB-P0-003** : Renforcer CSP (supprimer `unsafe-eval`)
- Modifier `vite.config.ts` pour production
- Utiliser nonces pour scripts inline
- Valider : Build prod, vérifier CSP header
5.**WEB-P0-002** : Migrer tokens vers httpOnly cookies (nécessite backend)
- **Option alternative** : Utiliser `sessionStorage` + rotation fréquente
- Valider : Test XSS ne peut plus voler tokens
6.**WEB-P0-005** : Implémenter CSRF token (nécessite backend)
- Implémenter `refreshCsrfToken()` dans `src/services/csrf.ts`
- Ajouter header dans intercepteurs Axios
- Valider : Test CSRF échoue sans token
### Phase 3 : Tests (1h)
7.**WEB-P1-003** : Corriger tests usePlaylistTrack
- Corriger signatures et exports
- Valider : `npm test -- usePlaylistTrack` passe
---
## Checklist P1 (par lots cohérents)
### Lot 1 : React Query v5 Migration (1-2h)
8.**WEB-P1-002** : Migrer `onSuccess``useEffect`
- Remplacer dans `src/features/chat/pages/ChatPage.tsx`
- Remplacer `keepPreviousData``placeholderData`
- Valider : `npm run typecheck` + `npm test`
### Lot 2 : Unification Clients API (3-4h)
9.**WEB-P1-005** : Unifier `apiService` et `apiClient`
- Choisir un client (recommandé : `apiClient` de `src/services/api/client.ts`)
- Migrer tous les usages
- Supprimer `src/services/api.ts` (ou le garder comme wrapper)
- Valider : `npm run typecheck` + `npm test` + tests manuels
### Lot 3 : JWT & WebSocket (3-4h)
10.**WEB-P1-004** : Validation JWT expiration côté client
- Décoder JWT et vérifier `exp`
- Refresh automatique si < 5min
- Valider : Test avec token expiré
11. **WEB-P1-006** : WebSocket token en header (nécessite backend)
- **Option alternative** : Utiliser token temporaire (one-time)
- Valider : Token n'apparaît pas dans logs
### Lot 4 : Utilitaires (1h)
12. **WEB-P1-007** : Créer utilitaire conversion IDs
- Créer `src/utils/id-converter.ts`
- Remplacer toutes les conversions manuelles
- Valider : `grep -r "String(.*\.id)" src/` retourne 0
---
## Quick Wins (≤ 1h chacun)
### Sécurité
- **WEB-P2-009** : Supprimer CSP middleware inutile (30min)
- **WEB-P3-002** : Ajouter HSTS header (5min)
- **WEB-P3-003** : Changer `X-Frame-Options` à `DENY` (5min)
- **WEB-P2-004** : Validation taille fichier upload (1h)
### Qualité
- **WEB-P2-008** : Analyser bundle size (30min)
- **WEB-P3-005** : Trier et documenter TODOs (1h)
---
## Tests à ajouter en priorité
### P0 — Critiques
1. **Test XSS** : Injecter `<script>alert(1)</script>` dans chat, vérifier qu'il ne s'exécute pas
2. **Test CSRF** : Requête POST depuis site externe, vérifier rejet
3. **Test token expiration** : Vérifier refresh automatique
### P1 — Importants
4. **Test rate limiting** : Envoyer 100 requêtes, vérifier limitation
5. **Test upload taille max** : Tenter upload > 100MB, vérifier rejet
6. **Test WebSocket reconnexion** : Simuler déconnexion, vérifier reconnexion
---
## PR Plan
### PR 1 : Build Fixes (P0-001, P1-001)
**Titre** : `fix: resolve TypeScript build errors (missing modules, incomplete types)`
**Fichiers** :
- Créer modules manquants
- Mettre à jour `src/types/api.ts`
**Tests** : `npm run typecheck` passe
### PR 2 : Security - XSS & CSP (P0-003, P0-004)
**Titre** : `security: improve XSS protection (DOMPurify, strict CSP)`
**Fichiers** :
- `src/features/chat/components/ChatMessages.tsx`
- `src/features/chat/components/VirtualizedChatMessages.tsx`
- `vite.config.ts`
**Tests** : Test XSS manuel
### PR 3 : Security - Tokens & CSRF (P0-002, P0-005)
**Titre** : `security: migrate tokens to httpOnly cookies, implement CSRF protection`
**Fichiers** :
- `src/services/api/auth.ts`
- `src/services/csrf.ts`
- `src/services/api/client.ts`
**Tests** : Tests CSRF, test XSS token theft
**Note** : Nécessite coordination backend
### PR 4 : React Query v5 Migration (P1-002)
**Titre** : `chore: migrate to React Query v5 API (remove onSuccess, keepPreviousData)`
**Fichiers** :
- `src/features/chat/pages/ChatPage.tsx`
- `src/features/library/hooks/useMyTracks.ts`
**Tests** : `npm test` passe
### PR 5 : Unify API Clients (P1-005)
**Titre** : `refactor: unify API clients (remove duplication)`
**Fichiers** :
- `src/services/api.ts` (supprimer ou wrapper)
- `src/services/api/client.ts` (garder)
- Tous les fichiers utilisant `apiService`
**Tests** : Tests complets + tests manuels
### PR 6 : JWT & WebSocket Security (P1-004, P1-006)
**Titre** : `security: add JWT expiration validation, move WebSocket token to header`
**Fichiers** :
- `src/services/api/client.ts`
- `src/services/websocket.ts`
- `src/utils/token-manager.ts`
**Tests** : Tests expiration, tests WebSocket
**Note** : WebSocket nécessite backend
### PR 7 : Utilities & Tests (P1-007, P1-003)
**Titre** : `fix: add ID converter utility, fix usePlaylistTrack tests`
**Fichiers** :
- `src/utils/id-converter.ts`
- `src/services/api.ts` (utiliser converter)
- `src/features/playlists/hooks/usePlaylistTrack.test.tsx`
**Tests** : Tests unitaires
### PR 8 : Quick Wins (P2-009, P3-002, P3-003, P2-004, P2-008, P3-005)
**Titre** : `chore: quick wins (remove dead code, add security headers, validate uploads, analyze bundle, document TODOs)`
**Fichiers** :
- `src/utils/csp.ts` (supprimer middleware)
- `nginx.conf` (ajouter headers)
- `src/features/library/components/UploadModal.tsx` (validation taille)
- `package.json` (script bundle analysis)
- Documentation TODOs
**Tests** : Tests manuels
---
## Estimation totale
- **P0** : 8-12h (build fixes + sécurité critique)
- **P1** : 8-12h (bugs fréquents, dette bloquante)
- **P2** : 6-10h (qualité, maintenabilité)
- **P3** : 2-4h (cosmétique)
**Total** : **24-38h** de travail (3-5 jours développeur)
---
## Dépendances externes
### Backend Go (`veza-backend-api`)
- Support httpOnly cookies pour tokens (P0-002)
- Endpoint CSRF token (P0-005)
- Validation CSRF sur toutes les requêtes POST/PUT/DELETE (P0-005)
### Chat Server Rust (`veza-chat-server`)
- Support token JWT en header au lieu de query param (P1-006)
### Stream Server Rust (`veza-stream-server`)
- Aucune dépendance identifiée
---
## Notes finales
### Points positifs
✅ Architecture feature-based claire
✅ Tests unitaires nombreux (189 fichiers)
✅ Utilisation de Zod pour validation
✅ ErrorBoundary React
✅ Upload par chunks pour gros fichiers
✅ WebSocket avec reconnexion intelligente
✅ PWA support (service worker)
✅ i18n (français/anglais)
### Points à surveiller
⚠️ Duplication de code (clients API)
⚠️ Logs console partout (121 occurrences)
⚠️ Types incomplets (Track/User)
⚠️ Sécurité tokens (localStorage)
### Recommandations long terme
1. **Centraliser la gestion d'erreurs** : Créer un service d'erreurs global
2. **Logger structuré** : Implémenter un logger avec niveaux et corrélation
3. **Métriques** : Ajouter des métriques custom (erreurs, latence, etc.)
4. **Documentation** : Documenter les conventions de code et les patterns
5. **CI/CD** : Ajouter des checks automatiques (typecheck, lint, tests) dans CI
---
**Fin du rapport d'audit**