# V0.502 Release Scope — Chat Server Rewrite (Rust → Go) **Status** : En préparation **Phase** : 5 — Streaming & Communication **Prérequis** : v0.501 (taguée) **ADR** : [ADR-002-chat-server.md](adr/ADR-002-chat-server.md) --- ## Objectif Réécrire le chat server Rust (`veza-chat-server`) en Go, intégré directement dans le backend API (`veza-backend-api`). Conserver le protocole WebSocket JSON identique pour une migration transparente du frontend. --- ## État actuel (pré-v0.502) | Composant | État | |-----------|------| | Chat server Rust (`veza-chat-server`) | Compile, **non intégré** (boot mode OFF) | | Modèles Go (`Room`, `RoomMember`, `Message`) | ✅ Existants | | Repositories Go (`RoomRepository`, `ChatRepository`) | ✅ Existants | | Services Go (`RoomService`, `ChatService`) | ✅ Existants | | Handlers REST Go (`/conversations/*`, `/chat/token`) | ✅ Existants | | Frontend chat (`useChat`, `chatStore`, `ChatPage`) | ✅ Existe, utilise mocks MSW | | Pattern WebSocket Go (`playback_websocket_handler.go`) | ✅ Référence | | Tables DB (`rooms`, `room_members`, `messages`) | ✅ Migration 051 | | Tables DB (`read_receipts`, `delivered_status`, `message_reactions`) | ❌ Manquantes en Go | --- ## Lots ### Lot CH1 : Chat Server Go — Backend (14 tâches) #### CH1-01 : Migrations DB complémentaires - **Migration 109** : `read_receipts` (user_id, message_id, read_at, index) - **Migration 110** : `delivered_status` (user_id, message_id, delivered_at, index) - **Migration 111** : `message_reactions` (user_id, message_id, emoji, created_at, unique constraint) - **Migration 112** : Ajout colonnes manquantes sur `messages` : `edited_at TIMESTAMPTZ`, `status VARCHAR(20) DEFAULT 'sent'` #### CH1-02 : Modèles Go complémentaires - Modèle `ReadReceipt` (UserID, MessageID, ReadAt) - Modèle `DeliveredStatus` (UserID, MessageID, DeliveredAt) - Modèle `MessageReaction` (UserID, MessageID, Emoji, CreatedAt) - Mise à jour modèle `Message` : ajout champs `EditedAt`, `Status`, `IsPinned`, `Metadata` #### CH1-03 : Repository Chat GORM enrichi - Créer `ChatMessageRepository` basé sur GORM (remplace `database/chat_repository.go` en raw SQL) - Méthodes : `Create`, `GetByID`, `GetByRoomID` (paginé, avant/après curseur), `Update`, `SoftDelete` - Méthodes : `Search` (ILIKE + to_tsvector), `GetSince` (pour sync) - Méthodes : `Pin`, `Unpin`, `GetPinned` #### CH1-04 : Repositories read/delivered/reactions - `ReadReceiptRepository` : `MarkRead`, `GetByMessage`, `GetByConversation` - `DeliveredStatusRepository` : `MarkDelivered`, `GetByMessage` - `ReactionRepository` : `Add`, `Remove`, `GetByMessage` #### CH1-05 : Service Redis PubSub - Créer `ChatPubSubService` dans `internal/services/chat_pubsub.go` - `Publish(roomID, message)` : publie un message JSON sur le canal `chat:room:{roomID}` - `Subscribe(roomID)` : s'abonne au canal et retourne un channel Go - `PublishPresence(event)` : publie sur `chat:presence` - Fallback in-memory si Redis non disponible (dev sans Redis) #### CH1-06 : WebSocket Hub (gestionnaire de connexions) - Créer `internal/websocket/chat/hub.go` - Structure `Hub` avec : - `clients map[uuid.UUID]*Client` (userID → client) - `rooms map[uuid.UUID]map[uuid.UUID]*Client` (roomID → userID → client) - `register`, `unregister`, `broadcast` channels - Goroutine `Run()` principale qui gère les événements - Méthodes : `BroadcastToRoom(roomID, message, excludeUserID)` - Méthodes : `SendToUser(userID, message)` - Intégration Redis PubSub pour multi-instance #### CH1-07 : Client WebSocket et pumps - Créer `internal/websocket/chat/client.go` - Structure `Client` : - `conn *websocket.Conn` - `userID uuid.UUID`, `username string` - `hub *Hub` - `send chan []byte` - `rooms map[uuid.UUID]bool` (rooms auxquelles le client est abonné) - Goroutine `readPump()` : lecture des messages, parsing JSON, dispatch - Goroutine `writePump()` : écriture des messages depuis le channel `send`, ping/pong keepalive - Timeouts : read 60s, write 10s, pong 60s #### CH1-08 : Types de messages WebSocket Go - Créer `internal/websocket/chat/messages.go` - Structs Go pour chaque type de message entrant (IncomingMessage avec champ `Type` discriminant) - Structs Go pour chaque type de message sortant (OutgoingMessage avec champ `Type` discriminant) - Marshaling/unmarshaling JSON identique au protocole Rust existant - Validation des messages entrants (champs requis selon le type) #### CH1-09 : Handler WebSocket — Messages CRUD - `handleSendMessage` : validation, persistence DB, broadcast room, Redis PubSub - `handleEditMessage` : vérification ownership, update DB, broadcast `MessageEdited` - `handleDeleteMessage` : vérification ownership, soft delete DB, broadcast `MessageDeleted` #### CH1-10 : Handler WebSocket — Rooms - `handleJoinConversation` : vérification membership, ajout au hub, broadcast présence - `handleLeaveConversation` : retrait du hub, broadcast départ #### CH1-11 : Handler WebSocket — Historique & Recherche - `handleFetchHistory` : query DB paginée (before/after curseur, limit), réponse `HistoryChunk` - `handleSearchMessages` : query ILIKE + FTS, réponse `SearchResults` - `handleSyncMessages` : messages depuis un timestamp, réponse `SyncChunk` #### CH1-12 : Handler WebSocket — Typing, Read, Delivered, Reactions - `handleTyping` : broadcast `UserTyping` avec timeout 3s auto-clear - `handleMarkAsRead` : persistence `read_receipts`, broadcast `MessageRead` - `handleDelivered` : persistence `delivered_status`, broadcast `MessageDelivered` - `handleAddReaction` : persistence, broadcast `ReactionAdded` - `handleRemoveReaction` : suppression, broadcast `ReactionRemoved` #### CH1-13 : Handler WebSocket — Signalisation WebRTC - `handleCallOffer` : relay SDP offer au target_user_id - `handleCallAnswer` : relay SDP answer au caller_user_id - `handleICECandidate` : relay ICE candidate au target_user_id - `handleCallHangup` : broadcast hangup - `handleCallReject` : broadcast rejection #### CH1-14 : Rate Limiting WebSocket - Créer `internal/websocket/chat/rate_limiter.go` - Rate limits par action et par utilisateur (Redis-backed, fallback mémoire) : - `SendMessage` : 10/s - `Typing` : 5/s - `AddReaction` : 5/s - `EditMessage` / `DeleteMessage` : 5/s - `SearchMessages` : 2/s - `CallSignaling` : 10/s - Réponse `Error` si rate limit dépassé --- ### Lot CH2 : Intégration Routes & Docker (6 tâches) #### CH2-01 : Endpoint WebSocket dans le router Go - Ajouter route `GET /api/v1/ws` dans `router.go` - Handler : upgrade HTTP → WebSocket avec `coder/websocket` - Auth : extraire token du query param `?token=`, valider JWT - Créer le client, l'enregistrer dans le Hub, lancer readPump/writePump #### CH2-02 : Mise à jour ChatService - Mettre à jour `GenerateToken` pour retourner le bon `WSUrl` (/api/v1/ws) - Ajouter méthode `ValidateChatToken(tokenString) (*Claims, error)` #### CH2-03 : Initialisation du Hub dans le backend - Instancier le Hub au démarrage du serveur (dans `main.go` ou `router.go`) - Injecter Redis PubSub si disponible - Lancer `hub.Run()` en goroutine #### CH2-04 : Suppression du chat server Rust de Docker - Retirer le service `chat-server` de `docker-compose.yml` - Retirer le service `chat-server` de `docker-compose.staging.yml` - Retirer le service `chat-server` de `docker-compose.prod.yml` - Supprimer les variables d'environnement `CHAT_SERVER_*` du backend - Mettre à jour les healthchecks et depends_on #### CH2-05 : Variables d'environnement - Supprimer `VITE_WS_URL` (le WebSocket est maintenant sur le même host que l'API) - Ou rediriger `VITE_WS_URL` vers `${VITE_API_URL}/ws` (même origin) - Mettre à jour `.env.example`, `.env.development` #### CH2-06 : Permissions et membership - Créer `internal/websocket/chat/permissions.go` - `CanRead(userID, roomID)` : vérifie membership via `room_members` - `CanSend(userID, roomID)` : vérifie membership + non-muted + non-banned - `CanJoin(userID, roomID)` : vérifie si room publique ou si invité - `CanModerate(userID, roomID)` : vérifie rôle admin/moderator --- ### Lot CH3 : Frontend Migration (6 tâches) #### CH3-01 : Mise à jour de l'URL WebSocket - Modifier `useChat.ts` pour construire l'URL WS depuis `VITE_API_URL` - Format : `ws(s)://{api_host}/api/v1/ws?token={token}` - Supprimer la logique de `VITE_WS_URL` séparée - Supprimer la vérification `127.0.0.1:8081` (dev hack) #### CH3-02 : Mise à jour du flux d'authentification - Modifier `useChatStore` : `setWsToken` n'a plus besoin de `wsUrl` - Le hook `useChat` construit l'URL dynamiquement depuis le token et l'API URL - Supprimer le champ `wsUrl` du store (ou le garder calculé) #### CH3-03 : Mise à jour des handlers MSW - Mettre à jour `handlers-misc.ts` : `POST /api/v1/chat/token` retourne le bon `ws_url` - Ajouter mock pour `GET /api/v1/conversations/:id/history` - S'assurer que le mock conversation list est compatible #### CH3-04 : Vérification des composants Chat - Vérifier `ChatPage.tsx`, `ChatInterface.tsx`, `ChatMessage.tsx` - Vérifier `TypingIndicator.tsx`, `VirtualizedChatMessages` - Vérifier que `useWebRTC.ts` (appels) fonctionne avec la nouvelle URL - S'assurer que `EditMessage` et `DeleteMessage` sont gérés dans `handleMessage` #### CH3-05 : Ajout types manquants - Ajouter `EditMessage`, `DeleteMessage`, `FetchHistory`, `SearchMessages`, `SyncMessages` dans `OutgoingMessage` - Ajouter `MessageEdited`, `MessageDeleted`, `SearchResults`, `SyncChunk` dans `IncomingMessage` - Ajouter les handlers correspondants dans `useChat.ts` `handleMessage` #### CH3-06 : Storybook & tests visuels - Mettre à jour les stories chat si nécessaire - Vérifier que `ChatPage.stories.tsx` fonctionne avec les nouveaux mocks - Ajouter une story pour l'état "WebSocket connecting" et "WebSocket error" --- ### Lot CH4 : Tests & Validation (8 tâches) #### CH4-01 : Tests unitaires Hub - Test `Hub.Run()` : register/unregister clients - Test `BroadcastToRoom` : message envoyé à tous les membres sauf l'expéditeur - Test `SendToUser` : message ciblé #### CH4-02 : Tests unitaires message handlers - Test `handleSendMessage` : message persisté, broadcasté - Test `handleEditMessage` : ownership vérifié, message mis à jour - Test `handleDeleteMessage` : soft delete, broadcast #### CH4-03 : Tests unitaires typing/read/reactions - Test `handleTyping` : broadcast UserTyping - Test `handleMarkAsRead` : read_receipt créé, broadcast - Test `handleAddReaction` / `handleRemoveReaction` #### CH4-04 : Tests E2E connexion WebSocket - Test : connexion avec token valide → upgrade réussie - Test : connexion sans token → refusée - Test : connexion avec token expiré → refusée - Test : ping/pong keepalive #### CH4-05 : Tests E2E flux de messages - Test : envoyer message → reçu par autre client dans la même room - Test : join room → receive history - Test : edit message → broadcast edit event - Test : delete message → broadcast delete event #### CH4-06 : Tests E2E real-time features - Test : typing indicator envoyé → reçu par les autres - Test : mark as read → broadcast read receipt - Test : add reaction → broadcast reaction added #### CH4-07 : Document de feature parity - Créer `docs/CHAT_FEATURE_PARITY.md` - Checklist : chaque feature du chat server Rust validée en Go - Colonnes : Feature | Rust | Go | Status #### CH4-08 : Tests de performance - Benchmark : 100 connexions simultanées - Benchmark : latence message delivery < 100ms - Benchmark : mémoire par connexion --- ## Fichiers impactés ### Nouveaux fichiers Go | Fichier | Contenu | |---------|---------| | `migrations/109_read_receipts.sql` | Table read_receipts | | `migrations/110_delivered_status.sql` | Table delivered_status | | `migrations/111_message_reactions.sql` | Table message_reactions | | `migrations/112_messages_extra_columns.sql` | Colonnes edited_at, status sur messages | | `internal/models/read_receipt.go` | Modèle ReadReceipt | | `internal/models/delivered_status.go` | Modèle DeliveredStatus | | `internal/models/message_reaction.go` | Modèle MessageReaction | | `internal/repositories/chat_message_repository.go` | Repository GORM pour messages | | `internal/repositories/read_receipt_repository.go` | Repository read receipts | | `internal/repositories/delivered_status_repository.go` | Repository delivered status | | `internal/repositories/reaction_repository.go` | Repository reactions | | `internal/services/chat_pubsub.go` | Service Redis PubSub | | `internal/websocket/chat/hub.go` | Hub WebSocket (gestionnaire de connexions) | | `internal/websocket/chat/client.go` | Client WebSocket (readPump/writePump) | | `internal/websocket/chat/messages.go` | Types de messages JSON | | `internal/websocket/chat/handler.go` | Dispatch des messages | | `internal/websocket/chat/handler_messages.go` | Send/Edit/Delete message | | `internal/websocket/chat/handler_rooms.go` | Join/Leave conversation | | `internal/websocket/chat/handler_history.go` | History/Search/Sync | | `internal/websocket/chat/handler_realtime.go` | Typing/Read/Delivered/Reactions | | `internal/websocket/chat/handler_calls.go` | WebRTC signaling | | `internal/websocket/chat/permissions.go` | Permission checks | | `internal/websocket/chat/rate_limiter.go` | Rate limiting | ### Fichiers modifiés | Fichier | Modification | |---------|-------------| | `internal/models/message.go` | Ajout EditedAt, Status, IsPinned, Metadata | | `internal/services/chat_service.go` | WSUrl mis à jour, ValidateChatToken | | `internal/api/router.go` | Route /ws, initialisation Hub | | `docker-compose.yml` | Suppression service chat-server | | `docker-compose.staging.yml` | Suppression service chat-server | | `docker-compose.prod.yml` | Suppression service chat-server | ### Fichiers frontend modifiés | Fichier | Modification | |---------|-------------| | `apps/web/src/features/chat/hooks/useChat.ts` | URL WS, auth flow, types manquants | | `apps/web/src/features/chat/types/index.ts` | Types Edit/Delete/Search/Sync | | `apps/web/src/features/chat/store/chatStore.ts` | Suppression wsUrl, ajout handlers | | `apps/web/src/mocks/handlers-misc.ts` | Mocks chat mis à jour | --- ## Critères d'acceptance - [ ] Toutes les features du chat server Rust fonctionnent avec le server Go - [ ] Protocole WebSocket JSON identique (0 changement frontend sémantique) - [ ] Latence de livraison des messages < 100ms - [ ] Rate limiting fonctionnel par utilisateur et par action - [ ] Read receipts et delivered status persistés en DB - [ ] Réactions persistées avec broadcast temps réel - [ ] Typing indicators avec auto-clear 3s - [ ] Signalisation WebRTC (appels 1-to-1) fonctionnelle - [ ] Redis PubSub pour broadcasting multi-instance - [ ] Chat server Rust supprimé de Docker Compose - [ ] Tests E2E pour connexion, messages, typing, reactions - [ ] Document de feature parity validé - [ ] Go backend compile sans erreur - [ ] Frontend TypeScript compile sans erreur - [ ] Storybook sans erreur réseau/console --- ## Hors scope v0.502 - Chat groupé multi-room UI (refonte UX) → v0.503 - Fichier upload dans le chat (images, audio inline) → v0.503 - Notifications push pour messages chat → v0.503 - Modération avancée (sanctions, bans, mutes depuis UI) → v0.503 - Message threading UI (réponses imbriquées) → v0.503 - Recherche full-text avancée dans le chat → v0.503