/** * 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; } } }