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:
2026-04-13 16:35:23 -04:00
parent 6521179e10
commit a3921a0b98
11 changed files with 691 additions and 295 deletions
+65
View File
@@ -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;
}
}
}