Decap CMS : architecture et mise en production
Decap CMS ajoute au site une interface d’édition, accessible à l’adresse /_cms/.
Le _ initial, comme pour les fichiers /_astro/ du site, évite tout conflit avec une section
de la documentation : un dossier admin/ ou cms/ dans src/content/docs/ produirait des pages
à la même adresse que l’interface.
Les rédacteurs y modifient les pages à l’aide de formulaires et d’un éditeur visuel ;
chaque enregistrement devient un commit sur le dépôt Git.
Rédacteur ──► /_cms/ (Decap CMS) ──► commit sur Bitbucket ──► Jenkins : npm run build ──► déploiementCette page décrit le fonctionnement technique de Decap et sa mise en production. Pour l’utilisation au quotidien, voir Éditer avec Decap CMS.
Comment fonctionne Decap
Section intitulée « Comment fonctionne Decap »« Headless » ne veut pas dire « sans back-end »
Section intitulée « « Headless » ne veut pas dire « sans back-end » »Un CMS headless (« sans tête ») gère le contenu mais ne produit pas le site : l’affichage est confié
à un autre outil. Ici, Decap modifie des fichiers .md, et c’est Astro qui en fait des pages.
C’est l’inverse d’un CMS traditionnel comme WordPress, qui stocke le contenu et génère les pages.
Headless ne signifie pas pour autant « sans serveur » : beaucoup de CMS headless ont un back-end complet. Ce qui distingue Decap, c’est d’être basé sur Git :
| CMS traditionnel (WordPress) | CMS headless « API » (Strapi, Directus…) | CMS headless « Git » (Decap) | |
|---|---|---|---|
| Produit les pages du site | Oui | Non | Non |
| Où est stocké le contenu | Base de données | Base de données | Fichiers dans le dépôt Git |
| Serveur d’application | Oui (PHP) | Oui (Node.js…) | Non |
| Historique des modifications | Limité | Variable | Celui de Git (commits) |
Tout se passe dans le navigateur
Section intitulée « Tout se passe dans le navigateur »Decap est une application JavaScript chargée par la page /_cms/, un fichier statique comme les autres.
Une fois chargée, elle s’exécute entièrement dans le navigateur du rédacteur. Son « back-end », c’est
l’API de Bitbucket, qu’elle appelle directement :
- pour afficher la liste des pages, elle lit le contenu des dossiers du dépôt ;
- pour ouvrir une page, elle télécharge le fichier
.md; - pour enregistrer, elle crée un commit contenant le fichier modifié (et l’image téléversée, le cas échéant).
Il n’y a donc ni serveur d’application, ni base de données à installer, sauvegarder ou mettre à jour. Toutes les données sont dans le dépôt Git.
Navigateur du rédacteur └─ Decap CMS (/_cms/) │ ├── lecture des fichiers, commits ────────────────────► API Bitbucket (api.bitbucket.org) │ └── connexion seulement ──► serveur OAuth (/auth) ────► BitbucketL’exception : la connexion
Section intitulée « L’exception : la connexion »Pour appeler l’API de Bitbucket, Decap a besoin d’un jeton d’accès au nom du rédacteur. Bitbucket ne le délivre qu’en échange d’un secret propre à l’application, et un secret ne peut pas figurer dans une page web, où n’importe qui pourrait le lire. Un petit serveur intermédiaire, le serveur OAuth, détient ce secret et n’intervient qu’à deux moments :
- À la connexion : il échange le code d’autorisation fourni par Bitbucket contre un jeton, et le transmet au navigateur.
- À l’expiration du jeton : il le renouvelle.
Le contenu des pages ne passe jamais par lui. Il ne stocke rien, n’a pas de base de données, et peut être redémarré à tout moment : c’est un simple relais d’authentification. Sa mise en place est décrite plus bas, dans Mise en production.
Et en local ?
Section intitulée « Et en local ? »En développement, npm run cms lance decap-server, qui joue le rôle de Bitbucket : au lieu d’appeler
l’API distante, Decap lit et écrit directement les fichiers du disque. Ce serveur ne sert qu’en local ;
il n’est jamais déployé.
| En local | En production | |
|---|---|---|
| Où Decap lit et écrit | Fichiers du disque, via decap-server |
Dépôt Bitbucket, via son API |
| Connexion | Aucune (bouton « Se connecter ») | Compte Bitbucket, via le serveur OAuth |
| Résultat d’un enregistrement | Fichier modifié, sans commit | Commit sur le dépôt |
Les fichiers concernés
Section intitulée « Les fichiers concernés »| Fichier | Rôle |
|---|---|
public/_cms/index.html |
Charge l’application Decap CMS, et déclare le composant « Encart » (voir plus bas) |
public/_cms/config.yml |
Configuration : dépôt Bitbucket, collections, champs des formulaires, dossier des images |
deploy/oauth/server.mjs |
Petit serveur de connexion OAuth entre Decap et Bitbucket (production uniquement) |
deploy/nginx-auth.conf, deploy/docker-compose.yml |
Mise en service du site et du serveur OAuth sur un même domaine |
Essayer en local
Section intitulée « Essayer en local »En local, Decap peut travailler directement sur les fichiers du disque, sans Bitbucket ni connexion. Il faut deux terminaux :
npm run dev # le site, sur http://localhost:4321npm run cms # le serveur local de Decap (port 8082), qui lit et écrit dans src/content/docs/Puis ouvrir http://localhost:4321/_cms/index.html et cliquer sur « Se connecter ».
Les modifications sont écrites immédiatement dans src/content/docs/ et visibles sur le site de
développement. En local, aucun commit n’est créé : c’est à vous de les faire.
Ce qui est modifiable
Section intitulée « Ce qui est modifiable »Le CMS présente une collection par section du site et par langue : Guides et Guides (EN),
Comprendre Astro & Starlight et sa version (EN), etc. Les collections anglaises pointent vers
src/content/docs/en/<section>/. La collection CMS a en plus les champs de la fiche
(site officiel, GitHub, licence, modèle, Bitbucket).
Les nouvelles pages créées depuis le CMS apparaissent automatiquement dans la barre latérale,
puisque celle-ci est autogénérée.
Seuls les fichiers .md sont gérés. Les pages .mdx (accueil, pages utilisant des composants)
n’apparaissent pas dans le CMS et restent modifiées dans le code : l’éditeur de Decap ne sait pas
manipuler les imports et composants MDX.
Le formulaire de chaque page reprend les champs du frontmatter :
| Champ du formulaire | Champ du frontmatter | Remarque |
|---|---|---|
| Nom du fichier | fichier |
Obligatoire ; sert à nommer le fichier à la création |
| Titre | title |
Obligatoire |
| Description | description |
Facultatif ; omis du fichier s’il est vide |
| Barre latérale → Ordre | sidebar.order |
Position dans le menu |
| Barre latérale → Masquer du menu | sidebar.hidden |
|
| Brouillon | draft |
Page exclue du site publié |
| Contenu | (corps de la page) | Éditeur visuel ou Markdown brut |
Le nom du fichier (et donc l’URL) vient du champ Nom du fichier (slug: "{{fields.fichier}}"
dans config.yml), et non du titre : sinon une page créée dans une collection française aurait
un nom français, que sa traduction devrait reprendre. Le champ est contrôlé par une expression
régulière (minuscules, chiffres et tirets). Le champ fichier est aussi enregistré dans le frontmatter :
il est déclaré dans src/content.config.ts, sans effet sur le site.
Les images
Section intitulée « Les images »Une image insérée depuis l’éditeur est enregistrée dans src/assets/images/ et référencée par un chemin relatif
(../../../assets/images/photo.webp). Elle est donc optimisée par Astro comme toute autre image.
Les particularités de l’éditeur visuel
Section intitulée « Les particularités de l’éditeur visuel »L’éditeur « Texte enrichi » de Decap convertit la page en interne puis la réécrit à l’enregistrement. Les essais réalisés sur ce site (y compris sur cette page) ont montré :
| Élément | Comportement |
|---|---|
| Titres, listes, liens, gras, blocs de code | Conservés à l’identique |
Encarts Starlight (:::note…) |
Affichés comme des blocs « Encart » avec un petit formulaire (type, titre, contenu), et conservés |
HTML (<div class="…">, <details>) |
Affiché en texte brut dans l’éditeur, mais conservé |
| Tableaux | Conservés, colonnes réalignées |
| Listes à puces | Conservées, - remplacé par * |
| Description longue | Coupée sur deux lignes dans le frontmatter (YAML équivalent) |
| Ligne vide après le frontmatter | Supprimée (sans effet sur le rendu) |
Syntaxe Markdown tapée au clavier (**gras**) |
Échappée (\*\*gras\*\*) : dans l’éditeur visuel, on utilise les boutons de la barre d’outils |
Le panneau d’aperçu, à droite de l’éditeur, n’utilise pas le style du site : c’est un aperçu du contenu, pas de la mise en page finale.
Mise en production avec Bitbucket Cloud
Section intitulée « Mise en production avec Bitbucket Cloud »Le serveur OAuth
Section intitulée « Le serveur OAuth »Comme expliqué dans L’exception : la connexion, la connexion à Bitbucket
passe par un petit serveur qui détient le secret de l’application. C’est le rôle de deploy/oauth/server.mjs (une centaine de lignes, sans dépendance). Il est servi
sur le même domaine que le site, sous /auth, via Nginx :
| Adresse | Rôle |
|---|---|
GET /auth |
Redirige le rédacteur vers la page d’autorisation de Bitbucket |
GET /auth/callback |
Reçoit le code de Bitbucket, l’échange contre un jeton, et le transmet à Decap |
POST /auth/refresh |
Renouvelle le jeton lorsqu’il expire (Bitbucket fait tourner les refresh tokens) |
Le rédacteur ne voit qu’une fenêtre « Se connecter avec Bitbucket », puis il est connecté.
1. Créer le consumer OAuth dans Bitbucket
Section intitulée « 1. Créer le consumer OAuth dans Bitbucket »Dans Bitbucket : Workspace settings → OAuth consumers → Add consumer.
- Callback URL :
https://doc.exemple.com/auth/callback(adresse publique du site +/auth/callback) - Permissions : Account → Read, Repositories → Write, et Pull requests → Write si le workflow éditorial est activé
Bitbucket fournit alors une Key et un Secret.
2. Configurer le serveur OAuth
Section intitulée « 2. Configurer le serveur OAuth »cp deploy/.env.exemple deploy/.envPuis renseigner BITBUCKET_KEY, BITBUCKET_SECRET et SITE_ORIGIN (ex. https://doc.exemple.com).
Le fichier deploy/.env contient un secret : il est exclu de Git.
3. Adapter public/_cms/config.yml
Section intitulée « 3. Adapter public/_cms/config.yml »backend: name: bitbucket repo: mon-espace/mon-depot # espace de travail/dépôt branch: main base_url: https://doc.exemple.com auth_endpoint: auth
base_url: https://doc.exemple.com # oui, une seconde fois, à la racine4. Déployer
Section intitulée « 4. Déployer »Sur AWS (solution retenue) : une petite instance créée par OpenTofu (deploy/aws/) héberge
Caddy, qui sert le site en HTTPS et relaie /auth vers le serveur OAuth. Jenkins compile et dépose
le site à chaque push sur main (Jenkinsfile, task deploy). Les secrets Bitbucket sont des variables
OpenTofu, et non un fichier .env. La procédure complète est dans deploy/aws/README.md.
Avec Docker, sur un serveur existant :
npm run builddocker compose -f deploy/docker-compose.yml up -dLe service site (Nginx) sert dist/ et relaie /auth vers le service oauth.
Le HTTPS est alors ajouté devant, par un reverse proxy ou un load balancer.
Chaque rédacteur doit avoir un compte Bitbucket avec un droit d’écriture sur le dépôt.
Pour aller plus loin
Section intitulée « Pour aller plus loin »- Workflow éditorial : avec
publish_mode: editorial_workflowdansconfig.yml, chaque modification crée une branche et une pull request Bitbucket, au lieu d’un commit direct surmain. Le CMS affiche alors un tableau « Brouillon / En relecture / Prêt » ; la publication correspond à la fusion de la pull request. - Version de Decap : l’interface est chargée depuis le CDN unpkg (
decap-cms@^3.16.3). Pour ne dépendre d’aucun service externe, on peut copier le fichierdecap-cms.jsdanspublic/_cms/. - Documentation officielle : decapcms.org/docs.