Édition collaborative
Principe de l’édition collaborative
Fatplant intègre un système d’édition collaborative temps réel qui permet à plusieurs journalistes ou éditeurs de travailler simultanément sur le même article ou la même page, sans risque de conflit ou de perte de données.
Cette fonctionnalité repose sur deux technologies clés :
- Yjs : une implémentation de l’algorithme CRDT (Conflict-free Replicated Data Type)
- WebSocket : le canal de communication temps réel entre les navigateurs et le serveur
Qu’est-ce qu’un CRDT ?
Un CRDT (type de données répliqué sans conflit) est une structure de données qui peut être modifiée simultanément par plusieurs acteurs, et dont les modifications peuvent toujours être fusionnées de manière déterministe, sans nécessiter de coordination centrale.
Concrètement, si l’éditeur A et l’éditeur B modifient le même paragraphe en même temps, l’algorithme CRDT garantit que les deux versions seront fusionnées correctement — sans écraser l’une ou l’autre.
Architecture du système
Navigateur A Serveur Yjs Navigateur B
(Éditeur 1) (WebSocket) (Éditeur 2)
│ │ │
│── modification ───>│ │
│ │── sync diff ──────>│
│<── présence ───────│<── modification ───│
│ │ │
│ │── persistance ─────> Backend Symfony
│ │ (HTTP POST) Le serveur Yjs
Le serveur Yjs est un processus Node.js indépendant qui joue le rôle de hub de synchronisation. Il maintient en mémoire les documents Yjs actifs (appelés rooms) et relaie les mises à jour entre tous les clients connectés à la même room.
Configuration
# Mode de persistance du serveur Yjs
YJS_SERVER_PERSIST=http
# URL du backend pour persister les documents
YJS_BACKEND_URL=http://backend:8000
# Clé partagée pour authentifier les appels de persistance
YJS_BACKEND_SECRET=votre_secret_partage Avec YJS_SERVER_PERSIST=http, le serveur Yjs persiste périodiquement l’état de chaque document dans le backend Symfony via une requête HTTP. Cela garantit que le contenu est sauvegardé même si le serveur Yjs redémarre.
Gestion de la mémoire
Les rooms inactives (sans client connecté) sont automatiquement évincées de la mémoire du serveur Yjs après un délai configurable. Avant l’éviction, un flush de persistance est déclenché pour ne pas perdre les dernières modifications.
Présence
La présence permet de voir en temps réel qui est en train d’éditer un document. Chaque utilisateur connecté à une room dispose d’un curseur de couleur unique visible par les autres collaborateurs.
Les informations de présence transmises incluent :
- Le nom d’affichage de l’utilisateur
- Sa couleur (assignée aléatoirement à la connexion)
- La position de son curseur dans le document
Les données de présence sont éphémères : elles ne sont jamais persistées dans la base de données.
Autosave
Le contenu est sauvegardé automatiquement selon deux mécanismes complémentaires :
- Autosave local : l’administration SvelteKit envoie les modifications à l’API backend toutes les quelques secondes si le document a changé.
- Flush Yjs : le serveur Yjs persiste l’état complet du document dans le backend lors de l’éviction de la room ou à intervalles réguliers.
Ce double mécanisme garantit qu’une modification n’est jamais perdue, même en cas de coupure réseau brève.
Intégration dans l’admin SvelteKit
L’administration utilise la bibliothèque yjs-bridge.ts pour connecter les composants Svelte au document Yjs :
// Connexion à une room Yjs pour un article
const room = await connectRoom(`article:${articleId}`);
// Écoute des mises à jour du document
room.on('update', (update) => {
applyDocumentUpdate(update);
});
// Mise à jour locale
applyLocalChange(room.doc, change); Cas de déconnexion
Si un utilisateur perd sa connexion réseau, Yjs met en file d’attente les modifications locales et les synchronise dès que la connexion est rétablie. Le document local continue de fonctionner en mode hors-ligne grâce au CRDT.
Limites actuelles
- Un seul serveur Yjs est supporté (pas de cluster multi-nœuds pour le moment).
- La présence est limitée aux utilisateurs connectés à l’administration (pas de collaboration avec des utilisateurs anonymes).