81172bda94
Update all references across source files, documentation, and configuration to reflect the new package scope and name. This includes updating `.npmrc` registry config, install instructions, module examples, and all import path comments throughout the codebase.
256 lines
6.1 KiB
Markdown
256 lines
6.1 KiB
Markdown
# Créer un module externe
|
|
|
|
Un module externe est un package npm indépendant qui s'intègre dans une app qui utilise `@zen/core`. Il n'a pas besoin de modifier le code source du CMS.
|
|
|
|
---
|
|
|
|
## Convention de nommage
|
|
|
|
```
|
|
@scope/zen-nom-du-module
|
|
```
|
|
|
|
Exemples : `@zen/core-invoice`, `@zen/core-nuage`.
|
|
|
|
---
|
|
|
|
## Structure du package
|
|
|
|
```
|
|
zen-invoice/
|
|
├── index.js # Point d'entrée — exporte defineModule()
|
|
├── admin/ # Composants React pour l'admin
|
|
│ ├── InvoiceListPage.js
|
|
│ ├── InvoiceCreatePage.js
|
|
│ └── InvoiceEditPage.js
|
|
├── db.js # createTables() et dropTables()
|
|
├── actions.js # Server actions pour pages publiques
|
|
├── metadata.js # Générateurs de métadonnées SEO
|
|
├── package.json
|
|
└── .env.example
|
|
```
|
|
|
|
---
|
|
|
|
## index.js
|
|
|
|
On utilise `defineModule()` importé depuis `@zen/core/modules/define`.
|
|
|
|
```js
|
|
import { lazy } from 'react';
|
|
import { defineModule } from '@zen/core/modules/define';
|
|
|
|
import { createTables, dropTables } from './db.js';
|
|
import { getInvoiceByToken } from './actions.js';
|
|
import { generatePaymentMetadata } from './metadata.js';
|
|
|
|
const InvoiceListPage = lazy(() => import('./admin/InvoiceListPage.js'));
|
|
const InvoiceCreatePage = lazy(() => import('./admin/InvoiceCreatePage.js'));
|
|
const InvoiceEditPage = lazy(() => import('./admin/InvoiceEditPage.js'));
|
|
|
|
export default defineModule({
|
|
name: 'invoice',
|
|
displayName: 'Facturation',
|
|
version: '1.0.0',
|
|
description: 'Gestion des factures et paiements.',
|
|
|
|
dependencies: [],
|
|
envVars: ['STRIPE_SECRET_KEY', 'ZEN_INVOICE_TAX_RATE'],
|
|
|
|
// Navigation dans le panneau admin
|
|
navigation: [
|
|
{
|
|
id: 'invoice',
|
|
title: 'Facturation',
|
|
icon: 'Invoice03Icon',
|
|
items: [
|
|
{ name: 'Factures', href: '/admin/invoice/list', icon: 'Invoice03Icon' },
|
|
{ name: 'Nouvelle', href: '/admin/invoice/new', icon: 'Add01Icon' },
|
|
],
|
|
},
|
|
],
|
|
|
|
// Pages admin avec leurs composants lazy
|
|
adminPages: {
|
|
'/admin/invoice/list': InvoiceListPage,
|
|
'/admin/invoice/new': InvoiceCreatePage,
|
|
'/admin/invoice/edit': InvoiceEditPage,
|
|
},
|
|
|
|
// Résolveur pour les routes dynamiques (ex: /admin/invoice/edit/123)
|
|
pageResolver(path) {
|
|
const parts = path.split('/').filter(Boolean);
|
|
if (parts[0] !== 'admin' || parts[1] !== 'invoice') return null;
|
|
if (parts[2] === 'list') return InvoiceListPage;
|
|
if (parts[2] === 'new') return InvoiceCreatePage;
|
|
if (parts[2] === 'edit') return InvoiceEditPage;
|
|
return null;
|
|
},
|
|
|
|
publicPages: {},
|
|
publicRoutes: [
|
|
{ pattern: ':token', description: 'Page de paiement' },
|
|
{ pattern: ':token/pdf', description: 'Télécharger la facture PDF' },
|
|
],
|
|
|
|
dashboardWidgets: [],
|
|
|
|
// Base de données
|
|
db: { createTables, dropTables },
|
|
|
|
// Server actions pour les pages publiques (/zen/invoice/...)
|
|
actions: { getInvoiceByToken },
|
|
|
|
// Générateurs de métadonnées SEO
|
|
metadata: {
|
|
payment: generatePaymentMetadata,
|
|
},
|
|
|
|
// Appelé une fois au démarrage du serveur
|
|
// ctx donne accès aux services du CMS
|
|
async setup(ctx) {
|
|
const stripe = await ctx.payments.then(p => p.stripe);
|
|
// Initialiser des webhooks, vérifier la config, etc.
|
|
console.log('[invoice] Stripe prêt :', !!stripe);
|
|
},
|
|
});
|
|
```
|
|
|
|
---
|
|
|
|
## L'objet ctx dans setup()
|
|
|
|
`setup(ctx)` reçoit un objet qui donne accès aux services du CMS. Chaque propriété retourne une promesse vers le module correspondant.
|
|
|
|
```js
|
|
async setup(ctx) {
|
|
// Base de données PostgreSQL
|
|
const { query, queryOne, queryAll } = await ctx.db;
|
|
const rows = await query('SELECT * FROM zen_auth_users LIMIT 1');
|
|
|
|
// Envoi de courriels (Resend)
|
|
const { sendEmail } = await ctx.email;
|
|
await sendEmail({ to: 'test@example.com', subject: 'Test', html: '<p>ok</p>' });
|
|
|
|
// Stockage de fichiers (Cloudflare R2)
|
|
const { uploadFile, deleteFile } = await ctx.storage;
|
|
|
|
// Stripe
|
|
const { stripe } = await ctx.payments;
|
|
|
|
// Variables d'environnement
|
|
const apiKey = ctx.config.get('STRIPE_SECRET_KEY');
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## package.json du module externe
|
|
|
|
```json
|
|
{
|
|
"name": "@zen/core-invoice",
|
|
"version": "1.0.0",
|
|
"type": "module",
|
|
"main": "./index.js",
|
|
"exports": {
|
|
".": "./index.js"
|
|
},
|
|
"peerDependencies": {
|
|
"@zen/core": ">=1.0.0",
|
|
"react": ">=19.0.0"
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Intégration dans l'app consommatrice
|
|
|
|
### 1. Installer le package
|
|
|
|
```bash
|
|
npm install @zen/core-invoice
|
|
```
|
|
|
|
### 2. Créer zen.config.js à la racine de l'app
|
|
|
|
```js
|
|
// zen.config.js
|
|
import invoiceModule from '@zen/core-invoice';
|
|
|
|
export default {
|
|
modules: [invoiceModule],
|
|
};
|
|
```
|
|
|
|
### 3. Passer la config à initializeZen()
|
|
|
|
```js
|
|
// instrumentation.js
|
|
import zenConfig from './zen.config.js';
|
|
|
|
export async function register() {
|
|
if (process.env.NEXT_RUNTIME === 'nodejs') {
|
|
const { initializeZen } = await import('@zen/core');
|
|
await initializeZen(zenConfig);
|
|
}
|
|
}
|
|
```
|
|
|
|
### 4. Passer les modules à ZenProvider
|
|
|
|
```jsx
|
|
// app/layout.js
|
|
import zenConfig from './zen.config.js';
|
|
import { ZenProvider } from '@zen/core/provider';
|
|
|
|
export default function RootLayout({ children }) {
|
|
return (
|
|
<html>
|
|
<body>
|
|
<ZenProvider modules={zenConfig.modules}>
|
|
{children}
|
|
</ZenProvider>
|
|
</body>
|
|
</html>
|
|
);
|
|
}
|
|
```
|
|
|
|
### 5. Activer le module via variable d'environnement
|
|
|
|
```bash
|
|
# .env.local
|
|
ZEN_MODULE_INVOICE=true
|
|
```
|
|
|
|
La convention est la même que pour les modules internes : tirets en underscores, tout en majuscules.
|
|
|
|
---
|
|
|
|
## Initialiser la base de données
|
|
|
|
Le module déclare ses tables dans `db.createTables`. Deux façons de les créer.
|
|
|
|
**Au démarrage du serveur** (si `skipDb: false`) :
|
|
|
|
```js
|
|
await initializeZen({ modules: zenConfig.modules, skipDb: false });
|
|
```
|
|
|
|
**Via la CLI** :
|
|
|
|
```bash
|
|
npx zen-db init
|
|
```
|
|
|
|
---
|
|
|
|
## Vérification rapide
|
|
|
|
1. Démarrer avec `ZEN_MODULE_INVOICE=true`.
|
|
2. Ouvrir `/admin`. La section "Facturation" doit apparaître dans la navigation.
|
|
3. Naviguer vers `/admin/invoice/list`. La page du module doit se charger.
|
|
4. Appeler `getModuleActions('invoice')` côté serveur. Les actions du module doivent être retournées.
|