Files
core/PROJECT.md
T
hykocx 3131df2b71 refactor: remove module system integration from admin and CLI
Removes all module-related logic from the admin dashboard, CLI database
initialization, and AdminPages component:

- Drop `initModules` call from `db init` CLI command and simplify the
  completion message to only reflect core feature tables
- Remove `getModuleDashboardStats` and module page routing from admin
  stats actions and update usage documentation accordingly
- Simplify `AdminPagesClient` to remove module page loading, lazy
  components, and module-specific props (`moduleStats`, `modulePageInfo`,
  `routeInfo`, `enabledModules`)
2026-04-14 19:26:48 -04:00

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() et apiError()

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.


Email

@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)

PDF

@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> et useToast()
  • Quatre types de notifications : success, error, warning, info
  • Disparition automatique avec durées configurables par type
  • Animation de sortie (fade-out 300ms)
  • Flag dismissible par 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 action
  • currency — 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.