1020 lines
32 KiB
Markdown
1020 lines
32 KiB
Markdown
|
|
# 🔍 AUDIT TECHNIQUE EXHAUSTIF - CHAT SERVER RUST
|
||
|
|
## Veza/Talas - Serveur de Chat Temps-Réel
|
||
|
|
|
||
|
|
**Date**: 2025-01-27
|
||
|
|
**Version analysée**: 0.2.0
|
||
|
|
**Auditeur**: Auto (AI Agent)
|
||
|
|
**Durée audit**: Exhaustif multi-phases
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 📋 SECTION 1 : RÉSUMÉ EXÉCUTIF
|
||
|
|
|
||
|
|
### État Global du Projet
|
||
|
|
|
||
|
|
| Composant | État | Complétude | Notes |
|
||
|
|
|-----------|------|------------|-------|
|
||
|
|
| **WebSocket Server** | 🟡 Partiel | 65% | Framework Axum OK, auth JWT présente mais incomplète |
|
||
|
|
| **Messages 1-to-1** | 🟢 Fonctionnel | 80% | Implémenté via conversations, persistence OK |
|
||
|
|
| **Group Chat/Channels** | 🟡 Partiel | 50% | Structure présente, permissions incomplètes |
|
||
|
|
| **Typing Indicators** | 🟢 Fonctionnel | 75% | Implémenté, timeout 3s, cleanup présent |
|
||
|
|
| **Read Receipts** | 🟡 Partiel | 40% | Structure DB présente, logique TODO ligne 258 |
|
||
|
|
| **Presence** | 🟢 Fonctionnel | 70% | Online/Away/Busy/Offline, multi-device partiel |
|
||
|
|
| **Modération** | 🟡 Partiel | 60% | Auto-modération basique, sanctions DB présentes |
|
||
|
|
| **Performance** | 🟡 À améliorer | 55% | Pas de benchmarks, optimisations manquantes |
|
||
|
|
| **Sécurité** | 🟠 Critique | 45% | Auth WebSocket OK, rate limiting présent, validation input partielle |
|
||
|
|
| **Tests** | 🔴 Insuffisant | 25% | Tests unitaires rares, integration tests ignorés |
|
||
|
|
| **Documentation** | 🟡 Partielle | 40% | Doc comments présents, exemples manquants |
|
||
|
|
|
||
|
|
### Top 10 Problèmes Critiques
|
||
|
|
|
||
|
|
1. **🔴 P0 - Auth WebSocket Token dans Query Params** : Token JWT exposé dans URL (ligne 43 `main.rs`)
|
||
|
|
2. **🔴 P0 - Read Receipts Non Implémentés** : TODO ligne 258 `websocket/handler.rs`
|
||
|
|
3. **🔴 P0 - Tests Coverage < 30%** : Tests d'intégration ignorés, unitaires rares
|
||
|
|
4. **🟠 P1 - Clippy Warnings Non Résolus** : 10+ warnings compilation (unused imports, PartialEq manquant)
|
||
|
|
5. **🟠 P1 - Rate Limiting Non Appliqué** : `DosProtectionManager` présent mais non utilisé dans handler
|
||
|
|
6. **🟠 P1 - Validation Input Messages** : Pas de sanitization XSS, longueur max non vérifiée
|
||
|
|
7. **🟠 P1 - Heartbeat/Ping-Pong Incomplet** : Ping géré, mais pas de timeout idle connections
|
||
|
|
8. **🟡 P2 - Modération Auto Basique** : Détection spam simpliste (similarité Levenshtein)
|
||
|
|
9. **🟡 P2 - Pas de Redis Pub/Sub** : Broadcast limité à instance unique (pas de scaling horizontal)
|
||
|
|
10. **🟡 P2 - Metrics Prometheus Non Utilisées** : Métriques définies mais non enregistrées dans handlers
|
||
|
|
|
||
|
|
### Effort Total Correction Estimé
|
||
|
|
|
||
|
|
- **P0 (Bloquants)**: 3-5 jours
|
||
|
|
- **P1 (Critiques)**: 5-7 jours
|
||
|
|
- **P2 (Majeurs)**: 7-10 jours
|
||
|
|
- **P3 (Mineurs)**: 3-5 jours
|
||
|
|
- **TOTAL**: 18-27 jours (3.5-5 semaines)
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 📊 SECTION 2 : CARTOGRAPHIE
|
||
|
|
|
||
|
|
### 2.1 Arborescence Rust
|
||
|
|
|
||
|
|
```
|
||
|
|
veza-chat-server/
|
||
|
|
├── src/
|
||
|
|
│ ├── main.rs ✅ Point d'entrée (Axum server)
|
||
|
|
│ ├── lib.rs ✅ Library root
|
||
|
|
│ ├── config.rs ✅ Configuration (env vars)
|
||
|
|
│ ├── error.rs ✅ Error types (thiserror)
|
||
|
|
│ ├── jwt_manager.rs ✅ JWT avec refresh tokens
|
||
|
|
│ ├── websocket/
|
||
|
|
│ │ ├── mod.rs ✅ Types messages WS
|
||
|
|
│ │ ├── handler.rs ⚠️ Handler WS (auth query param)
|
||
|
|
│ │ └── broadcast.rs ✅ Broadcast manager
|
||
|
|
│ ├── repository/
|
||
|
|
│ │ ├── message_repository.rs ✅ CRUD messages (SQLx)
|
||
|
|
│ │ └── room_repository.rs ✅ CRUD rooms
|
||
|
|
│ ├── models/
|
||
|
|
│ │ └── message.rs ✅ Modèle Message (UUID)
|
||
|
|
│ ├── services/
|
||
|
|
│ │ └── room_service.rs ✅ Service rooms
|
||
|
|
│ ├── auth.rs ⚠️ Auth manager (non utilisé)
|
||
|
|
│ ├── authentication.rs ⚠️ Auth alternatif (non utilisé)
|
||
|
|
│ ├── typing_indicator.rs ✅ Typing indicators
|
||
|
|
│ ├── read_receipts.rs ⚠️ Read receipts (i64 au lieu UUID)
|
||
|
|
│ ├── presence.rs ✅ Presence manager
|
||
|
|
│ ├── moderation.rs ⚠️ Modération (i32 user_id)
|
||
|
|
│ ├── rate_limiter.rs ⚠️ Rate limiter (non utilisé)
|
||
|
|
│ ├── prometheus_metrics.rs ⚠️ Metrics définies mais non utilisées
|
||
|
|
│ ├── event_bus.rs ✅ RabbitMQ EventBus
|
||
|
|
│ ├── database/
|
||
|
|
│ │ └── pool.rs ✅ Pool PostgreSQL
|
||
|
|
│ ├── security/
|
||
|
|
│ │ └── csrf.rs ✅ CSRF tokens
|
||
|
|
│ ├── core/ ⚠️ Modules non utilisés (duplication?)
|
||
|
|
│ └── hub/ ⚠️ Modules non utilisés (duplication?)
|
||
|
|
├── migrations/
|
||
|
|
│ ├── 001_create_clean_database.sql ✅ Schéma base
|
||
|
|
│ └── 002_advanced_features.sql ✅ Features avancées
|
||
|
|
├── tests/
|
||
|
|
│ └── integration_test.rs ⚠️ Tests ignorés (#[ignore])
|
||
|
|
├── Cargo.toml ✅ Dépendances complètes
|
||
|
|
└── env.example ✅ Configuration exemple
|
||
|
|
```
|
||
|
|
|
||
|
|
**Problèmes détectés**:
|
||
|
|
- Duplication: `auth.rs` vs `authentication.rs` vs `jwt_manager.rs`
|
||
|
|
- Modules non utilisés: `core/`, `hub/` (code mort?)
|
||
|
|
- Incohérence types: `i32` vs `UUID` (read_receipts, moderation)
|
||
|
|
|
||
|
|
### 2.2 Dépendances Cargo
|
||
|
|
|
||
|
|
| Catégorie | Dépendance | Version | État | Notes |
|
||
|
|
|-----------|------------|--------|------|-------|
|
||
|
|
| **Runtime** | tokio | 1.35 | ✅ | Full features |
|
||
|
|
| **WebSocket** | axum | 0.8 | ✅ | Framework moderne |
|
||
|
|
| **WebSocket** | tokio-tungstenite | 0.21 | ✅ | Core WS |
|
||
|
|
| **Database** | sqlx | 0.8.6 | ✅ | PostgreSQL, macros |
|
||
|
|
| **Cache** | redis | 0.32 | ⚠️ | Optional, non utilisé |
|
||
|
|
| **Auth** | jsonwebtoken | 9.2 | ✅ | JWT |
|
||
|
|
| **Serialization** | serde | 1.0 | ✅ | JSON |
|
||
|
|
| **Logging** | tracing | 0.1 | ✅ | Structured logging |
|
||
|
|
| **Metrics** | metrics | 0.22 | ⚠️ | Optional, non utilisé |
|
||
|
|
| **gRPC** | tonic | 0.11 | ⚠️ | Présent mais non utilisé |
|
||
|
|
| **Event Bus** | lapin | 2.3 | ✅ | RabbitMQ |
|
||
|
|
| **Validation** | validator | 0.20 | ⚠️ | Présent mais non utilisé |
|
||
|
|
| **Security** | bcrypt | 0.17 | ⚠️ | Présent mais non utilisé |
|
||
|
|
|
||
|
|
**Problèmes**:
|
||
|
|
- Dépendances optionnelles non utilisées (redis, metrics, validator)
|
||
|
|
- gRPC présent mais serveur non démarré dans `main.rs`
|
||
|
|
- Versions à jour ✅
|
||
|
|
|
||
|
|
### 2.3 Configuration
|
||
|
|
|
||
|
|
**Fichiers config**:
|
||
|
|
- `env.example` : ✅ Complet (131 lignes)
|
||
|
|
- `.env` : Présent (non lu pour audit)
|
||
|
|
- `config.rs` : ✅ Chargement depuis env vars
|
||
|
|
|
||
|
|
**Paramètres hardcodés**:
|
||
|
|
- `main.rs:162` : JWT secret par défaut hardcodé (fallback)
|
||
|
|
- `typing_indicator.rs:19` : Timeout 3s hardcodé
|
||
|
|
- `rate_limiter.rs:138` : MAX_MESSAGES_PER_MINUTE = 60 hardcodé
|
||
|
|
|
||
|
|
**Secrets management**:
|
||
|
|
- ⚠️ JWT secret dans env var (pas de vault)
|
||
|
|
- ⚠️ Pas de rotation automatique secrets
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## ⚡ SECTION 3 : WEBSOCKET & PROTOCOLE
|
||
|
|
|
||
|
|
### 3.1 Serveur WebSocket
|
||
|
|
|
||
|
|
**Framework**: Axum 0.8 avec `ws` feature ✅
|
||
|
|
|
||
|
|
**Architecture**:
|
||
|
|
- Single-threaded tokio runtime (par défaut)
|
||
|
|
- Connection pooling via `WebSocketManager` (Vec<Arc<WebSocketClient>>)
|
||
|
|
- Broadcast via `broadcast.rs` (tokio::sync::broadcast channels)
|
||
|
|
- State: `Arc<RwLock<>>` pour clients
|
||
|
|
|
||
|
|
**Gestion connections**:
|
||
|
|
- ✅ Handshake WebSocket via Axum `WebSocketUpgrade`
|
||
|
|
- ⚠️ **PROBLÈME P0**: Token JWT dans query params (ligne 43 `handler.rs`)
|
||
|
|
- ✅ Ping/Pong implémenté (lignes 129-138 `handler.rs`)
|
||
|
|
- ❌ Pas de timeout idle connections automatique
|
||
|
|
- ❌ Pas de heartbeat périodique (seulement ping client-initiated)
|
||
|
|
|
||
|
|
**Code problématique**:
|
||
|
|
```rust
|
||
|
|
// src/websocket/handler.rs:43
|
||
|
|
let token = match params.get("token") {
|
||
|
|
Some(t) => t,
|
||
|
|
None => {
|
||
|
|
error!("❌ Token manquant");
|
||
|
|
return (StatusCode::UNAUTHORIZED, "Missing token").into_response();
|
||
|
|
}
|
||
|
|
};
|
||
|
|
```
|
||
|
|
**Impact**: Token exposé dans logs serveur, URL, historique navigateur.
|
||
|
|
|
||
|
|
### 3.2 Protocole Messages
|
||
|
|
|
||
|
|
**Format**: JSON via `serde_json` ✅
|
||
|
|
|
||
|
|
**Schéma messages**:
|
||
|
|
```rust
|
||
|
|
// IncomingMessage (src/websocket/mod.rs:24)
|
||
|
|
pub enum IncomingMessage {
|
||
|
|
SendMessage { conversation_id, content, parent_message_id },
|
||
|
|
JoinConversation { conversation_id },
|
||
|
|
LeaveConversation { conversation_id },
|
||
|
|
MarkAsRead { conversation_id, message_id },
|
||
|
|
Ping,
|
||
|
|
}
|
||
|
|
|
||
|
|
// OutgoingMessage (src/websocket/mod.rs:47)
|
||
|
|
pub enum OutgoingMessage {
|
||
|
|
NewMessage { conversation_id, message_id, sender_id, content, created_at },
|
||
|
|
ActionConfirmed { action, success },
|
||
|
|
Error { message },
|
||
|
|
Pong,
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**Validation**:
|
||
|
|
- ✅ Deserialization avec `serde_json::from_str`
|
||
|
|
- ⚠️ Pas de validation longueur message (MAX_MESSAGE_LENGTH non appliqué)
|
||
|
|
- ❌ Pas de sanitization XSS (ammonia présent mais non utilisé)
|
||
|
|
- ❌ Pas de validation conversation_id (existence, permissions)
|
||
|
|
|
||
|
|
### 3.3 Authentification WebSocket
|
||
|
|
|
||
|
|
**Mécanisme**: JWT dans query params ⚠️
|
||
|
|
|
||
|
|
**Validation**:
|
||
|
|
- ✅ `JwtManager::validate_access_token` (ligne 52 `handler.rs`)
|
||
|
|
- ✅ Claims extraits (`AccessTokenClaims`)
|
||
|
|
- ⚠️ Pas de refresh token support dans WS
|
||
|
|
- ❌ Pas de validation permissions conversation
|
||
|
|
|
||
|
|
**Failles**:
|
||
|
|
1. Token dans URL (exposé logs, cache, referrer)
|
||
|
|
2. Pas de validation conversation access avant join
|
||
|
|
3. Pas de rate limiting par user_id (seulement par connection_id)
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 💬 SECTION 4 : FONCTIONNALITÉS CHAT
|
||
|
|
|
||
|
|
### 4.1 Messages 1-to-1
|
||
|
|
|
||
|
|
| Feature | Status | Implémentation | Notes |
|
||
|
|
|---------|--------|----------------|-------|
|
||
|
|
| Envoi message direct | ✅ | `MessageRepository::create` | UUID-based |
|
||
|
|
| Réception temps-réel | ✅ | `broadcast_to_conversation` | Via WebSocket |
|
||
|
|
| Historique messages | ✅ | `get_conversation_messages` | Pagination LIMIT |
|
||
|
|
| Chiffrement E2E | ❌ | Absent | Non implémenté |
|
||
|
|
|
||
|
|
**Code**:
|
||
|
|
```rust
|
||
|
|
// src/repository/message_repository.rs:18
|
||
|
|
pub async fn create(
|
||
|
|
&self,
|
||
|
|
conversation_id: Uuid,
|
||
|
|
sender_id: Uuid,
|
||
|
|
content: &str,
|
||
|
|
) -> Result<Message>
|
||
|
|
```
|
||
|
|
|
||
|
|
**Gaps**:
|
||
|
|
- Pas de chiffrement end-to-end
|
||
|
|
- Pagination basique (LIMIT/OFFSET, pas de cursor)
|
||
|
|
- Pas de recherche full-text
|
||
|
|
|
||
|
|
### 4.2 Group Chat & Channels
|
||
|
|
|
||
|
|
| Feature | Status | Implémentation | Notes |
|
||
|
|
|---------|--------|----------------|-------|
|
||
|
|
| Création groupe | ✅ | `RoomService::create_room` | UUID-based |
|
||
|
|
| Ajout membres | ✅ | `RoomService::add_user` | Via conversation_members |
|
||
|
|
| Suppression membres | ✅ | `RoomService::remove_user` | |
|
||
|
|
| Permissions | ⚠️ | Partiel | Rôles présents, vérification incomplète |
|
||
|
|
| Broadcast messages | ✅ | `broadcast_to_conversation` | |
|
||
|
|
| Historique groupe | ✅ | `get_conversation_messages` | |
|
||
|
|
|
||
|
|
**Permissions**:
|
||
|
|
- Rôles définis: `admin`, `moderator`, `member` (migration 001)
|
||
|
|
- ⚠️ Vérification permissions non appliquée dans handler WS
|
||
|
|
|
||
|
|
### 4.3 Features Temps-Réel
|
||
|
|
|
||
|
|
#### Typing Indicators
|
||
|
|
|
||
|
|
**Status**: ✅ Fonctionnel (75%)
|
||
|
|
|
||
|
|
**Implémentation**:
|
||
|
|
- `TypingIndicatorManager` (src/typing_indicator.rs)
|
||
|
|
- Timeout: 3s (hardcodé ligne 19)
|
||
|
|
- Cleanup périodique présent
|
||
|
|
- ⚠️ Pas de broadcast WebSocket intégré (manager isolé)
|
||
|
|
|
||
|
|
**Code**:
|
||
|
|
```rust
|
||
|
|
// src/typing_indicator.rs:25
|
||
|
|
pub async fn set_typing(&self, conversation_id: &str, user_id: &str)
|
||
|
|
```
|
||
|
|
|
||
|
|
**Gaps**:
|
||
|
|
- Pas de message WebSocket pour typing events
|
||
|
|
- Timeout non configurable
|
||
|
|
- Pas de debouncing côté client
|
||
|
|
|
||
|
|
#### Read Receipts
|
||
|
|
|
||
|
|
**Status**: 🟡 Partiel (40%)
|
||
|
|
|
||
|
|
**Implémentation**:
|
||
|
|
- Structure DB présente (`read_receipts` table)
|
||
|
|
- `ReadReceiptManager` présent (src/read_receipts.rs)
|
||
|
|
- ❌ **PROBLÈME P0**: TODO ligne 258 `websocket/handler.rs`
|
||
|
|
- ⚠️ Types incohérents: `i64` au lieu de `UUID`
|
||
|
|
|
||
|
|
**Code problématique**:
|
||
|
|
```rust
|
||
|
|
// src/websocket/handler.rs:249
|
||
|
|
IncomingMessage::MarkAsRead { conversation_id, message_id } => {
|
||
|
|
debug!("👁️ Client {} marque le message {} comme lu", client.id, message_id);
|
||
|
|
// TODO: Implémenter la logique de marquage comme lu
|
||
|
|
let outgoing = OutgoingMessage::ActionConfirmed {
|
||
|
|
action: "marked_as_read".to_string(),
|
||
|
|
success: true,
|
||
|
|
};
|
||
|
|
client.send_message(outgoing).await?;
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**Gaps**:
|
||
|
|
- Logique non implémentée
|
||
|
|
- Pas de synchronisation multi-device
|
||
|
|
- Pas de UI double-check marks
|
||
|
|
|
||
|
|
#### Presence
|
||
|
|
|
||
|
|
**Status**: ✅ Fonctionnel (70%)
|
||
|
|
|
||
|
|
**Implémentation**:
|
||
|
|
- `PresenceManager` (src/presence.rs)
|
||
|
|
- Statuts: Online, Away, Busy, Invisible, Offline
|
||
|
|
- Cleanup inactifs (5 min threshold)
|
||
|
|
- ⚠️ Pas de persistence DB (mémoire uniquement)
|
||
|
|
- ⚠️ Pas de broadcast WebSocket intégré
|
||
|
|
|
||
|
|
### 4.4 Modération
|
||
|
|
|
||
|
|
| Feature | Status | Implémentation | Notes |
|
||
|
|
|---------|--------|----------------|-------|
|
||
|
|
| Détection spam | ⚠️ | Basique | Similarité Levenshtein simplifiée |
|
||
|
|
| Blocage users | ✅ | `ModerationSystem::apply_sanction` | DB présente |
|
||
|
|
| Suppression messages | ✅ | `MessageRepository::delete` | Soft delete (is_deleted) |
|
||
|
|
| Modération auto | ⚠️ | Basique | Seuil 3 messages similaires |
|
||
|
|
| Logs modération | ✅ | Tracing + DB | Audit trail présent |
|
||
|
|
|
||
|
|
**Code modération auto**:
|
||
|
|
```rust
|
||
|
|
// src/moderation.rs:270
|
||
|
|
async fn detect_spam(&self, user_id: i32, content: &str) -> Result<bool> {
|
||
|
|
// Vérifier les messages répétitifs (5 dernières minutes)
|
||
|
|
let similar_count = recent_messages.iter()
|
||
|
|
.filter(|row| {
|
||
|
|
let msg_content: String = row.get("content");
|
||
|
|
self.calculate_similarity(content, &msg_content) > 0.8
|
||
|
|
})
|
||
|
|
.count();
|
||
|
|
Ok(similar_count >= 3)
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**Problèmes**:
|
||
|
|
- Détection spam trop simpliste
|
||
|
|
- Pas de ML/AI (TODO ligne 329 `security.rs`)
|
||
|
|
- Incohérence types: `i32` user_id au lieu UUID
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 🚀 SECTION 5 : PERFORMANCE
|
||
|
|
|
||
|
|
### 5.1 Benchmarks
|
||
|
|
|
||
|
|
**Status**: ❌ Absent
|
||
|
|
|
||
|
|
- Pas de dossier `benches/`
|
||
|
|
- Criterion présent dans `Cargo.toml` mais non utilisé
|
||
|
|
- Pas de métriques de performance documentées
|
||
|
|
|
||
|
|
**Recommandation**: Créer benchmarks pour:
|
||
|
|
- Messages/seconde supportés
|
||
|
|
- Latence p50/p95/p99
|
||
|
|
- Concurrent connections max
|
||
|
|
- Memory usage par connection
|
||
|
|
|
||
|
|
### 5.2 Gestion Mémoire
|
||
|
|
|
||
|
|
**Allocations**:
|
||
|
|
- ⚠️ Clones excessifs: `OutgoingMessage::clone()` ligne 144 `websocket/mod.rs`
|
||
|
|
- ✅ Arc<RwLock<>> utilisé correctement
|
||
|
|
- ⚠️ Vec<Arc<WebSocketClient>> dans WebSocketManager (croissance linéaire)
|
||
|
|
|
||
|
|
**Lifetimes**:
|
||
|
|
- ✅ Lifetimes Rust corrects (pas de dangling refs)
|
||
|
|
- ⚠️ Pas de vérification memory leaks (tests manquants)
|
||
|
|
|
||
|
|
**Buffer sizes**:
|
||
|
|
- ⚠️ Pas de limite taille message WebSocket (64KB max recommandé)
|
||
|
|
- ⚠️ Pas de backpressure handling
|
||
|
|
|
||
|
|
### 5.3 Concurrence
|
||
|
|
|
||
|
|
**Tokio**:
|
||
|
|
- ✅ Async/await partout
|
||
|
|
- ⚠️ Pas de `spawn_blocking()` pour opérations CPU-intensive
|
||
|
|
- ✅ Channels utilisés (tokio::sync::broadcast)
|
||
|
|
|
||
|
|
**Deadlocks potentiels**:
|
||
|
|
- ⚠️ Ordre locks: `WebSocketManager::clients` puis `client.conversations` (ligne 141 `websocket/mod.rs`)
|
||
|
|
- ⚠️ Pas de timeout sur locks (RwLock peut bloquer indéfiniment)
|
||
|
|
|
||
|
|
**Race conditions**:
|
||
|
|
- ⚠️ `WebSocketManager::remove_client` peut race avec `broadcast_to_conversation`
|
||
|
|
|
||
|
|
### 5.4 Scalabilité
|
||
|
|
|
||
|
|
**Horizontal scaling**:
|
||
|
|
- ❌ Pas de Redis pub/sub pour multi-instances
|
||
|
|
- ❌ Broadcast limité à instance unique
|
||
|
|
- ⚠️ RabbitMQ EventBus présent mais non utilisé pour broadcast
|
||
|
|
|
||
|
|
**Session sticky**:
|
||
|
|
- ❌ Pas de support load balancer aware
|
||
|
|
- ⚠️ Pas de session externalisée (Redis)
|
||
|
|
|
||
|
|
**Graceful shutdown**:
|
||
|
|
- ⚠️ Pas de drain connections
|
||
|
|
- ⚠️ Pas de timeout shutdown
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 🔒 SECTION 6 : SÉCURITÉ
|
||
|
|
|
||
|
|
### 6.1 Validation Input
|
||
|
|
|
||
|
|
**Handlers WebSocket**:
|
||
|
|
- ⚠️ Message length: Pas de vérification MAX_MESSAGE_LENGTH (2000 chars config)
|
||
|
|
- ❌ Sanitization XSS: `ammonia` présent mais non utilisé
|
||
|
|
- ⚠️ Injection: SQLx prepared statements ✅ (protection SQL)
|
||
|
|
- ❌ File upload: Non implémenté (feature flag présent)
|
||
|
|
|
||
|
|
**Code problématique**:
|
||
|
|
```rust
|
||
|
|
// src/websocket/handler.rs:170
|
||
|
|
IncomingMessage::SendMessage { conversation_id, content, parent_message_id } => {
|
||
|
|
// Pas de validation longueur content
|
||
|
|
// Pas de sanitization XSS
|
||
|
|
let message = state.message_repo.create(conversation_id, sender_uuid, &content).await?;
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### 6.2 Rate Limiting
|
||
|
|
|
||
|
|
**Implémentation**:
|
||
|
|
- ✅ `DosProtectionManager` présent (src/rate_limiter.rs)
|
||
|
|
- ❌ **PROBLÈME P1**: Non utilisé dans `websocket/handler.rs`
|
||
|
|
- ✅ Rate limiting par user_id et IP
|
||
|
|
- ⚠️ Configuration hardcodée (60 msg/min)
|
||
|
|
|
||
|
|
**Code manquant**:
|
||
|
|
```rust
|
||
|
|
// src/websocket/handler.rs devrait avoir:
|
||
|
|
let dos_manager = state.dos_manager.clone();
|
||
|
|
if !dos_manager.check_message_allowed(claims.user_id).await? {
|
||
|
|
return Err(ChatError::rate_limit_error("Rate limit exceeded"));
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### 6.3 Chiffrement
|
||
|
|
|
||
|
|
**TLS/WSS**:
|
||
|
|
- ⚠️ Pas de configuration TLS dans `main.rs`
|
||
|
|
- ⚠️ `ENABLE_TLS=false` dans `env.example`
|
||
|
|
- ❌ Pas de certificats validation
|
||
|
|
|
||
|
|
**End-to-end encryption**:
|
||
|
|
- ❌ Non implémenté
|
||
|
|
- ❌ Pas de Signal Protocol ou équivalent
|
||
|
|
|
||
|
|
**Storage at-rest**:
|
||
|
|
- ⚠️ Messages en clair dans PostgreSQL
|
||
|
|
- ⚠️ Pas de chiffrement colonnes sensibles
|
||
|
|
|
||
|
|
### 6.4 Audit Sécurité Rust
|
||
|
|
|
||
|
|
**Clippy**:
|
||
|
|
```bash
|
||
|
|
cargo clippy --all-targets --all-features -- -D warnings
|
||
|
|
```
|
||
|
|
**Résultats**:
|
||
|
|
- 10+ warnings (unused imports, PartialEq manquant)
|
||
|
|
- Pas de `unsafe` blocks (bon ✅)
|
||
|
|
|
||
|
|
**Cargo audit**:
|
||
|
|
- ⚠️ Non exécuté (recommandé: `cargo audit`)
|
||
|
|
|
||
|
|
**Vulnérabilités connues**:
|
||
|
|
- À vérifier avec `cargo audit`
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 💾 SECTION 7 : PERSISTENCE
|
||
|
|
|
||
|
|
### 7.1 Stockage Messages
|
||
|
|
|
||
|
|
**Backend**: PostgreSQL via SQLx ✅
|
||
|
|
|
||
|
|
**Schéma**:
|
||
|
|
- Table `messages` (UUID-based)
|
||
|
|
- Index: `conversation_id`, `sender_id`, `created_at` ✅
|
||
|
|
- Soft delete: `is_deleted` BOOLEAN ✅
|
||
|
|
- ⚠️ Pas de partitioning par date
|
||
|
|
- ⚠️ Pas de retention policy (purge old messages)
|
||
|
|
|
||
|
|
**Migrations**:
|
||
|
|
- `001_create_clean_database.sql` ✅
|
||
|
|
- `002_advanced_features.sql` ✅
|
||
|
|
- Schéma complet et cohérent
|
||
|
|
|
||
|
|
### 7.2 Queries Database
|
||
|
|
|
||
|
|
**N+1 queries**:
|
||
|
|
- ⚠️ Potentiel dans `get_conversation_messages` (pas de JOIN users)
|
||
|
|
- ✅ Pagination présente (LIMIT)
|
||
|
|
|
||
|
|
**Prepared statements**:
|
||
|
|
- ✅ SQLx `query!` macros utilisées ✅
|
||
|
|
- ✅ Protection injection SQL
|
||
|
|
|
||
|
|
**Queries lentes**:
|
||
|
|
- ⚠️ Pas d'EXPLAIN ANALYZE effectué
|
||
|
|
- ⚠️ Pas de monitoring query duration
|
||
|
|
|
||
|
|
### 7.3 Cache Strategy
|
||
|
|
|
||
|
|
**Redis**:
|
||
|
|
- ⚠️ Dépendance présente mais non utilisée
|
||
|
|
- ❌ Pas de cache recent messages
|
||
|
|
- ❌ Pas de cache user online status
|
||
|
|
- ❌ Pas de cache typing indicators
|
||
|
|
- ❌ Pas de pub/sub pour broadcast multi-instances
|
||
|
|
|
||
|
|
**Recommandation**: Implémenter cache Redis pour:
|
||
|
|
- Messages récents (TTL 1h)
|
||
|
|
- User presence (TTL 5min)
|
||
|
|
- Typing indicators (TTL 10s)
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## ✅ SECTION 8 : QUALITÉ CODE
|
||
|
|
|
||
|
|
### 8.1 Linting
|
||
|
|
|
||
|
|
**Clippy violations** (catégorisées):
|
||
|
|
|
||
|
|
| Catégorie | Count | Exemples |
|
||
|
|
|-----------|-------|----------|
|
||
|
|
| **Correctness** | 2 | `PartialEq` manquant `SslMode` |
|
||
|
|
| **Performance** | 0 | Aucun |
|
||
|
|
| **Style** | 8 | Unused imports |
|
||
|
|
| **Complexity** | 0 | Aucun |
|
||
|
|
|
||
|
|
**Fichiers problématiques**:
|
||
|
|
- `src/config.rs`: Unused imports `Pool`, `Postgres`, `error`
|
||
|
|
- `src/event_bus.rs`: Unused imports `ExchangeDeclareOptions`, `LapinError`, `ExchangeKind`
|
||
|
|
- `src/websocket/handler.rs`: Unused import `SinkExt`, `SimpleMessageStore`
|
||
|
|
- `src/config.rs:552`: `SslMode` manque `#[derive(PartialEq)]`
|
||
|
|
|
||
|
|
### 8.2 Formatting
|
||
|
|
|
||
|
|
**Status**: ⚠️ Non vérifié
|
||
|
|
|
||
|
|
**Recommandation**: Exécuter `cargo fmt --check`
|
||
|
|
|
||
|
|
### 8.3 Tests
|
||
|
|
|
||
|
|
**Coverage estimé**: 25%
|
||
|
|
|
||
|
|
**Tests présents**:
|
||
|
|
- `tests/integration_test.rs`: 6 tests, tous `#[ignore]`
|
||
|
|
- Tests unitaires: Rares (typing_indicator, jwt_manager, rate_limiter)
|
||
|
|
|
||
|
|
**Modules sans tests**:
|
||
|
|
- `websocket/handler.rs` ❌
|
||
|
|
- `repository/message_repository.rs` ❌
|
||
|
|
- `services/room_service.rs` ❌
|
||
|
|
- `moderation.rs` ❌
|
||
|
|
- `presence.rs` ❌
|
||
|
|
|
||
|
|
**Objectif ORIGIN_**: 75%+ coverage (non atteint)
|
||
|
|
|
||
|
|
### 8.4 Documentation
|
||
|
|
|
||
|
|
**Doc comments**:
|
||
|
|
- ✅ Présents sur modules principaux (`//!`)
|
||
|
|
- ⚠️ Manquants sur fonctions publiques
|
||
|
|
- ❌ Pas d'exemples dans doc (`#[doc = include_str!("...")]`)
|
||
|
|
|
||
|
|
**Cargo doc**:
|
||
|
|
- ⚠️ Non généré (recommandé: `cargo doc --no-deps --open`)
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 📈 SECTION 9 : OBSERVABILITÉ
|
||
|
|
|
||
|
|
### 9.1 Logging
|
||
|
|
|
||
|
|
**Framework**: `tracing` ✅
|
||
|
|
|
||
|
|
**Structured logging**:
|
||
|
|
- ✅ Utilisé partout
|
||
|
|
- ✅ Niveaux: trace/debug/info/warn/error
|
||
|
|
- ⚠️ Pas de logs sensibles détectés (tokens, passwords)
|
||
|
|
|
||
|
|
**Configuration**:
|
||
|
|
- JSON en production (ligne 106 `main.rs`)
|
||
|
|
- Debug en dev (ligne 109 `main.rs`)
|
||
|
|
|
||
|
|
### 9.2 Metrics
|
||
|
|
|
||
|
|
**Prometheus**:
|
||
|
|
- ✅ `PrometheusMetrics` défini (src/prometheus_metrics.rs)
|
||
|
|
- ❌ **PROBLÈME P2**: Métriques non enregistrées dans handlers
|
||
|
|
- ✅ Endpoint `/metrics` présent (ligne 203 `main.rs`)
|
||
|
|
|
||
|
|
**Métriques manquantes**:
|
||
|
|
- Messages/seconde non enregistrés
|
||
|
|
- Latence messages non mesurée
|
||
|
|
- Erreurs non comptées
|
||
|
|
|
||
|
|
### 9.3 Tracing
|
||
|
|
|
||
|
|
**OpenTelemetry**:
|
||
|
|
- ❌ Non implémenté
|
||
|
|
- ❌ Pas de spans (connection lifecycle, message flow)
|
||
|
|
- ❌ Pas de trace_id propagation
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 🔗 SECTION 10 : INTÉGRATION
|
||
|
|
|
||
|
|
### 10.1 API REST
|
||
|
|
|
||
|
|
**Endpoints exposés**:
|
||
|
|
- `GET /health` ✅
|
||
|
|
- `GET /healthz` ✅ (liveness)
|
||
|
|
- `GET /readyz` ✅ (readiness)
|
||
|
|
- `GET /metrics` ✅ (Prometheus)
|
||
|
|
- `GET /api/messages/{conversation_id}` ✅
|
||
|
|
- `POST /api/messages` ✅
|
||
|
|
- `GET /api/messages/stats` ⚠️ (hardcodé)
|
||
|
|
|
||
|
|
**Cohérence avec backend Go**:
|
||
|
|
- ⚠️ Non vérifiée (backend Go non analysé)
|
||
|
|
|
||
|
|
### 10.2 Communication Inter-Services
|
||
|
|
|
||
|
|
**RabbitMQ EventBus**:
|
||
|
|
- ✅ `RabbitMQEventBus` présent (src/event_bus.rs)
|
||
|
|
- ⚠️ Initialisé dans `main.rs` mais non utilisé pour events
|
||
|
|
- ✅ Retry logic présent
|
||
|
|
- ⚠️ Pas de circuit breaker
|
||
|
|
|
||
|
|
**gRPC**:
|
||
|
|
- ⚠️ `grpc_server.rs` présent mais non démarré
|
||
|
|
- ⚠️ `grpc_client.rs` présent mais non utilisé
|
||
|
|
|
||
|
|
### 10.3 Authentification Partagée
|
||
|
|
|
||
|
|
**JWT shared secret**:
|
||
|
|
- ✅ `JWT_SECRET` env var
|
||
|
|
- ⚠️ Fallback hardcodé (ligne 162 `main.rs`)
|
||
|
|
- ✅ Même algorithme (HS256 par défaut)
|
||
|
|
- ⚠️ Audience/Issuer configurables
|
||
|
|
|
||
|
|
**Incohérences**:
|
||
|
|
- ⚠️ Non vérifiées (backend Go non analysé)
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 📐 SECTION 11 : GAP ANALYSIS ORIGIN_
|
||
|
|
|
||
|
|
### 11.1 Matrice Complétude Features
|
||
|
|
|
||
|
|
| Feature ORIGIN_ | Status | Complétude | Gaps |
|
||
|
|
|-----------------|--------|------------|------|
|
||
|
|
| WebSocket temps-réel | ✅ | 80% | Heartbeat timeout, idle connections |
|
||
|
|
| Messages 1-to-1 | ✅ | 80% | E2E encryption manquant |
|
||
|
|
| Group chat | ✅ | 70% | Permissions incomplètes |
|
||
|
|
| Channels | ⚠️ | 50% | Structure présente, features manquantes |
|
||
|
|
| Typing indicators | ✅ | 75% | Broadcast WS manquant |
|
||
|
|
| Read receipts | 🟡 | 40% | **TODO ligne 258** |
|
||
|
|
| Presence | ✅ | 70% | Persistence DB manquante |
|
||
|
|
| Modération auto | ⚠️ | 60% | ML/AI manquant |
|
||
|
|
| Rate limiting | ⚠️ | 50% | Non appliqué dans handler |
|
||
|
|
| Redis pub/sub | ❌ | 0% | Non implémenté |
|
||
|
|
| Metrics Prometheus | ⚠️ | 30% | Définies mais non utilisées |
|
||
|
|
| Tests coverage 75%+ | ❌ | 25% | Tests ignorés, unitaires rares |
|
||
|
|
|
||
|
|
### 11.2 Écarts Architecture
|
||
|
|
|
||
|
|
**ORIGIN_ attendu**:
|
||
|
|
- Event-driven via RabbitMQ ✅ (présent mais non utilisé)
|
||
|
|
- Redis pub/sub pour scaling ❌ (non implémenté)
|
||
|
|
- gRPC inter-services ⚠️ (présent mais non démarré)
|
||
|
|
|
||
|
|
**Architecture réelle**:
|
||
|
|
- WebSocket direct (Axum)
|
||
|
|
- Broadcast in-memory (tokio::sync::broadcast)
|
||
|
|
- Pas de scaling horizontal
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 🎯 SECTION 12 : PLAN D'ACTION PRIORISÉ
|
||
|
|
|
||
|
|
### RUST-CHAT-001 | Auth WebSocket Token dans Query Params
|
||
|
|
```
|
||
|
|
├─ Gravité : 🔴 P0 - BLOQUANT
|
||
|
|
├─ Description : Token JWT exposé dans URL query params (ligne 43 handler.rs)
|
||
|
|
├─ Impact : Token visible dans logs, cache navigateur, referrer headers
|
||
|
|
├─ Effort : 2-3 heures
|
||
|
|
├─ Dépendances : Aucune
|
||
|
|
└─ Action : Déplacer token dans header Authorization ou cookie HTTP-only
|
||
|
|
```
|
||
|
|
|
||
|
|
**Solution**:
|
||
|
|
```rust
|
||
|
|
// Avant (handler.rs:43)
|
||
|
|
let token = params.get("token")?;
|
||
|
|
|
||
|
|
// Après
|
||
|
|
let auth_header = request.headers().get("authorization")?;
|
||
|
|
let token = extract_token_from_header(auth_header.to_str()?)?;
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### RUST-CHAT-002 | Read Receipts Non Implémentés
|
||
|
|
```
|
||
|
|
├─ Gravité : 🔴 P0 - BLOQUANT
|
||
|
|
├─ Description : TODO ligne 258 websocket/handler.rs - logique marquage comme lu absente
|
||
|
|
├─ Impact : Feature read receipts non fonctionnelle
|
||
|
|
├─ Effort : 1 jour
|
||
|
|
├─ Dépendances : ReadReceiptManager présent mais types i64 vs UUID
|
||
|
|
└─ Action : Implémenter logique + corriger types UUID
|
||
|
|
```
|
||
|
|
|
||
|
|
**Solution**:
|
||
|
|
```rust
|
||
|
|
// handler.rs:249
|
||
|
|
IncomingMessage::MarkAsRead { conversation_id, message_id } => {
|
||
|
|
let read_receipt_manager = state.read_receipt_manager.clone();
|
||
|
|
read_receipt_manager.mark_as_read(
|
||
|
|
message_id,
|
||
|
|
claims.user_id.parse()?,
|
||
|
|
conversation_id
|
||
|
|
).await?;
|
||
|
|
// Broadcast read receipt to conversation
|
||
|
|
state.ws_manager.broadcast_read_receipt(conversation_id, message_id, claims.user_id).await?;
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### RUST-CHAT-003 | Tests Coverage < 30%
|
||
|
|
```
|
||
|
|
├─ Gravité : 🔴 P0 - BLOQUANT
|
||
|
|
├─ Description : Tests d'intégration ignorés, unitaires rares
|
||
|
|
├─ Impact : Pas de confiance dans refactoring, bugs non détectés
|
||
|
|
├─ Effort : 5-7 jours
|
||
|
|
├─ Dépendances : Setup DB de test
|
||
|
|
└─ Action : Activer tests integration, ajouter tests unitaires critiques
|
||
|
|
```
|
||
|
|
|
||
|
|
**Actions**:
|
||
|
|
1. Retirer `#[ignore]` des tests integration
|
||
|
|
2. Ajouter tests unitaires: `websocket/handler.rs`, `message_repository.rs`
|
||
|
|
3. Setup CI/CD avec coverage reporting (tarpaulin)
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### RUST-CHAT-004 | Clippy Warnings Non Résolus
|
||
|
|
```
|
||
|
|
├─ Gravité : 🟠 P1 - CRITIQUE
|
||
|
|
├─ Description : 10+ warnings compilation (unused imports, PartialEq manquant)
|
||
|
|
├─ Impact : Code quality dégradée, warnings masquent vrais problèmes
|
||
|
|
├─ Effort : 1-2 heures
|
||
|
|
├─ Dépendances : Aucune
|
||
|
|
└─ Action : Corriger tous les warnings clippy
|
||
|
|
```
|
||
|
|
|
||
|
|
**Actions**:
|
||
|
|
1. Supprimer unused imports
|
||
|
|
2. Ajouter `#[derive(PartialEq)]` à `SslMode`
|
||
|
|
3. Ajouter `#[allow(dead_code)]` si nécessaire (temporaire)
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### RUST-CHAT-005 | Rate Limiting Non Appliqué
|
||
|
|
```
|
||
|
|
├─ Gravité : 🟠 P1 - CRITIQUE
|
||
|
|
├─ Description : DosProtectionManager présent mais non utilisé dans handler
|
||
|
|
├─ Impact : Pas de protection DoS, spam possible
|
||
|
|
├─ Effort : 2-3 heures
|
||
|
|
├─ Dépendances : DosProtectionManager présent
|
||
|
|
└─ Action : Intégrer rate limiting dans websocket handler
|
||
|
|
```
|
||
|
|
|
||
|
|
**Solution**:
|
||
|
|
```rust
|
||
|
|
// Ajouter dans WebSocketState
|
||
|
|
pub dos_manager: Arc<DosProtectionManager>,
|
||
|
|
|
||
|
|
// Dans handle_incoming_message
|
||
|
|
if !state.dos_manager.check_message_allowed(claims.user_id.parse()?).await? {
|
||
|
|
return Err(ChatError::rate_limit_error("Rate limit exceeded"));
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### RUST-CHAT-006 | Validation Input Messages
|
||
|
|
```
|
||
|
|
├─ Gravité : 🟠 P1 - CRITIQUE
|
||
|
|
├─ Description : Pas de sanitization XSS, longueur max non vérifiée
|
||
|
|
├─ Impact : XSS possible, DoS via messages géants
|
||
|
|
├─ Effort : 1 jour
|
||
|
|
├─ Dépendances : ammonia présent, validator présent
|
||
|
|
└─ Action : Ajouter validation longueur + sanitization XSS
|
||
|
|
```
|
||
|
|
|
||
|
|
**Solution**:
|
||
|
|
```rust
|
||
|
|
// handler.rs:170
|
||
|
|
const MAX_MESSAGE_LENGTH: usize = 2000;
|
||
|
|
if content.len() > MAX_MESSAGE_LENGTH {
|
||
|
|
return Err(ChatError::validation_error("Message too long"));
|
||
|
|
}
|
||
|
|
let sanitized = ammonia::clean(content);
|
||
|
|
let message = state.message_repo.create(conversation_id, sender_uuid, &sanitized).await?;
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### RUST-CHAT-007 | Heartbeat/Ping-Pong Incomplet
|
||
|
|
```
|
||
|
|
├─ Gravité : 🟠 P1 - CRITIQUE
|
||
|
|
├─ Description : Ping géré, mais pas de timeout idle connections
|
||
|
|
├─ Impact : Connections zombies consomment ressources
|
||
|
|
├─ Effort : 1 jour
|
||
|
|
├─ Dépendances : Aucune
|
||
|
|
└─ Action : Implémenter timeout idle + heartbeat périodique
|
||
|
|
```
|
||
|
|
|
||
|
|
**Solution**:
|
||
|
|
```rust
|
||
|
|
// Ajouter dans handle_socket
|
||
|
|
let mut last_ping = Instant::now();
|
||
|
|
const IDLE_TIMEOUT: Duration = Duration::from_secs(300); // 5 min
|
||
|
|
|
||
|
|
tokio::select! {
|
||
|
|
msg = receiver.next() => { /* ... */ },
|
||
|
|
_ = tokio::time::sleep(Duration::from_secs(30)) => {
|
||
|
|
// Heartbeat périodique
|
||
|
|
if last_ping.elapsed() > IDLE_TIMEOUT {
|
||
|
|
break; // Timeout
|
||
|
|
}
|
||
|
|
client.send_message(OutgoingMessage::Ping).await?;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### RUST-CHAT-008 | Modération Auto Basique
|
||
|
|
```
|
||
|
|
├─ Gravité : 🟡 P2 - MAJEUR
|
||
|
|
├─ Description : Détection spam simpliste (similarité Levenshtein)
|
||
|
|
├─ Impact : Faux positifs/négatifs, spam non détecté
|
||
|
|
├─ Effort : 3-5 jours
|
||
|
|
├─ Dépendances : ML model (optionnel)
|
||
|
|
└─ Action : Améliorer détection spam (rate limiting + patterns)
|
||
|
|
```
|
||
|
|
|
||
|
|
**Solution**:
|
||
|
|
- Combiner rate limiting + similarité
|
||
|
|
- Ajouter détection patterns (URLs répétées, mentions massives)
|
||
|
|
- Intégrer ML model (optionnel, TODO ligne 329)
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### RUST-CHAT-009 | Pas de Redis Pub/Sub
|
||
|
|
```
|
||
|
|
├─ Gravité : 🟡 P2 - MAJEUR
|
||
|
|
├─ Description : Broadcast limité à instance unique
|
||
|
|
├─ Impact : Pas de scaling horizontal
|
||
|
|
├─ Effort : 3-5 jours
|
||
|
|
├─ Dépendances : Redis présent mais non utilisé
|
||
|
|
└─ Action : Implémenter Redis pub/sub pour broadcast multi-instances
|
||
|
|
```
|
||
|
|
|
||
|
|
**Solution**:
|
||
|
|
```rust
|
||
|
|
// Ajouter Redis pub/sub dans broadcast.rs
|
||
|
|
pub async fn broadcast_to_conversation_redis(
|
||
|
|
&self,
|
||
|
|
conversation_id: Uuid,
|
||
|
|
message: OutgoingMessage,
|
||
|
|
) -> Result<()> {
|
||
|
|
// Publier sur Redis channel
|
||
|
|
let channel = format!("conversation:{}", conversation_id);
|
||
|
|
let json = serde_json::to_string(&message)?;
|
||
|
|
redis_client.publish(channel, json).await?;
|
||
|
|
Ok(())
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### RUST-CHAT-010 | Metrics Prometheus Non Utilisées
|
||
|
|
```
|
||
|
|
├─ Gravité : 🟡 P2 - MAJEUR
|
||
|
|
├─ Description : Métriques définies mais non enregistrées dans handlers
|
||
|
|
├─ Impact : Pas de monitoring production
|
||
|
|
├─ Effort : 1 jour
|
||
|
|
├─ Dépendances : PrometheusMetrics présent
|
||
|
|
└─ Action : Enregistrer métriques dans tous les handlers
|
||
|
|
```
|
||
|
|
|
||
|
|
**Solution**:
|
||
|
|
```rust
|
||
|
|
// Ajouter dans WebSocketState
|
||
|
|
pub metrics: Arc<PrometheusMetrics>,
|
||
|
|
|
||
|
|
// Dans handle_incoming_message
|
||
|
|
state.metrics.record_message_received(content.len() as u64);
|
||
|
|
let start = Instant::now();
|
||
|
|
// ... traitement ...
|
||
|
|
state.metrics.record_message_processing_duration(start.elapsed());
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### RUST-CHAT-011 | Incohérence Types i32 vs UUID
|
||
|
|
```
|
||
|
|
├─ Gravité : 🟡 P2 - MAJEUR
|
||
|
|
├─ Description : read_receipts.rs et moderation.rs utilisent i32 au lieu UUID
|
||
|
|
├─ Impact : Incompatibilité avec schéma DB (UUID)
|
||
|
|
├─ Effort : 1 jour
|
||
|
|
├─ Dépendances : Migration DB vers UUID
|
||
|
|
└─ Action : Migrer read_receipts et moderation vers UUID
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### RUST-CHAT-012 | Modules Non Utilisés (Code Mort)
|
||
|
|
```
|
||
|
|
├─ Gravité : 🟢 P3 - MINEUR
|
||
|
|
├─ Description : core/ et hub/ présents mais non utilisés
|
||
|
|
├─ Impact : Code mort, confusion
|
||
|
|
├─ Effort : 2 heures
|
||
|
|
├─ Dépendances : Aucune
|
||
|
|
└─ Action : Supprimer ou intégrer modules
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### RUST-CHAT-013 | Duplication Auth Modules
|
||
|
|
```
|
||
|
|
├─ Gravité : 🟢 P3 - MINEUR
|
||
|
|
├─ Description : auth.rs, authentication.rs, jwt_manager.rs (overlap)
|
||
|
|
├─ Impact : Confusion, maintenance difficile
|
||
|
|
├─ Effort : 1 jour
|
||
|
|
├─ Dépendances : Aucune
|
||
|
|
└─ Action : Consolider en un seul module auth
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### RUST-CHAT-014 | Pas de Graceful Shutdown
|
||
|
|
```
|
||
|
|
├─ Gravité : 🟡 P2 - MAJEUR
|
||
|
|
├─ Description : Pas de drain connections au shutdown
|
||
|
|
├─ Impact : Perte messages en cours, connections brutalement fermées
|
||
|
|
├─ Effort : 1 jour
|
||
|
|
├─ Dépendances : Tokio signal
|
||
|
|
└─ Action : Implémenter graceful shutdown avec drain
|
||
|
|
```
|
||
|
|
|
||
|
|
**Solution**:
|
||
|
|
```rust
|
||
|
|
// main.rs
|
||
|
|
tokio::select! {
|
||
|
|
result = axum::serve(listener, app) => { result? },
|
||
|
|
_ = tokio::signal::ctrl_c() => {
|
||
|
|
info!("🛑 Shutdown signal reçu, drain connections...");
|
||
|
|
// Drain WebSocket connections
|
||
|
|
ws_manager.drain_all().await;
|
||
|
|
info!("✅ Shutdown gracieux terminé");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 📝 CONCLUSION
|
||
|
|
|
||
|
|
Le Chat Server Rust présente une **base solide** avec:
|
||
|
|
- ✅ Architecture WebSocket moderne (Axum)
|
||
|
|
- ✅ Persistence PostgreSQL robuste (SQLx)
|
||
|
|
- ✅ Structure modulaire claire
|
||
|
|
- ✅ Features temps-réel partiellement implémentées
|
||
|
|
|
||
|
|
**Problèmes critiques** à résoudre en priorité:
|
||
|
|
1. 🔴 Auth WebSocket (token dans URL)
|
||
|
|
2. 🔴 Read receipts non implémentés
|
||
|
|
3. 🔴 Tests coverage insuffisant
|
||
|
|
4. 🟠 Rate limiting non appliqué
|
||
|
|
5. 🟠 Validation input manquante
|
||
|
|
|
||
|
|
**Effort total estimé**: 18-27 jours pour atteindre production-ready avec toutes les features ORIGIN_.
|
||
|
|
|
||
|
|
**Recommandation**: Commencer par P0 (3-5 jours), puis P1 (5-7 jours) avant déploiement production.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
**Fin du rapport d'audit**
|
||
|
|
|