388 lines
14 KiB
Markdown
388 lines
14 KiB
Markdown
# 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 :
|
|
|
|
```bash
|
|
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 :
|
|
|
|
```js
|
|
export async function register() {
|
|
if (process.env.NEXT_RUNTIME === 'nodejs') {
|
|
const { initializeZen } = await import('@zen/core');
|
|
await initializeZen();
|
|
}
|
|
}
|
|
```
|
|
|
|
Dans `app/layout.js` :
|
|
|
|
```jsx
|
|
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` :
|
|
|
|
```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
|
|
|
|
```bash
|
|
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`. |
|