14 KiB
ZEN — Plan du projet
Un CMS Next.js construit sur l'essentiel, rien de plus, rien de moins.
ZEN est un système de gestion de contenu (CMS) pour Next.js. Il s'installe automatiquement dans n'importe quel projet Next.js via :
npx @zen/start
Le package principal est @zen/core. Il fournit toute l'infrastructure nécessaire pour gérer un site : authentification, base de données, stockage, courriels, paiements, PDF, tâches planifiées, notifications. Chaque fonctionnalité est indépendante — les cores ne se connaissent pas entre eux, mais le reste du CMS s'appuie sur chacun d'eux.
Principes directeurs
Core purity — Chaque core contient uniquement du code propre à son domaine. Aucune logique métier, aucune dépendance vers un autre core.
Minimal by default — Pas de fonctionnalité superflue. Si ce n'est pas nécessaire, ça n'existe pas.
Sécuritaire par défaut — Requêtes paramétrées, protection CSRF, limitation de débit, validation en entrée, erreurs opaques vers le client.
Performant — Connexions en pool, cache HTTP, génération différée des services.
Structure du projet
src/
core/ # Infrastructure fondamentale — la base de tout
features/ # Fonctionnalités du CMS utilisant les cores
shared/ # Utilitaires, composants et styles partagés
src/core — Les piliers du CMS
Chaque core est une brique indépendante. Il n'existe qu'une seule façon de faire chaque chose dans ZEN : passer par le core concerné. L'ensemble du CMS, des features aux modules, repose sur ces cores.
API
@zen/core/api
L'API est le point d'entrée unique de toutes les requêtes HTTP du CMS. Que ce soit depuis l'Admin, le front-end du site ou un module tiers, tout passe par là. Il n'existe aucune autre route API dans ZEN.
Toutes les requêtes arrivent via le catch-all Next.js app/zen/api/[...path]/route.js. Le router les dispatche vers le bon handler selon le chemin et la méthode HTTP.
Ce qu'il fait :
- Routage dynamique avec paramètres nommés (
:id) et wildcards (/**) - Protection CSRF automatique sur toutes les requêtes mutantes (POST, PUT, PATCH, DELETE)
- Limitation de débit par IP avec des préréglages par action (login, register, api)
- Injection de session dans le contexte de chaque handler
- Trois niveaux d'accès :
public,user(session requise),admin(rôle admin requis) - Enregistrement dynamique des routes par les features
- Réponses standardisées via
apiSuccess()etapiError()
Règle absolue : Toute route API du CMS doit être enregistrée dans ce router. Jamais de route.js parallèle.
Database
@zen/core/database
La couche d'accès à la base de données PostgreSQL. Toute l'application communique avec la base de données uniquement via ce core — jamais directement.
Ce qu'il fait :
- Pool de connexions PostgreSQL (max 20 clients, reconnexion automatique)
- Fonctions de requête de bas niveau :
query(),queryOne(),queryAll(),transaction() - Helpers CRUD complets :
create(),find(),findOne(),findById(),update(),updateById(),delete(),deleteWhere(),count(),exists() - Protection contre l'injection SQL : requêtes paramétrées
$1, $2, ..., jamais de concaténation - Contrôle d'accès aux colonnes via liste blanche (
allowedColumns) — empêche le mass assignment - Validation et échappement des identifiants SQL (noms de tables et colonnes)
- Gestion SSL configurable : vérification complète en production, souple en développement
- Erreurs opaques : seul le code SQLSTATE est exposé, jamais les détails internes
Règle absolue : Aucun code ailleurs dans le CMS ne communique directement avec la base de données. Tout passe par ce core.
@zen/core/email
L'unique système d'envoi de courriels du CMS. Alimenté par Resend. Toute l'application envoie ses courriels via ce core.
Ce qu'il fait :
- Envoi de courriels simples :
sendEmail({ to, subject, html }) - Envoi en lot :
sendBatchEmails(emailArray) - Expéditeur configurable (adresse, nom d'affichage)
- Template de base React Email (
BaseLayout) : logo, marque, pied de page, lien de support - Réponses standardisées
{ success, data, error }
Templates fournis :
BaseLayout— Enveloppe visuelle commune à tous les courriels du CMS (logo, couleurs, pied de page)
Règle absolue : Tout courriel envoyé par le CMS passe par ce core.
Cron
@zen/core/cron
Le registre central de toutes les tâches planifiées. Chaque tâche récurrente de l'application s'enregistre ici — jamais en dehors.
Ce qu'il fait :
- Planification de tâches avec expressions cron standard (
schedule(name, expression, handler)) - Support des fuseaux horaires (défaut :
ZEN_TIMEZONE) - Survie aux hot reloads Next.js via stockage global (
Symbol.for) - Remplacement automatique si une tâche du même nom est enregistrée deux fois
- Déclenchement manuel :
trigger(name)pour exécuter une tâche immédiatement - Introspection :
getJobs(),getStatus(),isRunning(name) - Gestion des erreurs : un échec de tâche ne plante pas le scheduler
Règle absolue : Toutes les tâches cron de l'application sont enregistrées via ce core.
Storage
@zen/core/storage
La gestion complète du stockage de fichiers, compatible S3 (Cloudflare R2 ou Backblaze B2). Toute l'application lit et écrit des fichiers via ce core.
Ce qu'il fait :
- Upload, téléchargement, suppression, copie et déplacement de fichiers
- Upload d'images avec cache longue durée (
max-age=31536000) - Suppression en lot optimisée (S3 batch delete)
- URLs pré-signées pour accès direct (GET ou PUT)
- Liste paginée des fichiers avec préfixe et continuation token
- Signature AWS Signature V4 pour toutes les requêtes
- Protection contre le path traversal (
..,., segments vides, null bytes) - Contrôle d'accès via policies : préfixes publics vs chemins protégés (session + rôle)
- Headers de sécurité :
X-Content-Type-Options: nosniff,X-Frame-Options: DENY - Téléchargement forcé pour les fichiers non-images (prévient l'exécution en navigateur)
- Utilitaires : validation de type, validation de taille, nommage unique, extension, MIME type
Règle absolue : Tout accès fichier passe par ce core.
Payments
@zen/core/payments
L'intégration Stripe pour les paiements. Tout ce qui touche à la facturation ou aux transactions dans l'application passe par ce core.
Ce qu'il fait :
- Sessions de checkout Stripe (
createCheckoutSession) - PaymentIntents pour paiements personnalisés (
createPaymentIntent) - Gestion des clients Stripe (
createCustomer,getOrCreateCustomer) - Récupération des sessions et intentions de paiement
- Listing des moyens de paiement d'un client
- Vérification des webhooks Stripe (
verifyWebhookSignature) - Remboursements (
createRefund) - Initialisation paresseuse du client Stripe (seulement si configuré)
- Clé publiable exposée pour le front-end (
getPublishableKey)
@zen/core/pdf
La génération de fichiers PDF à partir de composants React. Tout PDF produit par l'application passe par ce core.
Ce qu'il fait :
- Rendu de documents PDF depuis des composants React (
renderToBuffer) - Réexporte l'API complète de
@react-pdf/renderer:Document,Page,View,Text,Image,Link,StyleSheet,Font - Utilitaire de nommage :
getFilename(prefix, identifier, date?)→"invoice-12345-2024-01-15.pdf"
Toast
@zen/core/toast
Le système de notifications visuelles de l'application. Que ce soit dans l'Admin ou sur le front-end, c'est l'unique système de toast à utiliser.
Ce qu'il fait :
- Contexte React via
<ToastProvider>etuseToast() - Quatre types de notifications :
success,error,warning,info - Disparition automatique avec durées configurables par type
- Animation de sortie (fade-out 300ms)
- Flag
dismissiblepar notification <ToastContainer>pour le rendu dans l'arbre React
Règle absolue : Un seul système de toast dans toute l'application — celui-ci.
src/features — Les fonctionnalités du CMS
Les features utilisent les cores pour implémenter les fonctionnalités centrales du CMS. Elles ont accès à l'API, à la base de données, aux courriels, etc.
Auth
@zen/core/auth
Le système d'authentification du CMS. Toute authentification d'utilisateur dans le site passe par ici.
Ce qu'il fait :
- Inscription et connexion d'utilisateurs
- Hachage des mots de passe avec
scrypt(natif Node.js) + sel aléatoire + comparaison résistante aux timing attacks - Gestion de sessions : création, validation, suppression, rafraîchissement automatique (<20 jours → 30 jours)
- Vérification d'adresse courriel par token
- Réinitialisation de mot de passe par lien sécurisé
- Middleware de protection de routes :
protect(),checkAuth(),requireRole() - Server Actions :
loginAction,registerAction,logoutAction,forgotPasswordAction,resetPasswordAction,verifyEmailAction - Tables gérées :
zen_auth_users,zen_auth_sessions,zen_auth_email_verifications,zen_auth_password_resets
Règle absolue : Toute authentification de site passe par cette feature.
Admin
@zen/core/admin
L'interface d'administration centrale. Tableau de bord visuel pour gérer le site et ses modules.
Ce qu'il fait :
- Protection des routes admin :
protectAdmin(),isAdmin() - Pages catch-all pour l'interface admin (
AdminPagesClient,AdminPagesLayout) - Navigation construite côté serveur (
buildNavigationSections) - Gestion des utilisateurs depuis l'interface
Navigation :
- Tableau de bord →
/admin/dashboard - Utilisateurs →
/admin/users
Provider
@zen/core/provider
Le provider React racine du CMS. Il s'insère dans le layout du site et active tout ce dont le CMS a besoin côté client.
Ce qu'il fait :
- Enveloppe l'application dans
<ToastProvider>avec son<ToastContainer> - Un seul composant à poser dans le layout :
<ZenProvider>
src/shared — Utilitaires partagés
Tout ce qui est utile à travers le CMS sans appartenir à un core ou une feature.
Composants UI (src/shared/components/)
Bibliothèque de composants React stylisés, utilisés dans l'Admin et les pages du CMS :
Badge, StatusBadge, TypeBadge, Button, Card, Input, Loading, LoadingState, Modal, Pagination, Select, StatCard, Table, Textarea, MarkdownEditor, PasswordStrengthIndicator, FilterTabs, Breadcrumb
Utilitaires (src/shared/lib/, src/shared/utils/)
appConfig— Lecture centralisée de la configuration (getAppName,getAppConfig,getPublicBaseUrl)logger— Console stylisée pour les logs (step,done,warn,fail,info)dates— Manipulation de dates en UTC (formatDateForDisplay,getDaysBetween,isOverdue, etc.)metadata— Génération de métadonnées Next.js (generateMetadata,generateTitle,generateRobots)rateLimit— Limitation de débit partagée (checkRateLimit) avec préréglages par actioncurrency— Formatage monétaire (formatCurrency,getCurrencySymbol)
Icons (src/shared/Icons.js)
Bibliothèque de plus de 1000 icônes (style Untitled UI).
Styles (src/shared/styles/zen.css)
Feuille de style CSS de base du CMS.
Initialisation
Dans instrumentation.js du projet Next.js :
export async function register() {
if (process.env.NEXT_RUNTIME === 'nodejs') {
const { initializeZen } = await import('@zen/core');
await initializeZen();
}
}
Dans app/layout.js :
import { ZenProvider } from '@zen/core/provider';
export default function RootLayout({ children }) {
return (
<html>
<body>
<ZenProvider>
{children}
</ZenProvider>
</body>
</html>
);
}
Dans app/zen/api/[...path]/route.js :
export { GET, POST, PUT, PATCH, DELETE } from '@zen/core/zen/api';
Variables d'environnement
| Variable | Description |
|---|---|
ZEN_NAME |
Nom de l'application |
ZEN_TIMEZONE |
Fuseau horaire IANA (défaut : America/Toronto) |
ZEN_CURRENCY |
Code monétaire (défaut : CAD) |
ZEN_CURRENCY_SYMBOL |
Symbole monétaire (défaut : $) |
NEXT_PUBLIC_URL |
URL de base (production) |
NEXT_PUBLIC_URL_DEV |
URL de base (développement) |
ZEN_DATABASE_URL |
Chaîne de connexion PostgreSQL (production) |
ZEN_DATABASE_URL_DEV |
Chaîne de connexion PostgreSQL (développement) |
ZEN_DB_SSL_DISABLED |
Désactiver TLS (local uniquement) |
ZEN_EMAIL_RESEND_APIKEY |
Clé API Resend |
ZEN_EMAIL_FROM_ADDRESS |
Adresse d'expédition |
ZEN_EMAIL_FROM_NAME |
Nom d'affichage de l'expéditeur |
ZEN_STORAGE_PROVIDER |
r2 ou backblaze |
ZEN_STORAGE_ENDPOINT |
Endpoint S3-compatible |
ZEN_STORAGE_ACCESS_KEY |
Clé d'accès stockage |
ZEN_STORAGE_SECRET_KEY |
Clé secrète stockage |
ZEN_STORAGE_BUCKET |
Nom du bucket |
STRIPE_SECRET_KEY |
Clé secrète Stripe |
STRIPE_PUBLISHABLE_KEY |
Clé publiable Stripe |
STRIPE_WEBHOOK_SECRET |
Secret webhook Stripe |
CLI base de données
npx zen-db init # Créer toutes les tables
npx zen-db test # Tester la connexion
npx zen-db drop # Supprimer toutes les tables (confirmation requise)
Flux d'une requête
Navigateur / Client
↓
app/zen/api/[...path]/route.js ← catch-all Next.js
↓
core/api — router.js ← CSRF, rate limit, auth
↓
Handler (feature) ← logique métier
↓
core/database, core/storage, ← accès aux ressources
core/email, core/payments…
↓
Réponse standardisée ← apiSuccess() / apiError()
Résumé des règles absolues
| Domaine | Règle |
|---|---|
| API | Toutes les routes HTTP passent par core/api. Aucune autre route API. |
| Base de données | Tout accès DB passe par core/database. Jamais de requêtes directes. |
| Courriels | Tout envoi de courriel passe par core/email. |
| Cron | Toutes les tâches planifiées s'enregistrent dans core/cron. |
| Stockage | Tout accès fichier passe par core/storage. |
| Notifications | Un seul système de toast dans toute l'app : core/toast. |
| Authentification | Toute auth de site passe par features/auth. |