feat: enhance database initialization with nullable columns and virtual labels for referral tokens

This commit is contained in:
seaznCode 2026-01-17 19:38:49 +01:00
parent 0a983d0654
commit 1685df8389

View File

@ -54,7 +54,7 @@ if (NODE_ENV === 'development') {
const allowCreateDb = String(process.env.DB_ALLOW_CREATE_DB || 'false').toLowerCase() === 'true';
// --- Performance Helpers (added) ---
// --- Performance Helpers ---
async function ensureIndex(conn, table, indexName, indexDDL) {
const [rows] = await conn.query(`SHOW INDEX FROM \`${table}\` WHERE Key_name = ?`, [indexName]);
if (!rows.length) {
@ -167,6 +167,23 @@ async function createDatabase() {
`);
console.log('✅ Company profiles table created/verified');
// Ensure registration_number allows NULL if table already existed
try {
await connection.query(`ALTER TABLE company_profiles MODIFY COLUMN registration_number VARCHAR(255) UNIQUE NULL`);
console.log('🔧 Ensured registration_number allows NULL');
} catch (e) {
console.log(' registration_number column already allows NULL or ALTER not required');
}
// Ensure phone columns are nullable if tables already existed
try {
await connection.query(`ALTER TABLE personal_profiles MODIFY COLUMN phone VARCHAR(255) NULL`);
await connection.query(`ALTER TABLE company_profiles MODIFY COLUMN phone VARCHAR(255) NULL`);
console.log('🔧 Ensured phone columns are nullable');
} catch (e) {
console.log(' Phone columns already nullable or ALTER not required');
}
// 4. user_status table: Comprehensive tracking of user verification and completion steps
await connection.query(`
CREATE TABLE IF NOT EXISTS user_status (
@ -481,6 +498,43 @@ async function createDatabase() {
console.log('✅ News table created/verified');
console.log('✅ Referral tokens table created/verified');
// Generated label columns (virtual) to display 'unlimited' instead of -1 in UI tools
try {
await connection.query(`
ALTER TABLE referral_tokens
ADD COLUMN max_uses_label VARCHAR(20)
GENERATED ALWAYS AS (CASE WHEN max_uses = -1 THEN 'unlimited' ELSE CAST(max_uses AS CHAR) END) VIRTUAL
`);
console.log('🆕 Added virtual column referral_tokens.max_uses_label');
} catch (e) {
console.log(' max_uses_label already exists or cannot add:', e.message);
}
try {
await connection.query(`
ALTER TABLE referral_tokens
ADD COLUMN uses_remaining_label VARCHAR(20)
GENERATED ALWAYS AS (CASE WHEN uses_remaining = -1 THEN 'unlimited' ELSE CAST(uses_remaining AS CHAR) END) VIRTUAL
`);
console.log('🆕 Added virtual column referral_tokens.uses_remaining_label');
} catch (e) {
console.log(' uses_remaining_label already exists or cannot add:', e.message);
}
// Normalized view now sources the generated label columns (still handy for joins)
try {
await connection.query(`
CREATE OR REPLACE VIEW referral_tokens_normalized AS
SELECT
rt.*,
rt.max_uses_label AS max_uses_display,
rt.uses_remaining_label AS uses_remaining_display
FROM referral_tokens rt;
`);
console.log('🆕 referral_tokens_normalized view created/updated');
} catch (e) {
console.warn('⚠️ Could not create referral_tokens_normalized view:', e.message);
}
// 13. referral_token_usage table: Tracks each use of a referral token
await connection.query(`
CREATE TABLE IF NOT EXISTS referral_token_usage (
@ -911,19 +965,6 @@ async function createDatabase() {
`);
console.log('✅ matrix_instances table created/verified');
// Legacy: ensure legacy matrix_config exists (still needed short-term for migration)
await connection.query(`
CREATE TABLE IF NOT EXISTS matrix_config (
id TINYINT PRIMARY KEY DEFAULT 1,
master_top_user_id INT NOT NULL,
name VARCHAR(255) NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
CONSTRAINT fk_matrix_config_master FOREIGN KEY (master_top_user_id) REFERENCES users(id) ON DELETE RESTRICT ON UPDATE CASCADE
);
`);
console.log(' matrix_config (legacy) verified');
await connection.query(`
CREATE TABLE IF NOT EXISTS pools (
id INT AUTO_INCREMENT PRIMARY KEY,
@ -1080,6 +1121,47 @@ async function createDatabase() {
console.log(' chk_matrix_singleton drop skipped:', e.message);
}
// --- Added Index Optimization Section ---
try {
// Core / status
await ensureIndex(connection, 'users', 'idx_users_created_at', 'created_at');
await ensureIndex(connection, 'user_status', 'idx_user_status_status', 'status');
await ensureIndex(connection, 'user_status', 'idx_user_status_registration_completed', 'registration_completed');
// Tokens & auth
await ensureIndex(connection, 'refresh_tokens', 'idx_refresh_user_expires', 'user_id, expires_at');
await ensureIndex(connection, 'email_verifications', 'idx_email_verifications_user_expires', 'user_id, expires_at');
await ensureIndex(connection, 'password_resets', 'idx_password_resets_user_expires', 'user_id, expires_at');
// Documents
await ensureIndex(connection, 'user_documents', 'idx_user_documents_upload_at', 'upload_at');
await ensureIndex(connection, 'user_documents', 'idx_user_documents_verified', 'verified_by_admin');
await ensureIndex(connection, 'user_id_documents', 'idx_user_id_docs_user_type', 'user_id, document_type');
// Activity logs (composite for common filtered ordering)
await ensureIndex(connection, 'user_action_logs', 'idx_user_action_logs_action_created', 'action, created_at');
// Referrals
await ensureIndex(connection, 'referral_token_usage', 'idx_referral_token_usage_used_at', 'used_at');
// Permissions
await ensureIndex(connection, 'permissions', 'idx_permissions_is_active', 'is_active');
await ensureIndex(connection, 'user_permissions', 'idx_user_permissions_granted_by', 'granted_by');
// Settings
await ensureIndex(connection, 'user_settings', 'idx_user_settings_theme', 'theme');
// Rate limit (for queries only on rate_key)
await ensureIndex(connection, 'rate_limit', 'idx_rate_limit_rate_key', 'rate_key');
await ensureIndex(connection, 'document_templates', 'idx_document_templates_user_type', 'user_type');
await ensureIndex(connection, 'document_templates', 'idx_document_templates_state_user_type', 'state, user_type');
await ensureIndex(connection, 'company_stamps', 'idx_company_stamps_company', 'company_id');
await ensureIndex(connection, 'company_stamps', 'idx_company_stamps_active', 'is_active');
console.log('🚀 Performance indexes created/verified');
} catch (e) {
console.warn('⚠️ Index optimization phase encountered an issue:', e.message);
}
console.log('🎉 Normalized database schema created/updated successfully!');
await connection.end();