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.
222 lines
5.2 KiB
Markdown
222 lines
5.2 KiB
Markdown
# Créer un module interne
|
|
|
|
Un module interne vit dans `src/modules/` et fait partie du package `@zen/core`. Il a accès direct aux services du CMS sans passer par un contexte injecté.
|
|
|
|
---
|
|
|
|
## Structure d'un module
|
|
|
|
```
|
|
src/modules/mon-module/
|
|
├── module.config.js # Obligatoire — navigation, pages, routes, cron, etc.
|
|
├── db.js # Optionnel — createTables() et dropTables()
|
|
├── api.js # Optionnel — routes REST
|
|
├── cron.config.js # Optionnel — tâches planifiées
|
|
├── crud.js # Optionnel — accès aux données
|
|
├── admin/ # Composants React pour l'admin
|
|
└── .env.example # Variables d'environnement requises
|
|
```
|
|
|
|
---
|
|
|
|
## module.config.js
|
|
|
|
C'est la source de vérité du module. On utilise `defineModule()` pour déclarer la configuration.
|
|
|
|
```js
|
|
import { lazy } from 'react';
|
|
import { defineModule } from '../../core/modules/defineModule.js';
|
|
|
|
const ListPage = lazy(() => import('./admin/ListPage.js'));
|
|
const CreatePage = lazy(() => import('./admin/CreatePage.js'));
|
|
const EditPage = lazy(() => import('./admin/EditPage.js'));
|
|
|
|
export default defineModule({
|
|
name: 'mon-module',
|
|
displayName: 'Mon module',
|
|
version: '1.0.0',
|
|
description: 'Description courte.',
|
|
|
|
// Modules dont celui-ci dépend (vérification au démarrage)
|
|
dependencies: [],
|
|
|
|
// Variables d'environnement que ce module lit
|
|
envVars: ['ZEN_MON_MODULE_OPTION'],
|
|
|
|
// Navigation admin — un ou plusieurs objets de section
|
|
navigation: [
|
|
{
|
|
id: 'mon-module',
|
|
title: 'Mon module',
|
|
icon: 'SomeIcon',
|
|
items: [
|
|
{ name: 'Liste', href: '/admin/mon-module/list', icon: 'SomeIcon' },
|
|
{ name: 'Nouveau', href: '/admin/mon-module/new', icon: 'AddIcon' },
|
|
],
|
|
},
|
|
],
|
|
|
|
// Pages admin — chemin exact vers composant lazy
|
|
adminPages: {
|
|
'/admin/mon-module/list': ListPage,
|
|
'/admin/mon-module/new': CreatePage,
|
|
'/admin/mon-module/edit': EditPage,
|
|
},
|
|
|
|
// Résolveur pour les routes dynamiques (non connues à la compilation)
|
|
// Retourner null si le chemin ne correspond pas
|
|
pageResolver(path) {
|
|
const parts = path.split('/').filter(Boolean);
|
|
if (parts[0] !== 'admin' || parts[1] !== 'mon-module') return null;
|
|
if (parts[2] === 'list') return ListPage;
|
|
if (parts[2] === 'new') return CreatePage;
|
|
if (parts[2] === 'edit') return EditPage;
|
|
return null;
|
|
},
|
|
|
|
// Pages publiques accessibles sans authentification (/zen/mon-module/...)
|
|
publicPages: {},
|
|
publicRoutes: [],
|
|
|
|
// Widgets affichés sur le dashboard admin
|
|
dashboardWidgets: [],
|
|
});
|
|
```
|
|
|
|
---
|
|
|
|
## db.js
|
|
|
|
Si le module crée des tables, on exporte `createTables` et `dropTables`.
|
|
|
|
```js
|
|
import { query } from '../../core/database/index.js';
|
|
import { tableExists } from '../../core/database/helpers.js';
|
|
|
|
export async function createTables() {
|
|
const created = [];
|
|
const skipped = [];
|
|
|
|
if (!(await tableExists('zen_mon_module'))) {
|
|
await query(`
|
|
CREATE TABLE zen_mon_module (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
titre TEXT NOT NULL,
|
|
created_at TIMESTAMPTZ DEFAULT NOW()
|
|
)
|
|
`);
|
|
created.push('zen_mon_module');
|
|
} else {
|
|
skipped.push('zen_mon_module');
|
|
}
|
|
|
|
return { created, skipped };
|
|
}
|
|
|
|
export async function dropTables() {
|
|
await query('DROP TABLE IF EXISTS zen_mon_module CASCADE');
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## api.js
|
|
|
|
Les routes API sont montées automatiquement sous `/api/zen/mon-module/...`.
|
|
|
|
```js
|
|
async function handleList(request) {
|
|
// ...
|
|
return Response.json({ items });
|
|
}
|
|
|
|
export default {
|
|
routes: [
|
|
{
|
|
path: 'mon-module/list',
|
|
method: 'GET',
|
|
handler: handleList,
|
|
requireAuth: true,
|
|
requireAdmin: false,
|
|
},
|
|
],
|
|
};
|
|
```
|
|
|
|
---
|
|
|
|
## cron.config.js
|
|
|
|
```js
|
|
import { maFonction } from './crud.js';
|
|
|
|
export default {
|
|
jobs: [
|
|
{
|
|
name: 'mon-module:nettoyage',
|
|
schedule: '0 3 * * *', // Tous les jours à 3h
|
|
handler: maFonction,
|
|
timezone: 'America/Toronto',
|
|
},
|
|
],
|
|
};
|
|
```
|
|
|
|
---
|
|
|
|
## Enregistrement — 2 étapes
|
|
|
|
Après avoir créé les fichiers, on enregistre le module dans deux endroits.
|
|
|
|
### 1. `src/modules/modules.registry.js`
|
|
|
|
Ajouter le nom du module à `AVAILABLE_MODULES` :
|
|
|
|
```js
|
|
export const AVAILABLE_MODULES = [
|
|
'posts',
|
|
'mon-module', // ajout
|
|
];
|
|
```
|
|
|
|
### 2. `src/modules/modules.pages.js`
|
|
|
|
Importer la config et l'ajouter à `MODULE_CONFIGS` :
|
|
|
|
```js
|
|
import postsConfig from './posts/module.config.js';
|
|
import monModuleConfig from './mon-module/module.config.js'; // ajout
|
|
|
|
const MODULE_CONFIGS = {
|
|
posts: postsConfig,
|
|
'mon-module': monModuleConfig, // ajout
|
|
};
|
|
```
|
|
|
|
---
|
|
|
|
## Activation
|
|
|
|
Le module est ignoré au démarrage tant que sa variable d'environnement n'est pas définie :
|
|
|
|
```bash
|
|
# .env.local
|
|
ZEN_MODULE_MON_MODULE=true
|
|
```
|
|
|
|
La règle de conversion : tirets et espaces deviennent des underscores, tout en majuscules.
|
|
|
|
```
|
|
mon-module → ZEN_MODULE_MON_MODULE
|
|
post-types → ZEN_MODULE_POST_TYPES
|
|
```
|
|
|
|
---
|
|
|
|
## Vérification rapide
|
|
|
|
1. Démarrer le serveur avec `ZEN_MODULE_MON_MODULE=true`.
|
|
2. Ouvrir `/admin`. La section de navigation du module doit apparaître.
|
|
3. Naviguer vers `/admin/mon-module/list`. La page doit se charger.
|
|
4. Lancer `npx zen-db init`. La table `zen_mon_module` doit être créée.
|