From 7948f6552c89faaf898871c261b6b2f51984d2cf Mon Sep 17 00:00:00 2001 From: Chris Date: Sun, 26 Apr 2026 10:07:55 +0300 Subject: [PATCH] fix: make password_digest migration compatible with all schema versions (#1078) * fix: replace 6-word limit with 150-character limit for project names Replaces the word-based validation with character-based validation as originally requested in #971. The 6-word limit was causing issues with small words and separators being counted equally, and didn't match the original requirement for a character limit. Changes: - Backend: Replace wordCount validator with len validator (1-150 chars) - Frontend: Replace word count validation with character length check - UI already has line-clamp-3 for display truncation Fixes #998 * fix: make password_digest migration compatible with all schema versions Fixes a critical bug where the make-password-optional migration would silently fail when upgrading from v1.0.0 or running on fresh v1.1.0-dev installations. The migration was trying to SELECT columns (ai_provider, openai_api_key, ollama_base_url, ollama_model) that don't exist in the users table at that point in the migration chain, causing the INSERT...SELECT to fail and leaving password_digest as NOT NULL. This prevented OIDC auto-provisioning from creating new users without passwords. The fix dynamically detects which columns exist in the users table using PRAGMA table_info and only selects columns that are guaranteed to exist. Missing columns (AI-related fields) will receive their default values from the new table schema. Changes: - Added dynamic column detection using PRAGMA table_info - Only SELECT columns that exist in the current users table - AI columns get default values if they don't exist yet - Applied same fix to both up and down migrations - Properly handle password/password_digest column name migration Fixes #1075 --- .../20260420000004-make-password-optional.js | 182 +++++++++++++----- 1 file changed, 130 insertions(+), 52 deletions(-) diff --git a/backend/migrations/20260420000004-make-password-optional.js b/backend/migrations/20260420000004-make-password-optional.js index 99d835a..38cf3d8 100644 --- a/backend/migrations/20260420000004-make-password-optional.js +++ b/backend/migrations/20260420000004-make-password-optional.js @@ -68,34 +68,79 @@ module.exports = { ); } + const existingColumns = new Set(columns.map((col) => col.name)); + + const baseColumns = [ + 'id', + 'uid', + 'name', + 'surname', + 'email', + 'appearance', + 'language', + 'timezone', + 'first_day_of_week', + 'avatar_image', + 'telegram_bot_token', + 'telegram_chat_id', + 'task_summary_enabled', + 'task_summary_frequency', + 'task_summary_last_run', + 'task_summary_next_run', + 'telegram_allowed_users', + 'task_intelligence_enabled', + 'auto_suggest_next_actions_enabled', + 'pomodoro_enabled', + 'productivity_assistant_enabled', + 'next_task_suggestion_enabled', + 'today_settings', + 'sidebar_settings', + 'ui_settings', + 'notification_preferences', + 'keyboard_shortcuts', + 'email_verified', + 'email_verification_token', + 'email_verification_token_expires_at', + 'created_at', + 'updated_at', + ]; + + const aiColumns = [ + 'ai_provider', + 'openai_api_key', + 'ollama_base_url', + 'ollama_model', + ]; + + const columnsToSelect = baseColumns.filter((col) => + existingColumns.has(col) + ); + + const aiColumnsToSelect = aiColumns.filter((col) => + existingColumns.has(col) + ); + + const columnsWithoutPassword = columnsToSelect.filter( + (col) => col !== passwordColumn && col !== 'password_digest' + ); + + const insertColumns = [ + ...columnsWithoutPassword, + 'password_digest', + ...aiColumnsToSelect, + ]; + const selectColumns = [ + ...columnsWithoutPassword, + `${passwordColumn} as password_digest`, + ...aiColumnsToSelect, + ]; + + const insertColumnsStr = insertColumns.join(', '); + const selectColumnsStr = selectColumns.join(', '); + await queryInterface.sequelize.query(` - INSERT INTO users_new ( - id, uid, name, surname, email, password_digest, appearance, language, - timezone, first_day_of_week, avatar_image, telegram_bot_token, - telegram_chat_id, task_summary_enabled, task_summary_frequency, - task_summary_last_run, task_summary_next_run, telegram_allowed_users, - task_intelligence_enabled, auto_suggest_next_actions_enabled, - pomodoro_enabled, productivity_assistant_enabled, - next_task_suggestion_enabled, today_settings, sidebar_settings, - ui_settings, notification_preferences, keyboard_shortcuts, - email_verified, email_verification_token, - email_verification_token_expires_at, created_at, updated_at, - ai_provider, openai_api_key, ollama_base_url, ollama_model - ) - SELECT - id, uid, name, surname, email, - ${passwordColumn} as password_digest, - appearance, language, - timezone, first_day_of_week, avatar_image, telegram_bot_token, - telegram_chat_id, task_summary_enabled, task_summary_frequency, - task_summary_last_run, task_summary_next_run, telegram_allowed_users, - task_intelligence_enabled, auto_suggest_next_actions_enabled, - pomodoro_enabled, productivity_assistant_enabled, - next_task_suggestion_enabled, today_settings, sidebar_settings, - ui_settings, notification_preferences, keyboard_shortcuts, - email_verified, email_verification_token, - email_verification_token_expires_at, created_at, updated_at, - ai_provider, openai_api_key, ollama_base_url, ollama_model + INSERT INTO users_new (${insertColumnsStr}) + SELECT ${selectColumnsStr} FROM users; `); @@ -155,32 +200,65 @@ module.exports = { ); `); + const [columns] = await queryInterface.sequelize.query( + 'PRAGMA table_info(users);' + ); + const existingColumns = new Set(columns.map((col) => col.name)); + + const baseColumns = [ + 'id', + 'uid', + 'name', + 'surname', + 'email', + 'password_digest', + 'appearance', + 'language', + 'timezone', + 'first_day_of_week', + 'avatar_image', + 'telegram_bot_token', + 'telegram_chat_id', + 'task_summary_enabled', + 'task_summary_frequency', + 'task_summary_last_run', + 'task_summary_next_run', + 'telegram_allowed_users', + 'task_intelligence_enabled', + 'auto_suggest_next_actions_enabled', + 'pomodoro_enabled', + 'productivity_assistant_enabled', + 'next_task_suggestion_enabled', + 'today_settings', + 'sidebar_settings', + 'ui_settings', + 'notification_preferences', + 'keyboard_shortcuts', + 'email_verified', + 'email_verification_token', + 'email_verification_token_expires_at', + 'created_at', + 'updated_at', + ]; + + const aiColumns = [ + 'ai_provider', + 'openai_api_key', + 'ollama_base_url', + 'ollama_model', + ]; + + const columnsToSelect = [ + ...baseColumns.filter((col) => existingColumns.has(col)), + ...aiColumns.filter((col) => existingColumns.has(col)), + ]; + + const insertColumnsStr = columnsToSelect.join(', '); + const selectColumnsStr = columnsToSelect.join(', '); + await queryInterface.sequelize.query(` - INSERT INTO users_new ( - id, uid, name, surname, email, password_digest, appearance, language, - timezone, first_day_of_week, avatar_image, telegram_bot_token, - telegram_chat_id, task_summary_enabled, task_summary_frequency, - task_summary_last_run, task_summary_next_run, telegram_allowed_users, - task_intelligence_enabled, auto_suggest_next_actions_enabled, - pomodoro_enabled, productivity_assistant_enabled, - next_task_suggestion_enabled, today_settings, sidebar_settings, - ui_settings, notification_preferences, keyboard_shortcuts, - email_verified, email_verification_token, - email_verification_token_expires_at, created_at, updated_at, - ai_provider, openai_api_key, ollama_base_url, ollama_model - ) - SELECT - id, uid, name, surname, email, password_digest, appearance, language, - timezone, first_day_of_week, avatar_image, telegram_bot_token, - telegram_chat_id, task_summary_enabled, task_summary_frequency, - task_summary_last_run, task_summary_next_run, telegram_allowed_users, - task_intelligence_enabled, auto_suggest_next_actions_enabled, - pomodoro_enabled, productivity_assistant_enabled, - next_task_suggestion_enabled, today_settings, sidebar_settings, - ui_settings, notification_preferences, keyboard_shortcuts, - email_verified, email_verification_token, - email_verification_token_expires_at, created_at, updated_at, - ai_provider, openai_api_key, ollama_base_url, ollama_model + INSERT INTO users_new (${insertColumnsStr}) + SELECT ${selectColumnsStr} FROM users; `);