diff --git a/database/createDb.js b/database/createDb.js index ef6fb02..d9b250f 100644 --- a/database/createDb.js +++ b/database/createDb.js @@ -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();