veza/docs/archive/root-md/CHAT_SERVER_UUID_MIGRATION.md
senke 43af35fd93 chore(audit 2.2, 2.3): nettoyer .md et .json à la racine
- Archiver 131 .md dans docs/archive/root-md/
- Archiver 22 .json dans docs/archive/root-json/
- Conserver 7 .md utiles (README, CONTRIBUTING, CHANGELOG, etc.)
- Conserver package.json, package-lock.json, turbo.json
- Ajouter README d'index dans chaque archive
2026-02-15 14:35:08 +01:00

795 lines
29 KiB
Markdown

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