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.).
10 KiB
Système de Read Receipts - Veza Chat Server
Vue d'ensemble
Le système de read receipts permet de tracker quels messages ont été lus par quels utilisateurs dans une conversation. Cette fonctionnalité est essentielle pour fournir un feedback visuel aux utilisateurs (indicateurs "lu" / "non lu") et améliorer l'expérience utilisateur.
Statut : ✅ Opérationnel (implémenté et testé)
Date d'implémentation : 2025-12-05
Architecture
Composants principaux
- Table de base de données :
read_receipts - Manager :
ReadReceiptManager(src/read_receipts.rs) - Handler WebSocket : Intégration dans
src/websocket/handler.rs - Messages WebSocket :
MarkAsRead(inbound) etMessageRead(outbound)
Schéma de base de données
La table read_receipts est créée par la migration 003_read_receipts.sql :
CREATE TABLE read_receipts (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
message_id UUID NOT NULL REFERENCES messages(id) ON DELETE CASCADE,
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
conversation_id UUID NOT NULL REFERENCES conversations(id) ON DELETE CASCADE,
read_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
UNIQUE(message_id, user_id)
);
Index :
idx_read_receipts_message_id: Recherche par messageidx_read_receipts_user_id: Recherche par utilisateuridx_read_receipts_conversation_id: Recherche par conversationidx_read_receipts_conversation_user: Requêtes fréquentes (dernière lecture)
Contrat WebSocket
Message Inbound : MarkAsRead
Envoyé par le client pour marquer un message comme lu.
{
"type": "MarkAsRead",
"conversation_id": "uuid-de-la-conversation",
"message_id": "uuid-du-message"
}
Validation côté serveur :
- Le message existe et appartient à la conversation indiquée
- L'utilisateur est membre de la conversation
- Le JWT est valide (vérifié automatiquement par le handler)
Réponses possibles :
- ✅
ActionConfirmed: Le message a été marqué comme lu - ❌
Error: Erreur de validation ou de permission
Message Outbound : MessageRead
Envoyé à tous les participants de la conversation lorsqu'un message est marqué comme lu.
{
"type": "MessageRead",
"message_id": "uuid-du-message",
"user_id": "uuid-de-l-utilisateur-qui-a-lu",
"conversation_id": "uuid-de-la-conversation",
"read_at": "2025-12-05T10:30:00Z"
}
Broadcast : Ce message est automatiquement diffusé à tous les clients connectés à la conversation (sauf l'utilisateur qui a initié l'action, qui reçoit ActionConfirmed).
Comportement serveur
Flux de traitement
- Réception : Le client envoie
MarkAsReadvia WebSocket - Validation :
- Vérification de l'existence du message
- Vérification de l'appartenance du message à la conversation
- Vérification de l'appartenance de l'utilisateur à la conversation
- Persistance :
- Création d'un nouveau read receipt si inexistant
- Mise à jour du timestamp
read_atsi le read receipt existe déjà
- Notification :
- Broadcast de
MessageReadà tous les participants - Envoi de
ActionConfirmedau client initiateur
- Broadcast de
Gestion des erreurs
| Erreur | Code | Comportement |
|---|---|---|
| Message inexistant | not_found |
Retourne une erreur au client |
| Message n'appartient pas à la conversation | validation_error |
Retourne une erreur au client |
| Utilisateur non membre | unauthorized |
Retourne une erreur au client |
| Erreur DB | internal_error |
Log l'erreur, retourne une erreur générique au client |
Logs structurés
Les événements suivants sont loggés avec tracing :
- ✅ Message marqué comme lu (info)
- ✅ Read receipt créé (info)
- ✅ Read receipt mis à jour (debug)
- ❌ Erreurs de validation/permission (error)
API du ReadReceiptManager
Méthodes principales
mark_as_read(user_id, message_id, conversation_id)
Marque un message comme lu par un utilisateur.
Retourne : ReadReceipt (créé ou mis à jour)
get_receipt(message_id, user_id)
Récupère le read receipt pour un message et un utilisateur spécifiques.
Retourne : Option<ReadReceipt>
get_receipts_for_message(message_id)
Récupère tous les read receipts pour un message (tous les utilisateurs qui l'ont lu).
Retourne : Vec<ReadReceipt>
get_message_status(message_id, user_id)
Récupère le statut de lecture d'un message pour un utilisateur.
Retourne : MessageReadStatus (Sent, Delivered, ou Read)
is_user_in_conversation(user_id, conversation_id)
Vérifie si un utilisateur est membre d'une conversation.
Retourne : bool
get_last_read_message(conversation_id, user_id)
Récupère l'ID du dernier message lu par un utilisateur dans une conversation.
Retourne : Option<Uuid>
get_unread_count(conversation_id, user_id, last_read_message_id)
Calcule le nombre de messages non lus pour un utilisateur dans une conversation.
Retourne : i64
Prérequis
Base de données
- Migration : Exécuter
migrations/003_read_receipts.sql - Extensions PostgreSQL :
uuid-ossp(déjà requis par les migrations précédentes)
Configuration
Aucune configuration spécifique requise. Le système utilise le pool de connexions PostgreSQL déjà configuré.
Tests
Tests unitaires
Les tests unitaires sont dans src/read_receipts.rs (module tests).
Exécution :
cd veza-chat-server
cargo test --lib read_receipts -- --ignored
Tests disponibles :
test_mark_as_read_creates_receipt: Vérifie la création d'un read receipttest_mark_as_read_updates_existing: Vérifie la mise à jour d'un read receipt existanttest_get_receipt: Vérifie la récupération d'un read receipttest_get_message_status: Vérifie le statut de lecturetest_get_receipts_for_message: Vérifie la récupération de tous les read receipts d'un message
Tests d'intégration
Le test d'intégration est dans tests/integration_test.rs : test_read_receipts_websocket.
Exécution :
cd veza-chat-server
# 1. Démarrer le serveur : cargo run
# 2. Dans un autre terminal :
cargo test --test integration_test test_read_receipts_websocket -- --ignored
Prérequis :
- Serveur chat-server en cours d'exécution
- Base de données avec migrations appliquées
- Variable d'environnement
DATABASE_URLconfigurée
Exemples d'utilisation
Côté client (WebSocket)
// Marquer un message comme lu
const markAsRead = {
type: "MarkAsRead",
conversation_id: "conversation-uuid",
message_id: "message-uuid"
};
websocket.send(JSON.stringify(markAsRead));
// Écouter les notifications de lecture
websocket.onmessage = (event) => {
const message = JSON.parse(event.data);
if (message.type === "MessageRead") {
console.log(`Message ${message.message_id} lu par ${message.user_id}`);
// Mettre à jour l'UI pour afficher l'indicateur "lu"
}
if (message.type === "ActionConfirmed" && message.action === "marked_as_read") {
console.log("Message marqué comme lu avec succès");
}
};
Côté serveur (Rust)
use chat_server::read_receipts::ReadReceiptManager;
// Dans votre handler
let manager = ReadReceiptManager::new(pool);
// Marquer un message comme lu
let receipt = manager
.mark_as_read(user_id, message_id, conversation_id)
.await?;
// Vérifier le statut
let status = manager
.get_message_status(message_id, user_id)
.await?;
match status {
MessageReadStatus::Read => println!("Message lu"),
MessageReadStatus::Sent => println!("Message envoyé"),
MessageReadStatus::Delivered => println!("Message livré"),
}
Limitations et améliorations futures
Limitations actuelles
-
Statut "Delivered" : Le système ne track pas encore le statut "livré" (message reçu mais pas encore lu). Actuellement, un message est soit
SentsoitRead. -
Batch operations : La méthode
mark_multiple_as_readexiste mais n'est pas encore exposée via WebSocket.
Améliorations possibles
-
Support "Delivered" : Implémenter un système de tracking "delivered" (message reçu par le client mais pas encore ouvert).
-
API REST : Exposer une API REST pour :
- Récupérer les read receipts d'un message
- Récupérer le nombre de messages non lus
- Marquer plusieurs messages comme lus en une requête
-
Optimisations :
- Cache des read receipts fréquemment consultés
- Batch processing pour les marquages multiples
-
Métriques : Ajouter des métriques Prometheus pour :
- Nombre de read receipts créés par seconde
- Temps moyen entre l'envoi et la lecture d'un message
- Taux de lecture par conversation
Migration depuis l'ancien système
Si vous migrez depuis un système utilisant i64 pour les IDs :
- Exécuter la migration :
migrations/003_read_receipts.sql - Migrer les données existantes (si applicable) :
-- Exemple de migration de données (à adapter selon votre schéma) INSERT INTO read_receipts (message_id, user_id, conversation_id, read_at) SELECT message_id::uuid, user_id::uuid, conversation_id::uuid, read_at FROM old_read_receipts; - Mettre à jour le code client : S'assurer que les clients utilisent des UUID au lieu d'entiers
Support et maintenance
Logs à surveiller
- Erreurs de validation/permission lors du marquage comme lu
- Erreurs de base de données lors de la création/mise à jour de read receipts
- Temps de réponse élevés pour les requêtes de read receipts
Monitoring recommandé
- Nombre de read receipts créés par minute
- Taux d'erreur lors du marquage comme lu
- Temps de réponse des requêtes
get_receipts_for_message
Références
- Migration :
migrations/003_read_receipts.sql - Code source :
src/read_receipts.rs - Handler WebSocket :
src/websocket/handler.rs - Types WebSocket :
src/websocket/mod.rs
Dernière mise à jour : 2025-12-05