feat: enhance database initialization with nullable columns and virtual labels for referral tokens
This commit is contained in:
parent
0a983d0654
commit
1685df8389
@ -54,7 +54,7 @@ if (NODE_ENV === 'development') {
|
|||||||
|
|
||||||
const allowCreateDb = String(process.env.DB_ALLOW_CREATE_DB || 'false').toLowerCase() === 'true';
|
const allowCreateDb = String(process.env.DB_ALLOW_CREATE_DB || 'false').toLowerCase() === 'true';
|
||||||
|
|
||||||
// --- Performance Helpers (added) ---
|
// --- Performance Helpers ---
|
||||||
async function ensureIndex(conn, table, indexName, indexDDL) {
|
async function ensureIndex(conn, table, indexName, indexDDL) {
|
||||||
const [rows] = await conn.query(`SHOW INDEX FROM \`${table}\` WHERE Key_name = ?`, [indexName]);
|
const [rows] = await conn.query(`SHOW INDEX FROM \`${table}\` WHERE Key_name = ?`, [indexName]);
|
||||||
if (!rows.length) {
|
if (!rows.length) {
|
||||||
@ -167,6 +167,23 @@ async function createDatabase() {
|
|||||||
`);
|
`);
|
||||||
console.log('✅ Company profiles table created/verified');
|
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
|
// 4. user_status table: Comprehensive tracking of user verification and completion steps
|
||||||
await connection.query(`
|
await connection.query(`
|
||||||
CREATE TABLE IF NOT EXISTS user_status (
|
CREATE TABLE IF NOT EXISTS user_status (
|
||||||
@ -481,6 +498,43 @@ async function createDatabase() {
|
|||||||
console.log('✅ News table created/verified');
|
console.log('✅ News table created/verified');
|
||||||
console.log('✅ Referral tokens 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
|
// 13. referral_token_usage table: Tracks each use of a referral token
|
||||||
await connection.query(`
|
await connection.query(`
|
||||||
CREATE TABLE IF NOT EXISTS referral_token_usage (
|
CREATE TABLE IF NOT EXISTS referral_token_usage (
|
||||||
@ -911,19 +965,6 @@ async function createDatabase() {
|
|||||||
`);
|
`);
|
||||||
console.log('✅ matrix_instances table created/verified');
|
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(`
|
await connection.query(`
|
||||||
CREATE TABLE IF NOT EXISTS pools (
|
CREATE TABLE IF NOT EXISTS pools (
|
||||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
@ -1080,6 +1121,47 @@ async function createDatabase() {
|
|||||||
console.log('ℹ️ chk_matrix_singleton drop skipped:', e.message);
|
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!');
|
console.log('🎉 Normalized database schema created/updated successfully!');
|
||||||
|
|
||||||
await connection.end();
|
await connection.end();
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user