feat(database): refactor CLI, add column whitelist, and SSL config
- Add `ZEN_DB_SSL_DISABLED` env variable to allow disabling SSL for database connections - Refactor database CLI to split init logic into `initFeatures` and `initModules` for modular table initialization, with graceful fallback when modules are absent - Extract `printHelp` and `askConfirmation` helpers for cleaner CLI structure - Ensure `closePool` is called on both success and error paths in CLI - Add `filterAllowedColumns` utility in `crud.js` to enforce column whitelists, preventing mass-assignment of privileged fields (e.g. `role`, `email_verified`) - Update drop command description from "auth tables" to "all tables"
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* Core Feature Database Initialization (CLI)
|
||||
*
|
||||
* Initializes and drops DB tables for each core feature.
|
||||
* Features are discovered from CORE_FEATURES — no manual wiring needed
|
||||
* when adding a new feature.
|
||||
*/
|
||||
|
||||
import { CORE_FEATURES } from './features.registry.js';
|
||||
import { done, fail, info, step } from '../shared/lib/logger.js';
|
||||
|
||||
/**
|
||||
* Initialize all core feature databases.
|
||||
* @returns {Promise<{ created: string[], skipped: string[] }>}
|
||||
*/
|
||||
export async function initFeatures() {
|
||||
const created = [];
|
||||
const skipped = [];
|
||||
|
||||
step('Initializing feature databases...');
|
||||
|
||||
for (const featureName of CORE_FEATURES) {
|
||||
try {
|
||||
step(`Initializing ${featureName}...`);
|
||||
const db = await import(`./${featureName}/db.js`);
|
||||
|
||||
if (typeof db.createTables === 'function') {
|
||||
const result = await db.createTables();
|
||||
|
||||
if (result?.created) created.push(...result.created);
|
||||
if (result?.skipped) skipped.push(...result.skipped);
|
||||
|
||||
done(`${featureName} initialized`);
|
||||
} else {
|
||||
info(`${featureName} has no createTables function`);
|
||||
}
|
||||
} catch (error) {
|
||||
fail(`${featureName}: ${error.message}`);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
return { created, skipped };
|
||||
}
|
||||
|
||||
/**
|
||||
* Drop all core feature databases in reverse order.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
export async function dropFeatures() {
|
||||
for (const featureName of [...CORE_FEATURES].reverse()) {
|
||||
try {
|
||||
const db = await import(`./${featureName}/db.js`);
|
||||
|
||||
if (typeof db.dropTables === 'function') {
|
||||
await db.dropTables();
|
||||
} else {
|
||||
info(`${featureName} has no dropTables function`);
|
||||
}
|
||||
} catch (error) {
|
||||
fail(`${featureName}: ${error.message}`);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user