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,117 @@
|
||||
/**
|
||||
* Auth Feature - Database
|
||||
* Creates and drops zen_auth_* tables.
|
||||
*/
|
||||
|
||||
import { query, tableExists } from '@zen/core/database';
|
||||
import { done, warn } from '../../shared/lib/logger.js';
|
||||
|
||||
const AUTH_TABLES = [
|
||||
{
|
||||
name: 'zen_auth_users',
|
||||
sql: `
|
||||
CREATE TABLE zen_auth_users (
|
||||
id text NOT NULL PRIMARY KEY,
|
||||
name text NOT NULL,
|
||||
email text NOT NULL UNIQUE,
|
||||
email_verified boolean NOT NULL DEFAULT false,
|
||||
image text,
|
||||
created_at timestamptz DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
updated_at timestamptz DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
role text DEFAULT 'user' CHECK (role IN ('admin', 'user'))
|
||||
)
|
||||
`
|
||||
},
|
||||
{
|
||||
name: 'zen_auth_sessions',
|
||||
sql: `
|
||||
CREATE TABLE zen_auth_sessions (
|
||||
id text NOT NULL PRIMARY KEY,
|
||||
expires_at timestamptz NOT NULL,
|
||||
token text NOT NULL UNIQUE,
|
||||
created_at timestamptz DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
updated_at timestamptz NOT NULL,
|
||||
ip_address text,
|
||||
user_agent text,
|
||||
user_id text NOT NULL REFERENCES zen_auth_users (id) ON DELETE CASCADE
|
||||
)
|
||||
`
|
||||
},
|
||||
{
|
||||
name: 'zen_auth_accounts',
|
||||
sql: `
|
||||
CREATE TABLE zen_auth_accounts (
|
||||
id text NOT NULL PRIMARY KEY,
|
||||
account_id text NOT NULL,
|
||||
provider_id text NOT NULL,
|
||||
user_id text NOT NULL REFERENCES zen_auth_users (id) ON DELETE CASCADE,
|
||||
access_token text,
|
||||
refresh_token text,
|
||||
id_token text,
|
||||
access_token_expires_at timestamptz,
|
||||
refresh_token_expires_at timestamptz,
|
||||
scope text,
|
||||
password text,
|
||||
created_at timestamptz DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
updated_at timestamptz NOT NULL
|
||||
)
|
||||
`
|
||||
},
|
||||
{
|
||||
name: 'zen_auth_verifications',
|
||||
sql: `
|
||||
CREATE TABLE zen_auth_verifications (
|
||||
id text NOT NULL PRIMARY KEY,
|
||||
identifier text NOT NULL,
|
||||
value text NOT NULL,
|
||||
token text NOT NULL,
|
||||
expires_at timestamptz NOT NULL,
|
||||
created_at timestamptz DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
updated_at timestamptz DEFAULT CURRENT_TIMESTAMP NOT NULL
|
||||
)
|
||||
`
|
||||
}
|
||||
];
|
||||
|
||||
/**
|
||||
* Create all authentication tables.
|
||||
* @returns {Promise<{ created: string[], skipped: string[] }>}
|
||||
*/
|
||||
export async function createTables() {
|
||||
const created = [];
|
||||
const skipped = [];
|
||||
|
||||
for (const table of AUTH_TABLES) {
|
||||
const exists = await tableExists(table.name);
|
||||
|
||||
if (!exists) {
|
||||
await query(table.sql);
|
||||
created.push(table.name);
|
||||
done(`Created table: ${table.name}`);
|
||||
} else {
|
||||
skipped.push(table.name);
|
||||
}
|
||||
}
|
||||
|
||||
return { created, skipped };
|
||||
}
|
||||
|
||||
/**
|
||||
* Drop all authentication tables in reverse dependency order.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
export async function dropTables() {
|
||||
const dropOrder = [...AUTH_TABLES].reverse().map(t => t.name);
|
||||
|
||||
warn('Dropping all Zen authentication tables...');
|
||||
|
||||
for (const tableName of dropOrder) {
|
||||
const exists = await tableExists(tableName);
|
||||
if (exists) {
|
||||
await query(`DROP TABLE IF EXISTS "${tableName}" CASCADE`);
|
||||
done(`Dropped table: ${tableName}`);
|
||||
}
|
||||
}
|
||||
|
||||
done('All authentication tables dropped');
|
||||
}
|
||||
Reference in New Issue
Block a user