b460ed0619
- update MODULES.md to document dual-entry pattern (main vs ./client) and explain why client entry must not import server-only code - filter client manifest to only include modules exposing a `./client` subpath export - add `moduleHasClientEntry` helper in discover.server.js to check package.json exports - update cli.js to use `moduleHasClientEntry` when rendering the client manifest - update init.js and modules/index.js to align with new client entry convention
104 lines
3.5 KiB
JavaScript
104 lines
3.5 KiB
JavaScript
/**
|
|
* Database initialization for features and modules.
|
|
*
|
|
* - Features core : auth (et tout futur core ayant un db.js).
|
|
* - Modules externes : découverts via discoverModules() ; chaque module
|
|
* exporte ses propres createTables/dropTables.
|
|
*
|
|
* Les permissions des modules (manifest.permissions) sont enregistrées AVANT
|
|
* le seed de la BD pour qu'elles soient persistées et auto-attribuées au rôle
|
|
* admin. register() n'est PAS appelé ici — les enregistrements runtime (pages,
|
|
* nav, routes) ne sont pas nécessaires pour l'init BD et tireraient des imports
|
|
* Next.js incompatibles avec le contexte CLI.
|
|
*/
|
|
|
|
import { createTables as authCreate, dropTables as authDrop } from './auth/db.js';
|
|
import { done, fail, info, step } from '@zen/core/shared/logger';
|
|
import { loadModulesForCli, validateModuleEnvVars } from '../core/modules/discover.server.js';
|
|
import { getRegisteredModules } from '../core/modules/registry.js';
|
|
import { registerPermissions } from '../core/users/permissions-registry.js';
|
|
|
|
const CORE_FEATURES = [
|
|
{ name: 'auth', createTables: authCreate, dropTables: authDrop },
|
|
];
|
|
|
|
async function loadModules() {
|
|
await loadModulesForCli();
|
|
const modules = getRegisteredModules();
|
|
validateModuleEnvVars(modules);
|
|
|
|
// Enregistre les permissions déclarées dans le manifest de chaque module.
|
|
// register() n'est PAS appelé ici : les enregistrements runtime (pages, nav,
|
|
// routes, storage) ne servent à rien dans le contexte CLI et tireraient des
|
|
// imports Next.js (next/headers, JSX) qui ne sont pas disponibles en Node.js.
|
|
for (const mod of modules) {
|
|
if (Array.isArray(mod.manifest?.permissions)) {
|
|
registerPermissions(mod.manifest.permissions);
|
|
}
|
|
}
|
|
return modules;
|
|
}
|
|
|
|
export async function initFeatures() {
|
|
const created = [];
|
|
const skipped = [];
|
|
|
|
step('Initializing feature databases...');
|
|
|
|
// Charger les modules d'abord pour que leurs permissions soient connues
|
|
// au moment du seed (et donc auto-attribuées au rôle admin).
|
|
const modules = await loadModules();
|
|
|
|
const targets = [
|
|
...CORE_FEATURES,
|
|
...modules
|
|
.filter(m => typeof m.createTables === 'function')
|
|
.map(m => ({ name: m.manifest.name, createTables: m.createTables, dropTables: m.dropTables })),
|
|
];
|
|
|
|
for (const { name, createTables } of targets) {
|
|
try {
|
|
step(`Initializing ${name}...`);
|
|
if (typeof createTables !== 'function') {
|
|
info(`${name} has no createTables function`);
|
|
continue;
|
|
}
|
|
const result = await createTables();
|
|
if (result?.created) created.push(...result.created);
|
|
if (result?.skipped) skipped.push(...result.skipped);
|
|
done(`${name} initialized`);
|
|
} catch (error) {
|
|
fail(`${name}: ${error.message}`);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
return { created, skipped };
|
|
}
|
|
|
|
export async function dropFeatures() {
|
|
const modules = await loadModules();
|
|
|
|
// Ordre de création : core, puis modules. Drop = ordre inverse pour que
|
|
// les tables modules (qui peuvent avoir des FK vers core) tombent d'abord.
|
|
const targets = [
|
|
...CORE_FEATURES,
|
|
...modules
|
|
.filter(m => typeof m.dropTables === 'function')
|
|
.map(m => ({ name: m.manifest.name, dropTables: m.dropTables })),
|
|
];
|
|
|
|
for (const { name, dropTables } of [...targets].reverse()) {
|
|
try {
|
|
if (typeof dropTables !== 'function') {
|
|
info(`${name} has no dropTables function`);
|
|
continue;
|
|
}
|
|
await dropTables();
|
|
} catch (error) {
|
|
fail(`${name}: ${error.message}`);
|
|
throw error;
|
|
}
|
|
}
|
|
}
|