Ghost + Discord : Synchroniser les rôles membres avec un Cloudflare Worker
Quand j'ai finalement décidé d'activer le mode membre sur Ghost pour la newsletter, une question s'est posée assez naturellement : comment synchroniser automatiquement les abonnements Ghost avec les rôles sur le serveur Discord Geeek ?
La solution évidente, c'est de passer par du NoCode — Make, Zapier et consorts. Mais la logique d'appairage à mettre en place n'est pas forcément triviale, même en LowCode. Il faut gérer plusieurs cas : l'ajout d'un membre, sa montée en gamme vers un abonnement payant, son downgrade, sa suppression... Et surtout, il faut faire le lien entre une adresse email Ghost et un compte Discord, ce qui n'est pas directement exposé par ces outils.
J'ai donc décidé de coder moi-même le maillon manquant hier soir.
L'architecture en deux mots
Le projet s'appelle ghost-discord-worker et repose sur trois briques : Ghost, un Cloudflare Worker, et l'API Discord.

Le principe est simple : Ghost envoie un webhook au Worker Cloudflare à chaque événement membre (ajout, modification, suppression). Le Worker consulte un KV store Cloudflare — une table d'appairage email ↔ discord_user_id — et met à jour les rôles Discord en conséquence.
Le cas le plus intéressant, c'est quand un utilisateur n'est pas encore connu du Worker. Dans ce cas, il est invité à taper la commande /link <email> directement sur le serveur Discord. Le Worker vérifie alors que l'email correspond bien à un membre Ghost existant (via l'Admin API), enregistre l'association dans le KV, et lui attribue le bon rôle immédiatement.
Les événements gérés
Le Worker couvre l'ensemble du cycle de vie d'un membre Ghost :
- Membre ajouté (free) → rôle Member
- Membre ajouté (paid/comped) → rôles Member + Premium Member
- Membre supprimé → suppression de tous les rôles
- Mise à jour free → paid → ajout du rôle Premium Member
- Mise à jour paid → free → suppression du rôle Premium Member
Installation en 7 étapes
Étape 1 : Cloner le dépôt et installer les dépendances
git clone https://github.com/ltoinel/ghost-discord-worker
cd ghost-discord-worker
npm install
Étape 2 : Créer le namespace KV
npx wrangler kv namespace create GHOST_DISCORD_MAPPING
Récupérez l'id retourné et remplacez REPLACE_WITH_KV_NAMESPACE_ID dans le fichier wrangler.toml.
Étape 3 : Configurer les secrets Cloudflare
npx wrangler secret put WEBHOOK_SECRET
npx wrangler secret put DISCORD_BOT_TOKEN
npx wrangler secret put DISCORD_GUILD_ID
npx wrangler secret put DISCORD_PUBLIC_KEY
npx wrangler secret put DISCORD_ROLE_MEMBER
npx wrangler secret put DISCORD_ROLE_PREMIUM
npx wrangler secret put GHOST_URL
npx wrangler secret put GHOST_ADMIN_API_KEY
Étape 4 : Déployer le Worker
npm run deploy
Étape 5 : Configurer les webhooks dans Ghost
Dans Ghost Admin → Settings → Integrations → Custom Integration, créer trois webhooks :
| Événement | URL |
|---|---|
| Member added | https://<worker>.workers.dev/webhook?secret=<WEBHOOK_SECRET> |
| Member updated | https://<worker>.workers.dev/webhook?secret=<WEBHOOK_SECRET> |
| Member deleted | https://<worker>.workers.dev/webhook/deleted?secret=<WEBHOOK_SECRET> |
Étape 6 : Enregistrer les slash commands Discord
Enregistrez les commandes /link et /unlink via l'API Discord et configurez l'Interactions Endpoint URL vers https://<worker>.workers.dev/discord dans le Discord Developer Portal.
Étape 7 : Vérifier la hiérarchie des rôles Discord
Important : le rôle du bot doit être positionné au-dessus des rôles Member et Premium Member dans la hiérarchie de votre serveur Discord. Sans ça, le bot ne pourra pas attribuer les rôles.
Un mot sur la sécurité
J'ai pris soin d'implémenter quelques bonnes pratiques : les webhooks Ghost sont authentifiés via un secret partagé, les interactions Discord sont vérifiées par signature Ed25519, et les endpoints d'administration /link sont protégés par un Bearer token. Le Worker vérifie également que l'email fourni lors du /link correspond bien à un membre Ghost existant avant d'enregistrer l'appairage.
Conclusion
Le projet est disponible sur GitHub : ltoinel/ghost-discord-worker. Le code est en TypeScript, tourne entièrement sur l'infrastructure Cloudflare (Worker + KV), et ne nécessite donc aucun serveur à maintenir.
J'espère que cet article vous sera utile si vous gérez vous aussi une communauté sur Ghost et Discord. N'hésitez pas à venir en discuter sur le serveur Discord Geeek !
