Structure des dossiers
Cartographie complète du projet Le Labo du Yeti : du code source backend/frontend aux données utilisateur (DB, médias, extensions), en passant par les templates et les artefacts de build. Cette page sert de référence pour comprendre où vit chaque chose et, surtout, ce qu'il ne faut jamais modifier à la main.
.env.
Vue d'ensemble de l'arborescence
À la racine du projet, on retrouve trois grandes familles : le code applicatif (backend/, frontend/), les données runtime (content/, uploads/, public/, brouillons/, backups/) et les ressources partagées (templates/, extensions/, documentation/).
cms-ia/
├── backend/ # API Node 22 + Express (ESM)
│ ├── src/
│ │ ├── routes/ # Définition Express : /api/*
│ │ ├── controllers/ # HTTP handlers (validation, réponses)
│ │ ├── services/ # Logique métier (auth, blog, cocon, ai, etc.)
│ │ ├── middleware/ # requireAuth, requireRole, requireCapability, rate-limit
│ │ ├── utils/ # db-store.js, datetime, paths, logger, etc.
│ │ └── config/ # Chargement .env, paths absolus, constantes
│ ├── scripts/ # Migrations one-shot, seeders, tools CLI
│ └── .env.example # Modèle de configuration (à committer)
│
├── frontend/ # SPA React 18 + Vite + Tailwind
│ ├── src/
│ │ ├── pages/ # Routes React (Dashboard, Pages, Blog, Cocon…)
│ │ ├── components/ # UI réutilisables (Sidebar, Topbar, MediaPicker…)
│ │ ├── services/ # Clients HTTP (api.js, ai.js, blog.js…)
│ │ ├── stores/ # Zustand (auth, ui, branding, capabilities)
│ │ ├── lib/ # Helpers (datetime, slugify, validators)
│ │ └── hooks/ # useCapability, useDebounce, etc.
│ └── dist/ # Build Vite (committé : voir Déploiement)
│
├── content/ # Données runtime
│ └── cms.db # SQLite (WAL) — auto-créée au boot
│
├── public/ # Site statique servi par Plesk (Document Root)
│ ├── index.html # Sortie du build (page d'accueil)
│ ├── blog/ # Articles compilés
│ ├── sitemap.xml
│ ├── robots.txt
│ └── 404.html
│
├── uploads/ # Médiathèque (images, vidéos, PDF)
│
├── brouillons/ # Previews cocon non publiés
│
├── extensions/
│ └── installed/ # Un dossier par extension (manifest + code)
│ ├── hello-world/
│ ├── webp-auto-converter/
│ └── surefeedback-connector/
│
├── templates/ # Bibliothèques HTML système
│ ├── library/ # Templates pages prédéfinis (versionnés)
│ ├── layouts/ # Layouts globaux (header/footer/structure)
│ ├── pages/ # Templates pages utilisateur (par UUID)
│ ├── articles/ # Templates articles blog (par UUID)
│ └── cocon/ # Templates content groups (par UUID)
│
├── backups/ # Snapshots ZIP (export/restore, safety backups)
│
└── documentation/ # Cette doc HTML (rendue statique)
├── tech/
├── user/
└── assets/
Backend (backend/)
Le backend est une application Node 22 LTS en ESM qui expose une API REST sous /api/* et sert également les fichiers statiques en développement. Toute la logique métier vit ici.
Organisation backend/src/
| Dossier | Responsabilité | Exemples |
|---|---|---|
routes/ | Déclaration des endpoints Express, chaînage des middlewares | blog.routes.js, cocon.routes.js, auth.routes.js |
controllers/ | HTTP handlers — validation entrée, appel service, sérialisation réponse | pages.controller.js, ai.controller.js |
services/ | Logique métier pure, accès DB, orchestration | blog-service.js, cocon-service.js, ai-service.js |
middleware/ | Auth (JWT + tokens API), RBAC, capabilities, rate-limit, uploads multer | requireAuth, requireRole, requireCapability |
utils/ | Helpers transverses sans état métier | db-store.js (~50 helpers SQLite), datetime.js, paths.js |
config/ | Chargement environnement, constantes, paths absolus | env.js, paths.js |
Scripts (backend/scripts/)
Scripts CLI one-shot pour les migrations, l'amorçage et la maintenance. Exécutés manuellement (node backend/scripts/<name>.js), jamais au démarrage normal. Les migrations idempotentes intégrées au boot (ex. migrateAdminsToUsers, migrateLegacyRoles) vivent à l'intérieur de db-store.js.
Configuration : .env.example
Le fichier backend/.env.example documente toutes les variables d'environnement nécessaires (port, URLs, secrets, fournisseurs IA, SMTP). Il est commité. Le .env réel, lui, n'est jamais versionné — en production Plesk, les vars sont injectées via l'interface Node.js du panel, pas via un fichier.
JWT_SECRET. Ne jamais réutiliser une valeur entre clients : c'est une faille d'identité multi-tenant.
Frontend (frontend/)
Single-Page Application React 18 + Vite + Tailwind CSS. État global via Zustand, communication HTTP via fetch wrappés dans services/api.js.
| Dossier | Contenu |
|---|---|
pages/ | Une route React = un fichier. Dashboard.jsx, Pages.jsx, BlogArticles.jsx, CoconGroups.jsx, Settings/*… |
components/ | UI réutilisable : Sidebar, Topbar, MediaPicker, BuilderIframe, Modal, Toast… |
services/ | Clients HTTP typés par domaine : auth.js, blog.js, cocon.js, ai.js, media.js… |
stores/ | Zustand stores : authStore, uiStore, brandingStore, capabilitiesStore. |
lib/ | Pur utilitaire : datetime.js (formats FR centralisés), slugify.js, validators.js. |
hooks/ | Hooks React custom : useCapability, useDebounce, useLocalStorage. |
Build (frontend/dist/)
Sortie de vite build — committée dans le repo. C'est volontaire : on évite de builder sur Plesk (problèmes RAM connus). À terme, le build sera produit par GitHub Actions sur la branche release ; en attendant, le développeur commit dist/ après chaque modification du frontend.
Données runtime : content/
Contient un unique fichier : cms.db, la base SQLite (mode WAL) qui stocke toutes les données du CMS — utilisateurs, pages, articles, cocon, logs, tokens, extensions, KV stores, usage IA, etc. Voir Base de données pour la liste détaillée des 8 tables et leurs schémas.
content/cms.db à la main. Le fichier est en mode WAL : trois fichiers cohabitent (cms.db, cms.db-wal, cms.db-shm) et un patch manuel via SQLite Browser pendant que l'app tourne corrompra la base. Pour toute modification : passer par l'API, ou arrêter l'app puis exécuter un script backend/scripts/*.js.
Le fichier est auto-créé au boot s'il n'existe pas (zéro-config). Les seeds par défaut (stores auth, global, blog, cocon, permissions…) sont injectés automatiquement par seedMissingStores().
Sortie de build : public/
Le Document Root pointé par Plesk. Ne contient que des fichiers statiques (HTML, CSS, JS, images compilées). C'est la photo du site à un instant T, produite par le service buildSite().
| Fichier / dossier | Rôle |
|---|---|
index.html | Page d'accueil compilée (page racine du site) |
<slug>/index.html | Chaque page CMS génère un dossier + index.html pour URLs propres |
blog/<slug>/ | Articles de blog compilés |
<parent>/<enfant>/ | Pages cocon avec slugs composites parent/enfant |
sitemap.xml | Sitemap regénéré par POST /api/build/sitemap |
robots.txt | Directives crawlers |
404.html | Page d'erreur personnalisable (Réglages → Page 404) |
uploads/ | Lien ou copie vers la médiathèque pour exposition publique |
public/ est régénérable. Lancer un build (POST /api/build) le reconstruit intégralement à partir des données SQLite + templates. Ne jamais éditer un fichier public/*.html à la main : la prochaine génération l'écrase.
Médiathèque : uploads/
Stockage des médias uploadés par les utilisateurs : images (JPG, PNG, WebP, SVG), vidéos, PDF, etc. Une seule arborescence à plat (pas de sous-dossiers par utilisateur) — la table SQLite référence les fichiers par leur nom.
Cette racine est exposée publiquement par le serveur (route statique /uploads/*) et également accessible via l'API /api/media. L'extension webp-auto-converter peut intercepter les uploads pour les convertir en WebP via sharp (hook filter media.before_save).
uploads/* à la main sans aussi mettre à jour la table SQLite associée — sinon les références côté pages/blog/cocon pointeront dans le vide.
Previews cocon : brouillons/
Dossier de travail temporaire pour les previews iframe de pages cocon non encore publiées. Le frontend (Cocon) y dépose le HTML rendu pour le visualiser avant validation. C'est un cache : tout peut être vidé sans perte de données métier (le contenu source vit en SQLite).
Extensions : extensions/installed/
Une extension = un sous-dossier dont le nom est l'ID unique de l'extension. Chaque dossier contient au minimum :
extension.json— manifest (id, version, hooks, permissions, sidebar, settings_schema)index.js— point d'entrée chargé par le loader au boot- (optionnel)
package.json+node_modules/— dépendances npm propres à l'extension, installées automatiquement par le loader - (optionnel)
views/,assets/,migrations/
Trois extensions sont livrées de base :
| ID | Rôle | Versionnée ? |
|---|---|---|
hello-world | Démo interne : ping endpoint + compteur de builds | Oui (référence) |
webp-auto-converter | Convertit auto les uploads en WebP via sharp | Non |
surefeedback-connector | Injecte un snippet HTML (SureFeedback, BugHerd…) avant </body> | Non |
Seul hello-world est commit (rôle de référence pour développeurs). Toutes les autres extensions installées sont ignorées via .gitignore : extensions/installed/* sauf !extensions/installed/hello-world/.
Templates : templates/
Bibliothèques HTML utilisées comme modèles pour générer pages, articles et cocon. Cinq sous-dossiers, deux logiques de versionnement.
| Dossier | Contenu | Commit ? |
|---|---|---|
templates/library/ | Templates pages prédéfinis distribués avec le CMS (catalogue d'exemples réutilisables) | Oui |
templates/layouts/ | Layouts globaux (header + footer + structure) sélectionnables par l'admin | Oui |
templates/pages/<uuid>/ | Templates HTML uploadés par utilisateur, un dossier par page (UUID) | Non |
templates/articles/<uuid>/ | Templates HTML d'articles blog, un dossier par article | Non |
templates/cocon/<uuid>/ | Templates de content groups (cocon SEO), un dossier par groupe | Non |
Sauvegardes : backups/
Stockage local des snapshots ZIP générés par :
- Les exports manuels (
GET /api/backup/export) — ZIP complet contenant DB, uploads, extensions, templates. - Les safety backups automatiques créés avant toute restauration (
POST /api/backup/restore), pour pouvoir revenir en arrière en cas d'erreur.
Listable via GET /api/backup/list. Accès réservé au rôle owner + capability settings_security.
Documentation : documentation/
La doc HTML statique que vous lisez actuellement. Deux espaces :
documentation/tech/— documentation technique (cette page, Architecture, Base de données, API REST…).documentation/user/— guide utilisateur final.documentation/assets/— CSS, JS, images partagés.documentation/_template.html— squelette partagé par toutes les pages doc.
Fichiers sensibles à ne JAMAIS modifier manuellement
content/cms.db(etcms.db-wal,cms.db-shm) — base SQLite en mode WAL.uploads/*— médias référencés en DB ; supprimer un fichier brise les liens des pages/articles.public/*— sortie de build, écrasée à chaquePOST /api/build.brouillons/*— cache de previews, géré par l'app.backups/*— manipuler via l'API uniquement.templates/pages|articles|cocon/<uuid>/*— modifier via le builder ou les endpointsPUT /api/{pages,blog,cocon}/:id/template.extensions/installed/<id>/— installer/désinstaller viaPOST /api/extensions.
Matrice : à committer vs à ignorer
Le .gitignore trace une frontière nette entre code source (versionné, partagé entre clients) et données runtime (propres à chaque instance).
| Chemin | Statut Git | Raison |
|---|---|---|
backend/src/** | Committé | Code source |
backend/.env.example | Committé | Modèle de config, sans secrets |
frontend/src/** | Committé | Code source |
frontend/dist/** | Committé | Build CI temporairement absent, on évite de builder sur Plesk |
templates/library/** | Committé | Catalogue commun à tous les clients |
templates/layouts/** | Committé | Layouts globaux partagés |
extensions/installed/hello-world/** | Committé | Extension de référence (démo + tests) |
documentation/** | Committé | Documentation du produit |
backend/.env | Ignoré | Secrets propres à l'instance |
content/cms.db* | Ignoré | Données utilisateur |
uploads/** | Ignoré | Médias utilisateur |
public/** | Ignoré | Sortie de build regénérable |
brouillons/** | Ignoré | Cache previews |
backups/** | Ignoré | Snapshots locaux, volumineux |
templates/pages|articles|cocon/** | Ignoré | Templates utilisateur (par UUID) |
extensions/installed/** | Ignoré | (sauf hello-world) installées via UI |
node_modules/** | Ignoré | Recompilation native obligatoire par OS (better-sqlite3) |
git pull sur Plesk ne touche jamais aux données du client (DB, uploads, templates utilisateur, extensions installées). C'est ce qui rend les mises à jour par tag Git sûres dans le modèle SaaS hybride.
Paths runtime : centralisation
Tous les chemins disque sont centralisés dans backend/src/config/paths.js (ou backend/src/utils/paths.js). Aucune route ou service ne doit utiliser de chemin en dur — passer toujours par les helpers :
import { paths } from '../config/paths.js';
paths.content // → <root>/content
paths.uploads // → <root>/uploads
paths.public // → <root>/public
paths.brouillons // → <root>/brouillons
paths.templates // → <root>/templates
paths.extensions // → <root>/extensions/installed
paths.backups // → <root>/backups
Ceci évite les bugs de chemin relatif entre dev (Windows) et prod (Linux Plesk), et permet à terme de relocaliser ces dossiers (par ex. uploads/ sur un volume monté) sans refactor.
Résumé
- Code applicatif :
backend/+frontend/— toujours commit. - Données utilisateur :
content/cms.db+uploads/+brouillons/+templates/{pages,articles,cocon}/+ extensions installées — jamais commit, jamais modifier à la main. - Artefact build :
public/— regénérable, ignoré côté Git mais c'est le Document Root Plesk. - Ressources partagées :
templates/library/,templates/layouts/,extensions/installed/hello-world/,documentation/— commit. - Configuration :
.env.examplecommit,.envignoré, en prod les vars sont dans Plesk Node.js Settings.
Pour aller plus loin : consultez Architecture technique pour comprendre comment ces dossiers s'articulent au runtime, ou Déploiement pour les implications sur Plesk.