- 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
795 lines
29 KiB
Markdown
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)
|
|
|