# 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<i64>` | `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<i32>` | - | Migrer vers `Uuid` | 🟡 Moyenne |
| `AuditLog` | `src/hub/audit.rs` | `id: i64`, `user_id: Option<i64>` | - | Migrer vers `Uuid` | 🟡 Moyenne |
| `SecurityEvent` | `src/hub/audit.rs` | `id: i64`, `user_id: Option<i64>` | - | 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<i64>` | Migrer vers `Option<Uuid>` |
| `log_security_event` | 112-137 | `INSERT INTO security_events ... RETURNING id` | `user_id: Option<i64>` | Migrer vers `Option<Uuid>` |
| `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<i64>` | `user_id: Option<Uuid>` | 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::<i64>()` : Parsing depuis string
- `get::<i64, _>("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<String>,
    pub owner_id: i64,     // ❌
    pub is_public: bool,
    pub is_archived: bool,
    pub max_members: Option<i32>,
    pub created_at: DateTime<Utc>,
    pub updated_at: DateTime<Utc>,
}

// APRÈS
#[derive(Debug, FromRow, Serialize, Deserialize)]
pub struct Room {
    pub id: Uuid,          // ✅ Renommé depuis uuid
    pub name: String,
    pub description: Option<String>,
    pub owner_id: Uuid,    // ✅ Migré
    pub is_public: bool,
    pub is_archived: bool,
    pub max_members: Option<i32>,
    pub created_at: DateTime<Utc>,
    pub updated_at: DateTime<Utc>,
}
```

**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<Utc>,
    pub left_at: Option<DateTime<Utc>>,
    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<Utc>,
    pub left_at: Option<DateTime<Utc>>,
    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<i64>, // ❌
    // ...
}

// 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<Uuid>, // ✅
    // ...
}
```

**Modification 4** : Fonction `create_room`

```rust
// AVANT
pub async fn create_room(
    hub: &ChatHub,
    owner_id: i64,  // ❌
    name: &str,
    // ...
) -> Result<Room> {
    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<Room> {
    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<i64>,  // ❌
    metadata: Option<Value>
) -> Result<i64> {  // ❌ 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<i64>
    // ...
    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<Uuid>,  // ✅
    metadata: Option<Value>
) -> Result<Uuid> {  // ✅ 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<Uuid>
    // ...
    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<i64>,  // ❌
    // ...
) -> Result<i64> {  // ❌
    // ...
}

// APRÈS
pub async fn log_action(
    hub: &ChatHub,
    action: &str,
    details: Value,
    user_id: Option<Uuid>,  // ✅
    // ...
) -> Result<Uuid> {  // ✅
    // ...
}
```

**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<i64>` 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 <jwt_token>"

# 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)

