Backend Go: - Remplacement complet des anciennes migrations par la base V1 alignée sur ORIGIN. - Durcissement global du parsing JSON (BindAndValidateJSON + RespondWithAppError). - Sécurisation de config.go, CORS, statuts de santé et monitoring. - Implémentation des transactions P0 (RBAC, duplication de playlists, social toggles). - Ajout d’un job worker structuré (emails, analytics, thumbnails) + tests associés. - Nouvelle doc backend : AUDIT_CONFIG, BACKEND_CONFIG, AUTH_PASSWORD_RESET, JOB_WORKER_*. Chat server (Rust): - Refonte du pipeline JWT + sécurité, audit et rate limiting avancé. - Implémentation complète du cycle de message (read receipts, delivered, edit/delete, typing). - Nettoyage des panics, gestion d’erreurs robuste, logs structurés. - Migrations chat alignées sur le schéma UUID et nouvelles features. Stream server (Rust): - Refonte du moteur de streaming (encoding pipeline + HLS) et des modules core. - Transactions P0 pour les jobs et segments, garanties d’atomicité. - Documentation détaillée de la pipeline (AUDIT_STREAM_*, DESIGN_STREAM_PIPELINE, TRANSACTIONS_P0_IMPLEMENTATION). Documentation & audits: - TRIAGE.md et AUDIT_STABILITY.md à jour avec l’état réel des 3 services. - Cartographie complète des migrations et des transactions (DB_MIGRATIONS_*, DB_TRANSACTION_PLAN, AUDIT_DB_TRANSACTIONS, TRANSACTION_TESTS_PHASE3). - Scripts de reset et de cleanup pour la lab DB et la V1. Ce commit fige l’ensemble du travail de stabilisation P0 (UUID, backend, chat et stream) avant les phases suivantes (Coherence Guardian, WS hardening, etc.).
29 KiB
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(colonnesuuid) mais aussiBIGSERIAL(colonnesid) - ❌ Code Rust : Utilise
i64pour 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 utiliseruuid
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 stringget::<i64, _>("id"): Récupération depuis SQLx Rowvalidate_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_eventsn'ont PAS de colonne UUID - Elles utilisent uniquement
BIGSERIALpour 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)
- Vérifier
Cargo.toml:uuidavec features["v4", "serde"]✅ (déjà présent) - Vérifier
Cargo.toml:sqlxavec featureuuid✅ (déjà présent) - Créer branche :
git checkout -b fix/chat-server-uuid-migration - Tag de sauvegarde :
git tag pre-uuid-migration-chat-server
Étape 2 : Migration des structs (du plus simple au plus complexe)
Ordre recommandé :
src/models/message.rs- ✅ Déjà migré, vérifier seulementsrc/messages.rs- MigrerWsInbound(simple, pas de DB)src/hub/channels.rs- MigrerRoom,RoomMember,RoomMessage(complexe)src/hub/audit.rs- Migrer structs d'audit- Autres structs dans autres fichiers
Étape 3 : Migration des requêtes SQLx
Ordre recommandé :
src/hub/channels.rs- Toutes les requêtes (fonctions principales)src/hub/audit.rs- Toutes les requêtes d'auditsrc/hub/direct_messages.rs- Requêtes DMsrc/repository/*.rs- Repositories- Autres fichiers avec requêtes SQL
Étape 4 : Migration handlers/WebSocket
src/websocket/handler.rs- Handlers WebSocketsrc/websocket/broadcast.rs- Broadcast messagessrc/message_handler.rs- Message handlers- Autres handlers
Étape 5 : Tests
- Mettre à jour tous les tests unitaires
- Mettre à jour les tests d'intégration
- Ajouter des tests de conversion UUID
3.2 Modifications fichier par fichier
Fichier : src/messages.rs
Modification : Migrer WsInbound pour accepter des UUID strings
// 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
// 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
// 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
// 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
// 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
// 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.
// 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 :
- Option A (recommandée) : Ajouter une migration DB pour ajouter des colonnes
uuid - Option B : Garder
BIGSERIALpour 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 :
{
"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 :
// 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 :
- Utiliser
uuid as iddans les SELECT pour mapper vers le champidde la struct - Utiliser
WHERE uuid = $1au lieu deWHERE id = $1 - Les paramètres
$1, $2, ...doivent être de typeUuid - 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
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
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
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
git commit -m "refactor(chat-server): migrate handlers and WS to Uuid"
- Modifier les handlers
- Modifier les messages WS
Commit 5 : Tests
git commit -m "test(chat-server): update tests for UUID migration"
- Mettre à jour tous les tests
- Tous les tests passent
Tag final
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
cd veza-chat-server
cargo test
Test d'intégration DB
# Vérifier que les requêtes fonctionnent avec la vraie DB
DATABASE_URL="postgres://..." cargo test --features integration
Test WebSocket manuel
# 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
# 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
- Ouvrir l'app web
- Rejoindre un chat room
- Envoyer un message
- Vérifier dans la console réseau que les IDs sont des strings UUID
5.2 Checklist finale
Compilation
cargo build --releasepasse sans warningcargo clippypasse sans erreurcargo test— tous les tests passent
Cohérence des types
- Aucun
i64pour des IDs dans src/ (vérifier avecgrep -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
# É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 :
- Ajouter des colonnes UUID (migration DB) - Recommandé
- 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é)
-
🔴 Haute priorité :
src/messages.rs- WebSocket inbound messagessrc/hub/channels.rs- Structures et fonctions principalessrc/hub/direct_messages.rs- Direct messagessrc/repository/room_repository.rs- Repository layersrc/repository/message_repository.rs- Repository layer
-
🟡 Moyenne priorité :
src/hub/audit.rs- Audit logssrc/services/room_service.rs- Service layersrc/message_store.rs- Message storagesrc/websocket/handler.rs- WebSocket handlerssrc/websocket/broadcast.rs- Broadcast messages
-
🟢 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)