# Migration Chat-Server Rust : i64 → UUID — Rapport complet **Date** : 2025-01-27 **Service** : `veza-chat-server` (Rust/Axum) **Objectif** : Migrer tous les IDs de `i64` vers `Uuid` pour cohérence avec le schéma DB et le backend Go --- ## Résumé exécutif - **Fichiers à modifier** : ~25 fichiers - **Structs à migrer** : 8 structures principales - **Requêtes SQL à mettre à jour** : ~50+ requêtes SQLx - **Messages WebSocket à migrer** : 5+ types de messages - **Estimation temps** : 4-6 heures - **Risque** : Moyen (nécessite tests exhaustifs) **État actuel** : - ✅ **Schéma DB** : Utilise `UUID` (colonnes `uuid`) mais aussi `BIGSERIAL` (colonnes `id`) - ❌ **Code Rust** : Utilise `i64` pour la plupart des IDs - ✅ **Frontend** : Envoie déjà des UUID strings - ⚠️ **Backend Go** : Mixte (certains handlers utilisent encore `int64`) **Problème identifié** : Le schéma DB a une **cohabitation BIGSERIAL/UUID** : - Colonnes `id` : `BIGSERIAL` (i64) - Colonnes `uuid` : `UUID` (Uuid) - Le code Rust utilise les colonnes `id` (i64) alors qu'il devrait utiliser `uuid` --- ## 1. Cartographie complète ### 1.1 Structures avec IDs à migrer | Struct | Fichier | Champs i64 | Champs déjà Uuid | Action | Priorité | |--------|---------|------------|------------------|--------|----------| | `Room` | `src/hub/channels.rs` | `id: i64`, `owner_id: i64` | `uuid: Uuid` | Supprimer `id`, renommer `uuid→id`, migrer `owner_id` | 🔴 Haute | | `RoomMember` | `src/hub/channels.rs` | `id: i64`, `conversation_id: i64`, `user_id: i64` | - | Migrer tous vers `Uuid` | 🔴 Haute | | `RoomMessage` | `src/hub/channels.rs` | `id: i64`, `author_id: i64`, `conversation_id: i64`, `parent_message_id: Option` | `uuid: Uuid` | Supprimer `id`, renommer `uuid→id`, migrer autres | 🔴 Haute | | `RoomStats` | `src/hub/channels.rs` | `room_id: i64` | - | Migrer vers `Uuid` | 🟡 Moyenne | | `EnhancedRoomMessage` | `src/hub/channels.rs` | `id: i64`, `author_id: i32`, `room_id: Option` | - | Migrer vers `Uuid` | 🟡 Moyenne | | `AuditLog` | `src/hub/audit.rs` | `id: i64`, `user_id: Option` | - | Migrer vers `Uuid` | 🟡 Moyenne | | `SecurityEvent` | `src/hub/audit.rs` | `id: i64`, `user_id: Option` | - | Migrer vers `Uuid` | 🟡 Moyenne | | `UserActivity` | `src/hub/audit.rs` | `user_id: i64` | - | Migrer vers `Uuid` | 🟡 Moyenne | | `RoomAuditSummary` | `src/hub/audit.rs` | `room_id: i64` | - | Migrer vers `Uuid` | 🟡 Moyenne | | `Message` | `src/models/message.rs` | - | `id: Uuid`, `conversation_id: Uuid`, `sender_id: Uuid` | ✅ Déjà migré | ✅ OK | | `WsInbound` | `src/messages.rs` | `to_user_id: i32`, `with: i32` | - | Migrer vers `Uuid` (string) | 🔴 Haute | **Total** : 10 structures à migrer (8 avec i64, 2 déjà OK) ### 1.2 Requêtes SQLx à mettre à jour #### Fichier : `src/hub/channels.rs` | Fonction | Ligne | Requête | Champs i64 concernés | Modification | |----------|-------|---------|---------------------|--------------| | `create_room` | 139-152 | `INSERT INTO conversations ... RETURNING id, uuid, ...` | `id`, `owner_id` | Utiliser `uuid` au lieu de `id`, migrer `owner_id` | | `join_room` | 198-220 | `SELECT id, uuid, ... FROM conversations WHERE id = $1` | `room_id`, `user_id` | Utiliser `uuid` au lieu de `id` | | `leave_room` | 254-290 | `SELECT id, ... FROM conversations WHERE id = $1` | `room_id`, `user_id` | Utiliser `uuid` | | `send_room_message` | 347-412 | `INSERT INTO messages ... RETURNING id` | `room_id`, `author_id`, `message_id`, `parent_message_id` | Utiliser `uuid` pour tous | | `pin_message` | 416-450 | `UPDATE messages ... WHERE id = $2` | `room_id`, `message_id`, `user_id` | Utiliser `uuid` | | `fetch_room_history` | 462-546 | `SELECT id, uuid, ... FROM messages WHERE conversation_id = $1` | `room_id`, `user_id`, `message_id` | Utiliser `uuid` | | `fetch_pinned_messages` | 548-593 | `SELECT ... FROM messages WHERE conversation_id = $1` | `room_id`, `user_id` | Utiliser `uuid` | | `get_room_stats` | 594-623 | `SELECT c.id as room_id, ...` | `room_id` | Utiliser `uuid` | | `list_room_members` | 625-670 | `SELECT ... FROM conversation_members WHERE conversation_id = $1` | `room_id`, `user_id` | Utiliser `uuid` | **Total dans channels.rs** : ~20 requêtes à modifier #### Fichier : `src/hub/audit.rs` | Fonction | Ligne | Requête | Champs i64 concernés | Modification | |----------|-------|---------|---------------------|--------------| | `log_action` | 81-100 | `INSERT INTO audit_logs ... RETURNING id` | `user_id: Option` | Migrer vers `Option` | | `log_security_event` | 112-137 | `INSERT INTO security_events ... RETURNING id` | `user_id: Option` | Migrer vers `Option` | | `log_room_created` | 150-173 | `log_action(..., room_id: i64, owner_id: i64)` | `room_id`, `owner_id` | Migrer vers `Uuid` | | `log_member_change` | 174-207 | `log_action(..., room_id: i64, target_user_id: i64, ...)` | `room_id`, `user_ids` | Migrer vers `Uuid` | | `log_message_modified` | 207-244 | `log_action(..., message_id: i64, room_id: i64, ...)` | Tous les IDs | Migrer vers `Uuid` | | `log_moderation_action` | 244-297 | `log_action(..., room_id: i64, ...)` | Tous les IDs | Migrer vers `Uuid` | | `get_room_audit_logs` | 297-346 | `SELECT ... FROM audit_logs WHERE ...` | `room_id`, `requesting_user_id` | Migrer vers `Uuid` | | `get_room_security_events` | 347-398 | `SELECT ... FROM security_events WHERE ...` | `room_id`, `requesting_user_id` | Migrer vers `Uuid` | | `generate_room_activity_report` | 399-515 | `SELECT ... WHERE room_id = $1` | `room_id`, `requesting_user_id` | Migrer vers `Uuid` | | `get_room_audit_summary` | 516-551 | `SELECT c.id as room_id, ...` | `room_id`, `requesting_user_id` | Migrer vers `Uuid` | | `detect_suspicious_patterns` | 552-590 | `SELECT ... WHERE room_id = $1` | `room_id` | Migrer vers `Uuid` | **Total dans audit.rs** : ~15 requêtes à modifier #### Autres fichiers | Fichier | Fonctions impactées | Requêtes | Priorité | |---------|---------------------|----------|----------| | `src/hub/direct_messages.rs` | Toutes fonctions DM | ~10 requêtes | 🔴 Haute | | `src/repository/room_repository.rs` | Toutes méthodes | ~8 requêtes | 🔴 Haute | | `src/repository/message_repository.rs` | Toutes méthodes | ~8 requêtes | 🔴 Haute | | `src/message_store.rs` | Store/retrieve | ~5 requêtes | 🟡 Moyenne | | `src/services/room_service.rs` | Service layer | ~5 requêtes | 🟡 Moyenne | **Total estimé** : ~60 requêtes SQLx à modifier ### 1.3 Conversions/parsing d'ID à migrer | Fichier | Ligne | Code actuel | Code cible | Contexte | |---------|-------|-------------|------------|----------| | `src/messages.rs` | 21 | `to_user_id: i32` | `to_user_id: String` (UUID string) | WebSocket inbound | | `src/messages.rs` | 33 | `with: i32` | `with: String` (UUID string) | WebSocket inbound | | `src/hub/channels.rs` | 122 | `owner_id: i64` | `owner_id: Uuid` | Paramètre fonction | | `src/hub/channels.rs` | 189 | `room_id: i64, user_id: i64` | `room_id: Uuid, user_id: Uuid` | Paramètres fonction | | `src/hub/channels.rs` | 326 | `author_id: i64` | `author_id: Uuid` | Paramètre fonction | | `src/hub/channels.rs` | 339 | `author_id as i32` | Supprimer conversion | Rate limiting | | `src/hub/channels.rs` | 383 | `message.get("id")` → `i64` | `message.get("uuid")` → `Uuid` | Récupération ID | | `src/hub/audit.rs` | 81 | `user_id: Option` | `user_id: Option` | Paramètre fonction | | `src/hub/audit.rs` | 150 | `room_id: i64, owner_id: i64` | `room_id: Uuid, owner_id: Uuid` | Paramètres fonction | **Patterns de conversion à chercher** : - `as i64` / `as i32` : Conversions explicites - `.parse::()` : Parsing depuis string - `get::("id")` : Récupération depuis SQLx Row - `validate_user_id(user_id as i32)` : Validation avec conversion ### 1.4 Messages/DTOs WebSocket à migrer | Struct | Fichier | Champs i64 | Sérialisé en JSON | Impact client | Action | |--------|---------|------------|-------------------|---------------|--------| | `WsInbound::DirectMessage` | `src/messages.rs` | `to_user_id: i32` | Oui | ❌ Frontend envoie UUID string | Migrer vers `String` (UUID) | | `WsInbound::DmHistory` | `src/messages.rs` | `with: i32` | Oui | ❌ Frontend envoie UUID string | Migrer vers `String` (UUID) | | `RoomMessage` | `src/hub/channels.rs` | `id: i64`, `author_id: i64`, `conversation_id: i64` | Oui | ⚠️ Frontend attend UUID string | Migrer vers `Uuid` (sérialisé en string) | | `Room` | `src/hub/channels.rs` | `id: i64`, `owner_id: i64` | Oui | ⚠️ Frontend attend UUID string | Migrer vers `Uuid` | | `RoomMember` | `src/hub/channels.rs` | `id: i64`, `user_id: i64` | Oui | ⚠️ Frontend attend UUID string | Migrer vers `Uuid` | **Note importante** : Le frontend envoie déjà des UUID strings (voir `apps/web/src/features/chat/types/index.ts`). Le problème est que le Rust attend des `i32`/`i64`. ### 1.5 Schéma DB (source de vérité) **Analyse du schéma** : `migrations/001_create_clean_database.sql` | Table | Colonne ID | Type DB | Colonne UUID | Type DB | Type Rust actuel | Conforme | Action | |-------|------------|---------|--------------|---------|------------------|----------|--------| | `users` | `id` | `BIGSERIAL` | `uuid` | `UUID` | `i64` | ❌ | Utiliser `uuid` | | `conversations` | `id` | `BIGSERIAL` | `uuid` | `UUID` | `i64` | ❌ | Utiliser `uuid` | | `conversation_members` | `id` | `BIGSERIAL` | - | - | `i64` | ❌ | **PROBLÈME** : Pas de colonne UUID | | `messages` | `id` | `BIGSERIAL` | `uuid` | `UUID` | `i64` | ❌ | Utiliser `uuid` | | `audit_logs` | `id` | `BIGSERIAL` | - | - | `i64` | ❌ | **PROBLÈME** : Pas de colonne UUID | | `security_events` | `id` | `BIGSERIAL` | - | - | `i64` | ❌ | **PROBLÈME** : Pas de colonne UUID | **Problème majeur identifié** : - Les tables `conversation_members`, `audit_logs`, `security_events` n'ont **PAS de colonne UUID** - Elles utilisent uniquement `BIGSERIAL` pour les IDs - **Solution** : Soit ajouter des colonnes UUID (migration DB), soit utiliser les IDs BIGSERIAL mais les convertir en UUID côté application **Recommandation** : Utiliser les colonnes `uuid` existantes et ajouter des migrations pour les tables sans UUID. --- ## 2. Impacts et dépendances ### 2.1 Communication avec le backend Go | Direction | Endpoint/Event | Format ID actuel (Rust) | Format attendu (Go) | Action | |-----------|---------------|------------------------|---------------------|--------| | Go → Rust | WebSocket token (JWT) | `user_id` dans JWT : `int64` | `user_id` : `uuid.UUID` | ⚠️ **PROBLÈME** : JWT contient int64 | | Go → Rust | HTTP webhook (si existe) | `user_id: i64` | `user_id: string (UUID)` | Vérifier si webhooks existent | | Rust → Go | Webhook callback (si existe) | `user_id: i64` | `user_id: string (UUID)` | Migrer vers UUID | **Problème identifié** : Le backend Go génère des tokens JWT avec `user_id` en `uuid.UUID`, mais le chat-server Rust pourrait s'attendre à un `int64`. À vérifier dans `src/auth.rs` et `src/jwt_manager.rs`. ### 2.2 Communication avec le Frontend | Message WS | Direction | Champ | Type actuel (Rust) | Type Frontend | Compatible | Action | |------------|-----------|-------|-------------------|---------------|------------|--------| | `NewMessage` | Server→Client | `message_id` | `i64` (number) | `string` (UUID) | ❌ | Migrer vers `Uuid` (sérialisé en string) | | `NewMessage` | Server→Client | `sender_id` | `i64` (number) | `string` (UUID) | ❌ | Migrer vers `Uuid` | | `NewMessage` | Server→Client | `conversation_id` | `i64` (number) | `string` (UUID) | ❌ | Migrer vers `Uuid` | | `join_room` | Client→Server | `room` | `String` (nom) | `string` (nom ou UUID) | ✅ | OK (utilise nom, pas ID) | | `direct_message` | Client→Server | `to_user_id` | `i32` (number) | `string` (UUID) | ❌ | Migrer vers `String` (UUID) | | `dm_history` | Client→Server | `with` | `i32` (number) | `string` (UUID) | ❌ | Migrer vers `String` (UUID) | **Résultat** : ❌ **Incompatible** - Le frontend envoie/reçoit des UUID strings, mais le Rust attend/envoie des `i64`. ### 2.3 Tests existants | Fichier test | Test | Utilise i64 | Modification | |--------------|------|-------------|--------------| | `src/hub/channels.rs` (tests inline) | `test_room_creation` | Probable | Changer en `Uuid::new_v4()` | | `tests/integration_test.rs` (si existe) | Tests d'intégration | Probable | Migrer vers UUID | | Tests unitaires | Tous | Probable | Migrer vers UUID | **Action** : Vérifier avec `grep -r "#\[test\]" veza-chat-server/src/` et mettre à jour tous les tests. --- ## 3. Plan de migration détaillé ### 3.1 Ordre des modifications (bottom-up) #### Étape 1 : Préparation (sans changement fonctionnel) 1. [ ] Vérifier `Cargo.toml` : `uuid` avec features `["v4", "serde"]` ✅ (déjà présent) 2. [ ] Vérifier `Cargo.toml` : `sqlx` avec feature `uuid` ✅ (déjà présent) 3. [ ] Créer branche : `git checkout -b fix/chat-server-uuid-migration` 4. [ ] Tag de sauvegarde : `git tag pre-uuid-migration-chat-server` #### Étape 2 : Migration des structs (du plus simple au plus complexe) **Ordre recommandé** : 1. [ ] `src/models/message.rs` - ✅ Déjà migré, vérifier seulement 2. [ ] `src/messages.rs` - Migrer `WsInbound` (simple, pas de DB) 3. [ ] `src/hub/channels.rs` - Migrer `Room`, `RoomMember`, `RoomMessage` (complexe) 4. [ ] `src/hub/audit.rs` - Migrer structs d'audit 5. [ ] Autres structs dans autres fichiers #### Étape 3 : Migration des requêtes SQLx **Ordre recommandé** : 1. [ ] `src/hub/channels.rs` - Toutes les requêtes (fonctions principales) 2. [ ] `src/hub/audit.rs` - Toutes les requêtes d'audit 3. [ ] `src/hub/direct_messages.rs` - Requêtes DM 4. [ ] `src/repository/*.rs` - Repositories 5. [ ] Autres fichiers avec requêtes SQL #### Étape 4 : Migration handlers/WebSocket 1. [ ] `src/websocket/handler.rs` - Handlers WebSocket 2. [ ] `src/websocket/broadcast.rs` - Broadcast messages 3. [ ] `src/message_handler.rs` - Message handlers 4. [ ] Autres handlers #### Étape 5 : Tests 1. [ ] Mettre à jour tous les tests unitaires 2. [ ] Mettre à jour les tests d'intégration 3. [ ] Ajouter des tests de conversion UUID ### 3.2 Modifications fichier par fichier #### Fichier : `src/messages.rs` **Modification** : Migrer `WsInbound` pour accepter des UUID strings ```rust // AVANT #[derive(Debug, Deserialize)] #[serde(tag = "type")] pub enum WsInbound { #[serde(rename = "direct_message")] DirectMessage { to_user_id: i32, // ❌ content: String, }, #[serde(rename = "dm_history")] DmHistory { with: i32, // ❌ limit: i64, } } // APRÈS #[derive(Debug, Deserialize)] #[serde(tag = "type")] pub enum WsInbound { #[serde(rename = "direct_message")] DirectMessage { to_user_id: String, // ✅ UUID string depuis frontend content: String, }, #[serde(rename = "dm_history")] DmHistory { with: String, // ✅ UUID string depuis frontend limit: i64, } } ``` **Fonctions impactées** : Aucune (juste parsing) --- #### Fichier : `src/hub/channels.rs` **Modification 1** : Struct `Room` ```rust // AVANT #[derive(Debug, FromRow, Serialize, Deserialize)] pub struct Room { pub id: i64, // ❌ pub uuid: Uuid, // ✅ Existe déjà pub name: String, pub description: Option, pub owner_id: i64, // ❌ pub is_public: bool, pub is_archived: bool, pub max_members: Option, pub created_at: DateTime, pub updated_at: DateTime, } // APRÈS #[derive(Debug, FromRow, Serialize, Deserialize)] pub struct Room { pub id: Uuid, // ✅ Renommé depuis uuid pub name: String, pub description: Option, pub owner_id: Uuid, // ✅ Migré pub is_public: bool, pub is_archived: bool, pub max_members: Option, pub created_at: DateTime, pub updated_at: DateTime, } ``` **Modification 2** : Struct `RoomMember` ```rust // AVANT #[derive(Debug, FromRow, Serialize, Deserialize)] pub struct RoomMember { pub id: i64, // ❌ pub conversation_id: i64, // ❌ pub user_id: i64, // ❌ pub role: String, pub joined_at: DateTime, pub left_at: Option>, pub is_muted: bool, } // APRÈS #[derive(Debug, FromRow, Serialize, Deserialize)] pub struct RoomMember { pub id: Uuid, // ✅ pub conversation_id: Uuid, // ✅ pub user_id: Uuid, // ✅ pub role: String, pub joined_at: DateTime, pub left_at: Option>, pub is_muted: bool, } ``` **Modification 3** : Struct `RoomMessage` ```rust // AVANT #[derive(Debug, FromRow, Serialize)] pub struct RoomMessage { pub id: i64, // ❌ pub uuid: Uuid, // ✅ Existe déjà pub author_id: i64, // ❌ pub author_username: String, pub conversation_id: i64, // ❌ pub content: String, pub parent_message_id: Option, // ❌ // ... } // APRÈS #[derive(Debug, FromRow, Serialize)] pub struct RoomMessage { pub id: Uuid, // ✅ Renommé depuis uuid pub author_id: Uuid, // ✅ pub author_username: String, pub conversation_id: Uuid, // ✅ pub content: String, pub parent_message_id: Option, // ✅ // ... } ``` **Modification 4** : Fonction `create_room` ```rust // AVANT pub async fn create_room( hub: &ChatHub, owner_id: i64, // ❌ name: &str, // ... ) -> Result { let room_uuid = Uuid::new_v4(); let conversation = query_as::<_, Room>(" INSERT INTO conversations (uuid, type, name, description, owner_id, is_public, max_members) VALUES ($1, 'public_room', $2, $3, $4, $5, $6) RETURNING id, uuid, name, description, owner_id, is_public, is_archived, max_members, created_at, updated_at ") .bind(room_uuid) .bind(owner_id) // ❌ i64 // ... } // APRÈS pub async fn create_room( hub: &ChatHub, owner_id: Uuid, // ✅ name: &str, // ... ) -> Result { let room_uuid = Uuid::new_v4(); let conversation = query_as::<_, Room>(" INSERT INTO conversations (uuid, type, name, description, owner_id, is_public, max_members) VALUES ($1, 'public_room', $2, $3, $4, $5, $6) RETURNING uuid as id, name, description, owner_id, is_public, is_archived, max_members, created_at, updated_at ") .bind(room_uuid) .bind(owner_id) // ✅ Uuid // ... } ``` **Note** : La requête SQL doit utiliser `uuid as id` pour mapper la colonne `uuid` vers le champ `id` de la struct. **Modification 5** : Fonction `send_room_message` ```rust // AVANT pub async fn send_room_message( hub: &ChatHub, room_id: i64, // ❌ author_id: i64, // ❌ username: &str, content: &str, parent_message_id: Option, // ❌ metadata: Option ) -> Result { // ❌ Retourne i64 // ... let message = query(" INSERT INTO messages (uuid, author_id, conversation_id, content, parent_message_id, metadata, status) VALUES ($1, $2, $3, $4, $5, $6, 'sent') RETURNING id, created_at ") .bind(message_uuid) .bind(author_id) // ❌ i64 .bind(room_id) // ❌ i64 .bind(parent_message_id) // ❌ Option // ... let message_id: i64 = message.get("id"); // ❌ // ... Ok(message_id) // ❌ } // APRÈS pub async fn send_room_message( hub: &ChatHub, room_id: Uuid, // ✅ author_id: Uuid, // ✅ username: &str, content: &str, parent_message_id: Option, // ✅ metadata: Option ) -> Result { // ✅ Retourne Uuid // ... let message = query(" INSERT INTO messages (uuid, author_id, conversation_id, content, parent_message_id, metadata, status) VALUES ($1, $2, $3, $4, $5, $6, 'sent') RETURNING uuid as id, created_at ") .bind(message_uuid) .bind(author_id) // ✅ Uuid .bind(room_id) // ✅ Uuid .bind(parent_message_id) // ✅ Option // ... let message_id: Uuid = message.get("id"); // ✅ (depuis uuid as id) // ... Ok(message_id) // ✅ } ``` **Toutes les autres fonctions** : Même pattern - remplacer `i64` par `Uuid` dans les paramètres et utiliser `uuid as id` dans les requêtes SQL. --- #### Fichier : `src/hub/audit.rs` **Modification** : Toutes les fonctions utilisent `i64` pour les IDs. Migrer vers `Uuid`. ```rust // AVANT pub async fn log_action( hub: &ChatHub, action: &str, details: Value, user_id: Option, // ❌ // ... ) -> Result { // ❌ // ... } // APRÈS pub async fn log_action( hub: &ChatHub, action: &str, details: Value, user_id: Option, // ✅ // ... ) -> Result { // ✅ // ... } ``` **Note** : Les tables `audit_logs` et `security_events` n'ont pas de colonne `uuid`. Deux options : 1. **Option A (recommandée)** : Ajouter une migration DB pour ajouter des colonnes `uuid` 2. **Option B** : Garder `BIGSERIAL` pour ces tables (moins idéal) --- ### 3.3 Gestion de la sérialisation JSON **Configuration Serde** : Avec `uuid = { version = "1.6", features = ["v4", "serde"] }`, les `Uuid` se sérialisent automatiquement en strings. **Vérification** : Le JSON produit sera : ```json { "id": "550e8400-e29b-41d4-a716-446655440000", "name": "General" } ``` **Pas besoin de configuration spéciale** - Serde gère automatiquement. ### 3.4 Gestion des requêtes SQLx **Pattern de migration** : ```rust // AVANT (i64) let room = query_as::<_, Room>(" SELECT id, uuid, name, description, owner_id, is_public, is_archived, max_members, created_at, updated_at FROM conversations WHERE id = $1 ") .bind(room_id) // i64 .fetch_one(&pool) .await?; // APRÈS (Uuid) let room = query_as::<_, Room>(" SELECT uuid as id, name, description, owner_id, is_public, is_archived, max_members, created_at, updated_at FROM conversations WHERE uuid = $1 ") .bind(room_id) // Uuid .fetch_one(&pool) .await?; ``` **Points d'attention** : 1. Utiliser `uuid as id` dans les SELECT pour mapper vers le champ `id` de la struct 2. Utiliser `WHERE uuid = $1` au lieu de `WHERE id = $1` 3. Les paramètres `$1, $2, ...` doivent être de type `Uuid` 4. SQLx vérifie les types au compile-time - les erreurs seront explicites --- ## 4. Gestion des erreurs et rollback ### 4.1 Points de rollback **Stratégie de commits** : #### Commit 1 : Préparation ```bash git commit -m "chore(chat-server): prepare UUID migration dependencies" ``` - Vérifier/ajouter dépendances Cargo.toml ✅ (déjà présentes) - Créer types/ids.rs si nécessaire (optionnel) #### Commit 2 : Migration des structs ```bash git commit -m "refactor(chat-server): migrate structs from i64 to Uuid" ``` - Modifier toutes les structs - **Le code NE COMPILE PAS encore** (c'est normal) #### Commit 3 : Migration des requêtes DB ```bash git commit -m "refactor(chat-server): migrate SQLx queries to Uuid" ``` - Modifier toutes les requêtes SQLx - **Le code devrait compiler maintenant** #### Commit 4 : Migration handlers/WebSocket ```bash git commit -m "refactor(chat-server): migrate handlers and WS to Uuid" ``` - Modifier les handlers - Modifier les messages WS #### Commit 5 : Tests ```bash git commit -m "test(chat-server): update tests for UUID migration" ``` - Mettre à jour tous les tests - Tous les tests passent #### Tag final ```bash git tag chat-server-uuid-migration-complete ``` ### 4.2 Erreurs attendues et solutions #### Erreur 1 : Type mismatch dans query_as! ``` error: type mismatch: expected `i64`, found `Uuid` ``` **Solution** : Vérifier que la struct ET la requête utilisent le même type. Utiliser `uuid as id` dans le SELECT. #### Erreur 2 : Cannot convert i64 to Uuid ``` error: the trait `From` is not implemented for `Uuid` ``` **Solution** : Il reste du code qui utilise i64 — chercher avec `grep -r "i64" src/ | grep -v test` #### Erreur 3 : Serde désérialisation échoue ``` error: invalid type: integer, expected a string ``` **Solution** : Le client envoie un number au lieu d'un string UUID. Vérifier le frontend ou accepter les deux formats temporairement. #### Erreur 4 : SQLx compile-time check échoue ``` error: column "id" is of type uuid but expression is of type bigint ``` **Solution** : La requête SQL utilise encore un paramètre i64. Migrer vers Uuid. --- ## 5. Validation et tests ### 5.1 Tests de non-régression #### Tests unitaires Rust ```bash cd veza-chat-server cargo test ``` #### Test d'intégration DB ```bash # Vérifier que les requêtes fonctionnent avec la vraie DB DATABASE_URL="postgres://..." cargo test --features integration ``` #### Test WebSocket manuel ```bash # Avec websocat ou wscat wscat -c ws://localhost:8080/ws # Envoyer un message avec UUID {"type": "join_room", "room": "general"} {"type": "direct_message", "to_user_id": "550e8400-e29b-41d4-a716-446655440000", "content": "test"} # Vérifier la réponse (doit contenir des UUID strings, pas des numbers) ``` #### Test intégration Backend Go ↔ Chat Server ```bash # Depuis le backend Go, obtenir un token curl -X GET http://localhost:8080/api/v1/chat/token \ -H "Authorization: Bearer " # Vérifier que le token contient un UUID (pas un int64) ``` #### Test Frontend 1. Ouvrir l'app web 2. Rejoindre un chat room 3. Envoyer un message 4. Vérifier dans la console réseau que les IDs sont des strings UUID ### 5.2 Checklist finale #### Compilation - [ ] `cargo build --release` passe sans warning - [ ] `cargo clippy` passe sans erreur - [ ] `cargo test` — tous les tests passent #### Cohérence des types - [ ] Aucun `i64` pour des IDs dans src/ (vérifier avec `grep -r "i64" src/ | grep -v test | grep -v limit | grep -v count`) - [ ] Tous les champs ID sont de type `Uuid` - [ ] Toutes les requêtes SQLx utilisent `Uuid` #### Sérialisation JSON - [ ] Les réponses JSON contiennent des UUID strings (pas des numbers) - [ ] Les requêtes JSON acceptent des UUID strings #### Intégration - [ ] Le backend Go peut communiquer avec le chat-server - [ ] Le frontend peut se connecter et envoyer/recevoir des messages - [ ] Les IDs dans les messages WebSocket sont des strings #### Documentation - [ ] README mis à jour si nécessaire - [ ] Commentaires de code à jour --- ## 6. Commandes d'exécution ```bash # Étape 1 : Créer branche git checkout -b fix/chat-server-uuid-migration # Étape 2 : Tag de sauvegarde git tag pre-uuid-migration-chat-server # Étape 3 : Appliquer les modifications (voir sections 3.2) # Étape 4 : Tester cd veza-chat-server cargo build --release cargo test # Étape 5 : Commit git add . git commit -m "refactor(chat-server): migrate all IDs from i64 to Uuid" # Étape 6 : Tag final git tag chat-server-uuid-migration-complete ``` --- ## 7. Questions à clarifier ### 7.1 Schéma DB - Tables sans UUID **Problème** : Les tables `conversation_members`, `audit_logs`, `security_events` n'ont pas de colonne `uuid`. **Options** : 1. **Ajouter des colonnes UUID** (migration DB) - Recommandé 2. **Garder BIGSERIAL** et convertir en UUID côté application - Moins idéal **Recommandation** : Créer une migration pour ajouter des colonnes `uuid` à ces tables. ### 7.2 Backend Go - Handlers avec int64 **Problème** : `veza-backend-api/internal/api/handlers/chat_handlers.go` utilise encore `strconv.ParseInt` pour les room_id. **Action** : Migrer aussi le backend Go (hors scope de ce rapport, mais à noter). ### 7.3 JWT Tokens - Format user_id **Question** : Le JWT généré par le backend Go contient-il `user_id` en UUID ou int64 ? **Action** : Vérifier dans `src/auth.rs` et `src/jwt_manager.rs` comment le JWT est parsé. --- ## 8. Résumé des modifications ### Fichiers à modifier (ordre de priorité) 1. 🔴 **Haute priorité** : - `src/messages.rs` - WebSocket inbound messages - `src/hub/channels.rs` - Structures et fonctions principales - `src/hub/direct_messages.rs` - Direct messages - `src/repository/room_repository.rs` - Repository layer - `src/repository/message_repository.rs` - Repository layer 2. 🟡 **Moyenne priorité** : - `src/hub/audit.rs` - Audit logs - `src/services/room_service.rs` - Service layer - `src/message_store.rs` - Message storage - `src/websocket/handler.rs` - WebSocket handlers - `src/websocket/broadcast.rs` - Broadcast messages 3. 🟢 **Basse priorité** : - Tests unitaires - Documentation - Autres fichiers avec IDs ### Statistiques - **Structs à migrer** : 10 - **Fonctions à modifier** : ~40 - **Requêtes SQL à mettre à jour** : ~60 - **Lignes de code à modifier** : ~500-800 - **Temps estimé** : 4-6 heures --- **Document généré le** : 2025-01-27 **Prochaine étape** : Commencer la migration avec l'étape 1 (préparation)