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.).
444 lines
12 KiB
Markdown
444 lines
12 KiB
Markdown
# Documentation : Édition et Suppression de Messages
|
|
|
|
**Date de création** : 2025-12-05
|
|
**Version** : 1.0.0
|
|
**Statut** : ✅ Implémenté
|
|
|
|
## Vue d'ensemble
|
|
|
|
Ce document décrit l'implémentation complète de l'édition et de la suppression (soft delete) de messages dans le serveur de chat Veza. Ces fonctionnalités sont essentielles pour un système de chat moderne et respectent les meilleures pratiques de sécurité, permissions et cohérence temps réel.
|
|
|
|
## Table des matières
|
|
|
|
1. [Architecture](#architecture)
|
|
2. [Événements WebSocket](#événements-websocket)
|
|
3. [Permissions](#permissions)
|
|
4. [Base de données](#base-de-données)
|
|
5. [Services](#services)
|
|
6. [Exemples d'utilisation](#exemples-dutilisation)
|
|
7. [Conséquences UX](#conséquences-ux)
|
|
8. [Impact sur la recherche et pagination](#impact-sur-la-recherche-et-pagination)
|
|
|
|
---
|
|
|
|
## Architecture
|
|
|
|
### Composants principaux
|
|
|
|
1. **Migration SQL** (`migrations/005_message_edit_delete.sql`)
|
|
- Ajoute `deleted_at` pour la traçabilité
|
|
- Index pour les requêtes de nettoyage
|
|
|
|
2. **PermissionService** (`src/security/permission.rs`)
|
|
- `can_edit_message()` : Vérifie les permissions d'édition
|
|
- `can_delete_message()` : Vérifie les permissions de suppression
|
|
|
|
3. **MessageEditService** (`src/services/message_edit_service.rs`)
|
|
- `edit_message()` : Édite un message avec validation
|
|
- `delete_message()` : Supprime un message (soft delete)
|
|
|
|
4. **MessageRepository** (`src/repository/message_repository.rs`)
|
|
- `update()` : Met à jour le contenu d'un message
|
|
- `delete()` : Marque un message comme supprimé
|
|
- `get_by_id_including_deleted()` : Récupère même les messages supprimés
|
|
|
|
5. **WebSocket Handlers** (`src/websocket/handler.rs`)
|
|
- Gère les événements `EditMessage` et `DeleteMessage`
|
|
- Broadcast les événements `MessageEdited` et `MessageDeleted`
|
|
|
|
---
|
|
|
|
## Événements WebSocket
|
|
|
|
### Inbound Events (Client → Serveur)
|
|
|
|
#### EditMessage
|
|
|
|
Édite un message existant.
|
|
|
|
```json
|
|
{
|
|
"type": "EditMessage",
|
|
"message_id": "550e8400-e29b-41d4-a716-446655440000",
|
|
"conversation_id": "660e8400-e29b-41d4-a716-446655440000",
|
|
"new_content": "Nouveau contenu du message"
|
|
}
|
|
```
|
|
|
|
**Règles de validation** :
|
|
- `new_content` doit être différent du contenu précédent
|
|
- `new_content` ne peut pas être vide (après trim)
|
|
- `new_content` ne peut pas dépasser 4000 caractères
|
|
- Le message ne doit pas être supprimé
|
|
- L'utilisateur doit avoir les permissions d'édition
|
|
|
|
#### DeleteMessage
|
|
|
|
Supprime un message (soft delete).
|
|
|
|
```json
|
|
{
|
|
"type": "DeleteMessage",
|
|
"message_id": "550e8400-e29b-41d4-a716-446655440000",
|
|
"conversation_id": "660e8400-e29b-41d4-a716-446655440000"
|
|
}
|
|
```
|
|
|
|
**Règles de validation** :
|
|
- L'utilisateur doit avoir les permissions de suppression
|
|
- L'opération est idempotente (supprimer un message déjà supprimé retourne OK)
|
|
|
|
### Outbound Events (Serveur → Client)
|
|
|
|
#### MessageEdited
|
|
|
|
Notifie tous les clients d'une conversation qu'un message a été édité.
|
|
|
|
```json
|
|
{
|
|
"type": "MessageEdited",
|
|
"message_id": "550e8400-e29b-41d4-a716-446655440000",
|
|
"conversation_id": "660e8400-e29b-41d4-a716-446655440000",
|
|
"editor_id": "770e8400-e29b-41d4-a716-446655440000",
|
|
"edited_at": "2025-12-05T10:30:00Z",
|
|
"new_content": "Nouveau contenu du message"
|
|
}
|
|
```
|
|
|
|
#### MessageDeleted
|
|
|
|
Notifie tous les clients d'une conversation qu'un message a été supprimé.
|
|
|
|
```json
|
|
{
|
|
"type": "MessageDeleted",
|
|
"message_id": "550e8400-e29b-41d4-a716-446655440000",
|
|
"conversation_id": "660e8400-e29b-41d4-a716-446655440000",
|
|
"deleter_id": "770e8400-e29b-41d4-a716-446655440000",
|
|
"deleted_at": "2025-12-05T10:30:00Z"
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Permissions
|
|
|
|
### Règles d'édition
|
|
|
|
Un utilisateur peut éditer un message si :
|
|
|
|
1. **Il est l'auteur du message** : L'auteur peut toujours éditer son propre message
|
|
2. **Il est admin/modérateur de la conversation** : Les admins et modérateurs peuvent éditer n'importe quel message dans leur conversation
|
|
3. **Le message n'est pas supprimé** : Un message supprimé ne peut jamais être édité
|
|
|
|
### Règles de suppression
|
|
|
|
Un utilisateur peut supprimer un message si :
|
|
|
|
1. **Il est l'auteur du message** : L'auteur peut toujours supprimer son propre message
|
|
2. **Il est admin/modérateur de la conversation** : Les admins et modérateurs peuvent supprimer n'importe quel message dans leur conversation
|
|
|
|
### Limitations de temps
|
|
|
|
Actuellement, il n'y a pas de limitation de temps pour l'édition ou la suppression. Un message peut être édité ou supprimé à tout moment tant que les permissions sont respectées.
|
|
|
|
**Note** : Pour une implémentation future, on pourrait ajouter :
|
|
- Fenêtre d'édition limitée (ex: 15 minutes après l'envoi)
|
|
- Fenêtre de suppression limitée (ex: 5 minutes après l'envoi)
|
|
|
|
---
|
|
|
|
## Base de données
|
|
|
|
### Schéma
|
|
|
|
La table `messages` contient les colonnes suivantes pour l'édition et la suppression :
|
|
|
|
```sql
|
|
CREATE TABLE messages (
|
|
id UUID PRIMARY KEY,
|
|
conversation_id UUID NOT NULL,
|
|
sender_id UUID NOT NULL,
|
|
content TEXT NOT NULL,
|
|
-- ... autres colonnes ...
|
|
is_edited BOOLEAN NOT NULL DEFAULT FALSE,
|
|
is_deleted BOOLEAN NOT NULL DEFAULT FALSE,
|
|
edited_at TIMESTAMPTZ,
|
|
deleted_at TIMESTAMPTZ,
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
```
|
|
|
|
### Migration
|
|
|
|
La migration `005_message_edit_delete.sql` ajoute :
|
|
- `deleted_at` : Timestamp de suppression (pour la traçabilité)
|
|
- Index sur `deleted_at` pour les requêtes de nettoyage
|
|
- Index sur `edited_at` pour les requêtes de recherche
|
|
|
|
### Soft Delete
|
|
|
|
Les messages ne sont **jamais supprimés physiquement** de la base de données. Au lieu de cela :
|
|
- `is_deleted` est mis à `true`
|
|
- `deleted_at` est mis à `NOW()`
|
|
- Le contenu reste dans la base de données (pour audit futur)
|
|
|
|
**Note** : Pour une implémentation future, on pourrait :
|
|
- Créer une table `message_archive` pour stocker les messages supprimés
|
|
- Vider le contenu du message après suppression (mettre `content` à `NULL` ou `""`)
|
|
|
|
---
|
|
|
|
## Services
|
|
|
|
### MessageEditService
|
|
|
|
Service centralisé pour l'édition et la suppression de messages.
|
|
|
|
#### `edit_message(user_id, message_id, new_content) -> Result<Message>`
|
|
|
|
Édite un message avec validation complète.
|
|
|
|
**Validation** :
|
|
1. Contenu non vide (après trim)
|
|
2. Longueur maximale (4000 caractères)
|
|
3. Contenu différent de l'original
|
|
4. Message non supprimé
|
|
5. Permissions d'édition
|
|
|
|
**Mise à jour DB** :
|
|
- `content` = nouveau contenu
|
|
- `is_edited` = `true`
|
|
- `edited_at` = `NOW()`
|
|
- `updated_at` = `NOW()`
|
|
|
|
#### `delete_message(user_id, message_id) -> Result<Message>`
|
|
|
|
Supprime un message (soft delete).
|
|
|
|
**Validation** :
|
|
1. Permissions de suppression
|
|
|
|
**Mise à jour DB** :
|
|
- `is_deleted` = `true`
|
|
- `deleted_at` = `NOW()`
|
|
- `updated_at` = `NOW()`
|
|
|
|
**Idempotence** : Si le message est déjà supprimé, retourne le message tel quel sans erreur.
|
|
|
|
---
|
|
|
|
## Exemples d'utilisation
|
|
|
|
### Édition d'un message
|
|
|
|
**Client** :
|
|
```json
|
|
{
|
|
"type": "EditMessage",
|
|
"message_id": "550e8400-e29b-41d4-a716-446655440000",
|
|
"conversation_id": "660e8400-e29b-41d4-a716-446655440000",
|
|
"new_content": "Correction : Nouveau contenu"
|
|
}
|
|
```
|
|
|
|
**Réponse (confirmation)** :
|
|
```json
|
|
{
|
|
"type": "ActionConfirmed",
|
|
"action": "message_edited",
|
|
"success": true
|
|
}
|
|
```
|
|
|
|
**Broadcast (tous les clients de la conversation)** :
|
|
```json
|
|
{
|
|
"type": "MessageEdited",
|
|
"message_id": "550e8400-e29b-41d4-a716-446655440000",
|
|
"conversation_id": "660e8400-e29b-41d4-a716-446655440000",
|
|
"editor_id": "770e8400-e29b-41d4-a716-446655440000",
|
|
"edited_at": "2025-12-05T10:30:00Z",
|
|
"new_content": "Correction : Nouveau contenu"
|
|
}
|
|
```
|
|
|
|
### Suppression d'un message
|
|
|
|
**Client** :
|
|
```json
|
|
{
|
|
"type": "DeleteMessage",
|
|
"message_id": "550e8400-e29b-41d4-a716-446655440000",
|
|
"conversation_id": "660e8400-e29b-41d4-a716-446655440000"
|
|
}
|
|
```
|
|
|
|
**Réponse (confirmation)** :
|
|
```json
|
|
{
|
|
"type": "ActionConfirmed",
|
|
"action": "message_deleted",
|
|
"success": true
|
|
}
|
|
```
|
|
|
|
**Broadcast (tous les clients de la conversation)** :
|
|
```json
|
|
{
|
|
"type": "MessageDeleted",
|
|
"message_id": "550e8400-e29b-41d4-a716-446655440000",
|
|
"conversation_id": "660e8400-e29b-41d4-a716-446655440000",
|
|
"deleter_id": "770e8400-e29b-41d4-a716-446655440000",
|
|
"deleted_at": "2025-12-05T10:30:00Z"
|
|
}
|
|
```
|
|
|
|
### Gestion des erreurs
|
|
|
|
**Permission refusée** :
|
|
```json
|
|
{
|
|
"type": "Error",
|
|
"message": "Permissions insuffisantes pour edit_message dans la conversation 660e8400-e29b-41d4-a716-446655440000"
|
|
}
|
|
```
|
|
|
|
**Message introuvable** :
|
|
```json
|
|
{
|
|
"type": "Error",
|
|
"message": "Message 550e8400-e29b-41d4-a716-446655440000 introuvable"
|
|
}
|
|
```
|
|
|
|
**Message supprimé (tentative d'édition)** :
|
|
```json
|
|
{
|
|
"type": "Error",
|
|
"message": "Un message supprimé ne peut pas être édité"
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Conséquences UX
|
|
|
|
### Affichage des messages édités
|
|
|
|
Lorsqu'un message est édité, l'interface utilisateur doit :
|
|
|
|
1. **Afficher le nouveau contenu** : Remplacer l'ancien contenu par le nouveau
|
|
2. **Indicateur visuel** : Afficher un indicateur "Édité" (ex: "✏️ Édité")
|
|
3. **Timestamp d'édition** : Optionnellement afficher `edited_at` au survol
|
|
4. **Historique** : Pour une implémentation future, on pourrait afficher l'historique des éditions
|
|
|
|
**Exemple d'affichage** :
|
|
```
|
|
[Utilisateur] Message original ✏️ Édité
|
|
```
|
|
|
|
### Affichage des messages supprimés
|
|
|
|
Lorsqu'un message est supprimé, l'interface utilisateur doit :
|
|
|
|
1. **Placeholder** : Afficher un placeholder comme "Message supprimé" ou "Ce message a été supprimé"
|
|
2. **Style visuel** : Utiliser un style atténué (gris, italique)
|
|
3. **Informations limitées** : Ne pas afficher le contenu original
|
|
4. **Timestamp** : Optionnellement afficher `deleted_at`
|
|
|
|
**Exemple d'affichage** :
|
|
```
|
|
[Utilisateur] Ce message a été supprimé
|
|
```
|
|
|
|
### Cohérence multi-device
|
|
|
|
Les événements WebSocket garantissent que :
|
|
- Tous les clients connectés à la conversation reçoivent les mises à jour en temps réel
|
|
- Les modifications sont synchronisées instantanément
|
|
- Pas besoin de rafraîchir la page
|
|
|
|
---
|
|
|
|
## Impact sur la recherche et pagination
|
|
|
|
### Recherche
|
|
|
|
Les messages supprimés sont **exclus** des résultats de recherche par défaut.
|
|
|
|
**Requête SQL** :
|
|
```sql
|
|
SELECT * FROM messages
|
|
WHERE conversation_id = $1
|
|
AND is_deleted = false
|
|
AND content ILIKE $2
|
|
ORDER BY created_at DESC;
|
|
```
|
|
|
|
**Note** : Pour une implémentation future, on pourrait :
|
|
- Permettre aux admins de rechercher dans les messages supprimés
|
|
- Créer une vue `messages_active` qui exclut automatiquement les messages supprimés
|
|
|
|
### Pagination
|
|
|
|
Les messages supprimés sont **exclus** de la pagination par défaut.
|
|
|
|
**Requête SQL** :
|
|
```sql
|
|
SELECT * FROM messages
|
|
WHERE conversation_id = $1
|
|
AND is_deleted = false
|
|
ORDER BY created_at DESC
|
|
LIMIT $2 OFFSET $3;
|
|
```
|
|
|
|
**Placeholder dans la liste** : Si un message est supprimé pendant qu'un utilisateur consulte l'historique, il peut être remplacé par un placeholder dans la liste.
|
|
|
|
### Impact sur les métriques
|
|
|
|
- Les messages supprimés ne sont pas comptés dans les statistiques de messages
|
|
- Les messages édités sont comptés comme des messages normaux (pas de double comptage)
|
|
|
|
---
|
|
|
|
## Tests
|
|
|
|
Les tests sont disponibles dans `tests/chat_edit_delete.rs` :
|
|
|
|
- ✅ Édition par l'auteur
|
|
- ✅ Édition interdite pour un non-auteur
|
|
- ✅ Édition interdite pour un message supprimé
|
|
- ✅ Édition avec contenu identique interdite
|
|
- ✅ Édition avec contenu vide interdite
|
|
- ✅ Suppression par l'auteur
|
|
- ✅ Suppression par un admin
|
|
- ✅ Suppression interdite pour un non-auteur
|
|
- ✅ Suppression idempotente
|
|
- ✅ Validation de la longueur maximale
|
|
|
|
**Note** : Les tests nécessitent une base de données de test et sont marqués avec `#[ignore]`.
|
|
|
|
---
|
|
|
|
## Améliorations futures
|
|
|
|
1. **Limitation de temps** : Fenêtre d'édition/suppression limitée
|
|
2. **Historique d'édition** : Stocker l'historique des modifications
|
|
3. **Archive de messages** : Table séparée pour les messages supprimés
|
|
4. **Raison de suppression** : Champ optionnel pour la raison de suppression (modération)
|
|
5. **Recherche dans les supprimés** : Permettre aux admins de rechercher dans les messages supprimés
|
|
6. **Notifications** : Notifier l'auteur lorsqu'un admin supprime son message
|
|
|
|
---
|
|
|
|
## Références
|
|
|
|
- Migration : `migrations/005_message_edit_delete.sql`
|
|
- Service : `src/services/message_edit_service.rs`
|
|
- Permissions : `src/security/permission.rs`
|
|
- Repository : `src/repository/message_repository.rs`
|
|
- WebSocket : `src/websocket/handler.rs`
|
|
- Tests : `tests/chat_edit_delete.rs`
|
|
|