feat(storage): refactor storage config and remove module registry
Introduce a dedicated `storage-config.js` for registering public prefixes and access policies via `configureStorageApi()`, replacing the previous `getAllStoragePublicPrefixes` / `getAllStorageAccessPolicies` imports from the module registry. Remove `getAllApiRoutes()` from the router so module-level routes are no longer auto-collected; feature routes must now be registered explicitly via `registerFeatureRoutes()` during `initializeZen()`. Update `.env.example` to document separate `ZEN_STORAGE_PROVIDER`, `ZEN_STORAGE_B2_*` variables for Backblaze B2 alongside the existing Cloudflare R2 variables, making provider selection explicit. Clean up admin navigation and page components to drop module-injected nav entries, keeping only core and system sections.
This commit is contained in:
+15
-102
@@ -1,136 +1,49 @@
|
||||
/**
|
||||
* ZEN Initialization
|
||||
* Initialize all ZEN services and modules using dynamic module discovery
|
||||
*/
|
||||
|
||||
import { discoverModules, registerExternalModules, startModuleCronJobs, stopModuleCronJobs } from '../../core/modules/index.js';
|
||||
import { configureRouter, registerFeatureRoutes, clearFeatureRoutes, clearRouterConfig } from '../../core/api/index.js';
|
||||
import { validateSession } from '../../features/auth/lib/session.js';
|
||||
import { routes as authRoutes } from '../../features/auth/api.js';
|
||||
import { step, done, warn, fail } from './logger.js';
|
||||
|
||||
// Use globalThis to persist initialization flag across module reloads
|
||||
const ZEN_INIT_KEY = Symbol.for('__ZEN_INITIALIZED__');
|
||||
|
||||
/**
|
||||
* Initialize ZEN system
|
||||
* Discovers modules dynamically and starts cron jobs
|
||||
*
|
||||
* Recommended: Use instrumentation.js for automatic initialization
|
||||
* Alternative: Call this function manually in your root layout
|
||||
* Wires core feature dependencies into the API router.
|
||||
* This is the composition root — the only place that connects features to core.
|
||||
*
|
||||
* @example
|
||||
* // instrumentation.js (Recommended) — internal modules only
|
||||
* // instrumentation.js
|
||||
* export async function register() {
|
||||
* if (process.env.NEXT_RUNTIME === 'nodejs') {
|
||||
* const { initializeZen } = await import('@zen/core');
|
||||
* await initializeZen();
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* @example
|
||||
* // instrumentation.js — with external modules from zen.config.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);
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* @param {Object} config - Configuration object
|
||||
* @param {Array} config.modules - External module configs (from zen.config.js)
|
||||
* @param {boolean} config.skipCron - Skip cron job initialization
|
||||
* @param {boolean} config.skipDb - Skip database initialization
|
||||
* @returns {Promise<Object>} Initialization result
|
||||
*/
|
||||
export async function initializeZen(config = {}) {
|
||||
const { modules: externalModules = [], skipCron = false, skipDb = true } = config;
|
||||
|
||||
// Only run on server-side
|
||||
import { configureRouter, registerFeatureRoutes, clearRouterConfig, clearFeatureRoutes } from '../../core/api/index.js';
|
||||
import { validateSession } from '../../features/auth/lib/session.js';
|
||||
import { routes as authRoutes } from '../../features/auth/api.js';
|
||||
import { done, warn } from './logger.js';
|
||||
|
||||
const ZEN_INIT_KEY = Symbol.for('__ZEN_INITIALIZED__');
|
||||
|
||||
export async function initializeZen() {
|
||||
if (typeof window !== 'undefined') {
|
||||
return { skipped: true, reason: 'client-side' };
|
||||
}
|
||||
|
||||
// Prevent multiple initializations using globalThis
|
||||
if (globalThis[ZEN_INIT_KEY]) {
|
||||
warn('ZEN: already initialized, skipping');
|
||||
return { skipped: true, reason: 'already-initialized' };
|
||||
}
|
||||
|
||||
globalThis[ZEN_INIT_KEY] = true;
|
||||
step('ZEN starting...');
|
||||
|
||||
const result = {
|
||||
discovery: null,
|
||||
cron: { started: [], errors: [] }
|
||||
};
|
||||
configureRouter({ resolveSession: validateSession });
|
||||
registerFeatureRoutes(authRoutes);
|
||||
|
||||
try {
|
||||
// Step 1: Wire core feature dependencies into the API router.
|
||||
// init.js is the composition root — the only place that wires features
|
||||
// into core, keeping core/api/ free of static feature-level imports.
|
||||
configureRouter({ resolveSession: validateSession });
|
||||
registerFeatureRoutes(authRoutes);
|
||||
done('ZEN: ready');
|
||||
|
||||
// Step 2: Discover and register internal modules (from modules.registry.js)
|
||||
result.discovery = await discoverModules();
|
||||
|
||||
const enabledCount = result.discovery.enabled?.length || 0;
|
||||
const skippedCount = result.discovery.skipped?.length || 0;
|
||||
|
||||
if (enabledCount > 0) {
|
||||
done(`ZEN: ${enabledCount} module(s): ${result.discovery.enabled.join(', ')}`);
|
||||
}
|
||||
if (skippedCount > 0) {
|
||||
warn(`ZEN: skipped ${skippedCount} module(s): ${result.discovery.skipped.join(', ')}`);
|
||||
}
|
||||
|
||||
// Step 3: Register external modules from zen.config.js (if any)
|
||||
if (externalModules.length > 0) {
|
||||
result.external = await registerExternalModules(externalModules);
|
||||
|
||||
if (result.external.registered.length > 0) {
|
||||
done(`ZEN: ${result.external.registered.length} external module(s): ${result.external.registered.join(', ')}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Step 4: Start cron jobs for all enabled modules (internal + external)
|
||||
if (!skipCron) {
|
||||
result.cron = await startModuleCronJobs();
|
||||
|
||||
if (result.cron.started.length > 0) {
|
||||
done(`ZEN: ${result.cron.started.length} cron job(s): ${result.cron.started.join(', ')}`);
|
||||
}
|
||||
}
|
||||
|
||||
done('ZEN: ready');
|
||||
|
||||
} catch (error) {
|
||||
fail(`ZEN: init failed: ${error.message}`);
|
||||
result.error = error.message;
|
||||
}
|
||||
|
||||
return result;
|
||||
return {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset initialization flag (useful for testing or manual reinitialization)
|
||||
* @returns {void}
|
||||
*/
|
||||
export function resetZenInitialization() {
|
||||
globalThis[ZEN_INIT_KEY] = false;
|
||||
|
||||
// Stop all cron jobs using the module system
|
||||
try {
|
||||
stopModuleCronJobs();
|
||||
} catch (e) {
|
||||
// Cron system not available
|
||||
}
|
||||
|
||||
// Clear router config and feature routes so they are re-registered on next initializeZen()
|
||||
clearRouterConfig();
|
||||
clearFeatureRoutes();
|
||||
|
||||
warn('ZEN: initialization reset');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user