|
@ -253,7 +253,7 @@ async function validateMigrationApplication<T>( |
|
|
await sqlQuery( |
|
|
await sqlQuery( |
|
|
`SELECT name FROM sqlite_master WHERE type='table' AND name='${tableName}'`, |
|
|
`SELECT name FROM sqlite_master WHERE type='table' AND name='${tableName}'`, |
|
|
); |
|
|
); |
|
|
logger.log(`✅ [Migration-Validation] Table ${tableName} exists`); |
|
|
// Reduced logging - only log on error
|
|
|
} catch (error) { |
|
|
} catch (error) { |
|
|
validation.isValid = false; |
|
|
validation.isValid = false; |
|
|
validation.errors.push(`Table ${tableName} missing`); |
|
|
validation.errors.push(`Table ${tableName} missing`); |
|
@ -269,9 +269,7 @@ async function validateMigrationApplication<T>( |
|
|
try { |
|
|
try { |
|
|
await sqlQuery(`SELECT iViewContent FROM contacts LIMIT 1`); |
|
|
await sqlQuery(`SELECT iViewContent FROM contacts LIMIT 1`); |
|
|
validation.hasExpectedColumns = true; |
|
|
validation.hasExpectedColumns = true; |
|
|
logger.log( |
|
|
// Reduced logging - only log on error
|
|
|
`✅ [Migration-Validation] Column iViewContent exists in contacts table`, |
|
|
|
|
|
); |
|
|
|
|
|
} catch (error) { |
|
|
} catch (error) { |
|
|
validation.isValid = false; |
|
|
validation.isValid = false; |
|
|
validation.errors.push( |
|
|
validation.errors.push( |
|
@ -333,18 +331,16 @@ async function isSchemaAlreadyPresent<T>( |
|
|
const hasTable = |
|
|
const hasTable = |
|
|
result?.values?.length > 0 || |
|
|
result?.values?.length > 0 || |
|
|
(Array.isArray(result) && result.length > 0); |
|
|
(Array.isArray(result) && result.length > 0); |
|
|
logger.log( |
|
|
// Reduced logging - only log on error
|
|
|
`🔍 [Migration-Schema] Initial migration schema check - accounts table exists: ${hasTable}`, |
|
|
|
|
|
); |
|
|
|
|
|
return hasTable; |
|
|
return hasTable; |
|
|
} else if (migration.name === "002_add_iViewContent_to_contacts") { |
|
|
} else if (migration.name === "002_add_iViewContent_to_contacts") { |
|
|
// Check if iViewContent column exists in contacts table
|
|
|
// Check if iViewContent column exists in contacts table
|
|
|
try { |
|
|
try { |
|
|
await sqlQuery(`SELECT iViewContent FROM contacts LIMIT 1`); |
|
|
await sqlQuery(`SELECT iViewContent FROM contacts LIMIT 1`); |
|
|
logger.log(`🔍 [Migration-Schema] iViewContent column already exists`); |
|
|
// Reduced logging - only log on error
|
|
|
return true; |
|
|
return true; |
|
|
} catch (error) { |
|
|
} catch (error) { |
|
|
logger.log(`🔍 [Migration-Schema] iViewContent column does not exist`); |
|
|
// Reduced logging - only log on error
|
|
|
return false; |
|
|
return false; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
@ -354,7 +350,7 @@ async function isSchemaAlreadyPresent<T>( |
|
|
// // Check if future migration schema already exists
|
|
|
// // Check if future migration schema already exists
|
|
|
// }
|
|
|
// }
|
|
|
} catch (error) { |
|
|
} catch (error) { |
|
|
logger.log( |
|
|
logger.error( |
|
|
`🔍 [Migration-Schema] Schema check failed for ${migration.name}, assuming not present:`, |
|
|
`🔍 [Migration-Schema] Schema check failed for ${migration.name}, assuming not present:`, |
|
|
error, |
|
|
error, |
|
|
); |
|
|
); |
|
@ -413,83 +409,55 @@ export async function runMigrations<T>( |
|
|
|
|
|
|
|
|
// Step 1: Create migrations table if it doesn't exist
|
|
|
// Step 1: Create migrations table if it doesn't exist
|
|
|
// Note: We use IF NOT EXISTS here because this is infrastructure, not a business migration
|
|
|
// Note: We use IF NOT EXISTS here because this is infrastructure, not a business migration
|
|
|
logger.log( |
|
|
|
|
|
"🔧 [Migration] Creating migrations table if it doesn't exist...", |
|
|
|
|
|
); |
|
|
|
|
|
await sqlExec(` |
|
|
await sqlExec(` |
|
|
CREATE TABLE IF NOT EXISTS migrations ( |
|
|
CREATE TABLE IF NOT EXISTS migrations ( |
|
|
name TEXT PRIMARY KEY, |
|
|
name TEXT PRIMARY KEY, |
|
|
applied_at TEXT DEFAULT CURRENT_TIMESTAMP |
|
|
applied_at TEXT DEFAULT CURRENT_TIMESTAMP |
|
|
); |
|
|
); |
|
|
`);
|
|
|
`);
|
|
|
logger.log("✅ [Migration] Migrations table ready"); |
|
|
|
|
|
|
|
|
|
|
|
// Step 2: Get list of already applied migrations
|
|
|
// Step 2: Get list of already applied migrations
|
|
|
logger.log("🔍 [Migration] Querying existing migrations..."); |
|
|
|
|
|
const appliedMigrationsResult = await sqlQuery( |
|
|
const appliedMigrationsResult = await sqlQuery( |
|
|
"SELECT name FROM migrations", |
|
|
"SELECT name FROM migrations", |
|
|
); |
|
|
); |
|
|
logger.log("📊 [Migration] Raw query result:", appliedMigrationsResult); |
|
|
|
|
|
|
|
|
|
|
|
const appliedMigrations = extractMigrationNames(appliedMigrationsResult); |
|
|
const appliedMigrations = extractMigrationNames(appliedMigrationsResult); |
|
|
logger.log( |
|
|
|
|
|
"📋 [Migration] Extracted applied migrations:", |
|
|
|
|
|
Array.from(appliedMigrations), |
|
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
// Step 3: Get all registered migrations
|
|
|
// Step 3: Get all registered migrations
|
|
|
const migrations = migrationRegistry.getMigrations(); |
|
|
const migrations = migrationRegistry.getMigrations(); |
|
|
|
|
|
|
|
|
if (migrations.length === 0) { |
|
|
if (migrations.length === 0) { |
|
|
logger.warn("⚠️ [Migration] No migrations registered"); |
|
|
logger.warn("⚠️ [Migration] No migrations registered"); |
|
|
logger.warn("[MigrationService] No migrations registered"); |
|
|
|
|
|
return; |
|
|
return; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
logger.log( |
|
|
logger.log( |
|
|
`📊 [Migration] Found ${migrations.length} total migrations, ${appliedMigrations.size} already applied`, |
|
|
`📊 [Migration] Found ${migrations.length} total migrations, ${appliedMigrations.size} already applied`, |
|
|
); |
|
|
); |
|
|
logger.log( |
|
|
|
|
|
`📝 [Migration] Registered migrations: ${migrations.map((m) => m.name).join(", ")}`, |
|
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
let appliedCount = 0; |
|
|
let appliedCount = 0; |
|
|
let skippedCount = 0; |
|
|
let skippedCount = 0; |
|
|
|
|
|
|
|
|
// Step 4: Process each migration
|
|
|
// Step 4: Process each migration
|
|
|
for (const migration of migrations) { |
|
|
for (const migration of migrations) { |
|
|
logger.log(`\n🔍 [Migration] Processing migration: ${migration.name}`); |
|
|
|
|
|
|
|
|
|
|
|
// Check 1: Is it recorded as applied in migrations table?
|
|
|
// Check 1: Is it recorded as applied in migrations table?
|
|
|
const isRecordedAsApplied = appliedMigrations.has(migration.name); |
|
|
const isRecordedAsApplied = appliedMigrations.has(migration.name); |
|
|
|
|
|
|
|
|
// Check 2: Does the schema already exist in the database?
|
|
|
// Check 2: Does the schema already exist in the database?
|
|
|
const isSchemaPresent = await isSchemaAlreadyPresent(migration, sqlQuery); |
|
|
const isSchemaPresent = await isSchemaAlreadyPresent(migration, sqlQuery); |
|
|
|
|
|
|
|
|
logger.log( |
|
|
|
|
|
`🔍 [Migration] ${migration.name} - Recorded: ${isRecordedAsApplied}, Schema: ${isSchemaPresent}`, |
|
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
// Skip if already recorded as applied
|
|
|
// Skip if already recorded as applied
|
|
|
if (isRecordedAsApplied) { |
|
|
if (isRecordedAsApplied) { |
|
|
logger.log( |
|
|
|
|
|
`⏭️ [Migration] Skipping already applied: ${migration.name}`, |
|
|
|
|
|
); |
|
|
|
|
|
skippedCount++; |
|
|
skippedCount++; |
|
|
continue; |
|
|
continue; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// Handle case where schema exists but isn't recorded
|
|
|
// Handle case where schema exists but isn't recorded
|
|
|
if (isSchemaPresent) { |
|
|
if (isSchemaPresent) { |
|
|
logger.log( |
|
|
|
|
|
`🔄 [Migration] Schema exists but not recorded. Marking ${migration.name} as applied...`, |
|
|
|
|
|
); |
|
|
|
|
|
try { |
|
|
try { |
|
|
const insertResult = await sqlExec( |
|
|
await sqlExec("INSERT INTO migrations (name) VALUES (?)", [ |
|
|
"INSERT INTO migrations (name) VALUES (?)", |
|
|
migration.name, |
|
|
[migration.name], |
|
|
]); |
|
|
); |
|
|
|
|
|
logger.log(`✅ [Migration] Migration record inserted:`, insertResult); |
|
|
|
|
|
logger.log( |
|
|
logger.log( |
|
|
`✅ [Migration] Marked existing schema as applied: ${migration.name}`, |
|
|
`✅ [Migration] Marked existing schema as applied: ${migration.name}`, |
|
|
); |
|
|
); |
|
@ -509,11 +477,7 @@ export async function runMigrations<T>( |
|
|
|
|
|
|
|
|
try { |
|
|
try { |
|
|
// Execute the migration SQL
|
|
|
// Execute the migration SQL
|
|
|
logger.log(`🔧 [Migration] Executing SQL for ${migration.name}...`); |
|
|
|
|
|
await sqlExec(migration.sql); |
|
|
await sqlExec(migration.sql); |
|
|
logger.log( |
|
|
|
|
|
`✅ [Migration] SQL executed successfully for ${migration.name}`, |
|
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
// Validate the migration was applied correctly
|
|
|
// Validate the migration was applied correctly
|
|
|
const validation = await validateMigrationApplication( |
|
|
const validation = await validateMigrationApplication( |
|
@ -525,26 +489,14 @@ export async function runMigrations<T>( |
|
|
`⚠️ [Migration] Validation failed for ${migration.name}:`, |
|
|
`⚠️ [Migration] Validation failed for ${migration.name}:`, |
|
|
validation.errors, |
|
|
validation.errors, |
|
|
); |
|
|
); |
|
|
} else { |
|
|
|
|
|
logger.log( |
|
|
|
|
|
`✅ [Migration] Schema validation passed for ${migration.name}`, |
|
|
|
|
|
); |
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// Record that the migration was applied
|
|
|
// Record that the migration was applied
|
|
|
logger.log( |
|
|
await sqlExec("INSERT INTO migrations (name) VALUES (?)", [ |
|
|
`📝 [Migration] Recording migration ${migration.name} as applied...`, |
|
|
migration.name, |
|
|
); |
|
|
]); |
|
|
const insertResult = await sqlExec( |
|
|
|
|
|
"INSERT INTO migrations (name) VALUES (?)", |
|
|
|
|
|
[migration.name], |
|
|
|
|
|
); |
|
|
|
|
|
logger.log(`✅ [Migration] Migration record inserted:`, insertResult); |
|
|
|
|
|
|
|
|
|
|
|
logger.log(`🎉 [Migration] Successfully applied: ${migration.name}`); |
|
|
logger.log(`🎉 [Migration] Successfully applied: ${migration.name}`); |
|
|
logger.info( |
|
|
|
|
|
`[MigrationService] Successfully applied migration: ${migration.name}`, |
|
|
|
|
|
); |
|
|
|
|
|
appliedCount++; |
|
|
appliedCount++; |
|
|
} catch (error) { |
|
|
} catch (error) { |
|
|
logger.error(`❌ [Migration] Error applying ${migration.name}:`, error); |
|
|
logger.error(`❌ [Migration] Error applying ${migration.name}:`, error); |
|
@ -569,11 +521,7 @@ export async function runMigrations<T>( |
|
|
migration, |
|
|
migration, |
|
|
sqlQuery, |
|
|
sqlQuery, |
|
|
); |
|
|
); |
|
|
if (validation.isValid) { |
|
|
if (!validation.isValid) { |
|
|
logger.log( |
|
|
|
|
|
`✅ [Migration] Schema validation passed for ${migration.name}`, |
|
|
|
|
|
); |
|
|
|
|
|
} else { |
|
|
|
|
|
logger.warn( |
|
|
logger.warn( |
|
|
`⚠️ [Migration] Schema validation failed for ${migration.name}:`, |
|
|
`⚠️ [Migration] Schema validation failed for ${migration.name}:`, |
|
|
validation.errors, |
|
|
validation.errors, |
|
@ -582,21 +530,10 @@ export async function runMigrations<T>( |
|
|
|
|
|
|
|
|
// Mark the migration as applied since the schema change already exists
|
|
|
// Mark the migration as applied since the schema change already exists
|
|
|
try { |
|
|
try { |
|
|
logger.log( |
|
|
await sqlExec("INSERT INTO migrations (name) VALUES (?)", [ |
|
|
`📝 [Migration] Attempting to record ${migration.name} as applied despite error...`, |
|
|
migration.name, |
|
|
); |
|
|
]); |
|
|
const insertResult = await sqlExec( |
|
|
|
|
|
"INSERT INTO migrations (name) VALUES (?)", |
|
|
|
|
|
[migration.name], |
|
|
|
|
|
); |
|
|
|
|
|
logger.log( |
|
|
|
|
|
`✅ [Migration] Migration record inserted after error:`, |
|
|
|
|
|
insertResult, |
|
|
|
|
|
); |
|
|
|
|
|
logger.log(`✅ [Migration] Marked as applied: ${migration.name}`); |
|
|
logger.log(`✅ [Migration] Marked as applied: ${migration.name}`); |
|
|
logger.info( |
|
|
|
|
|
`[MigrationService] Successfully marked migration as applied: ${migration.name}`, |
|
|
|
|
|
); |
|
|
|
|
|
appliedCount++; |
|
|
appliedCount++; |
|
|
} catch (insertError) { |
|
|
} catch (insertError) { |
|
|
// If we can't insert the migration record, log it but don't fail
|
|
|
// If we can't insert the migration record, log it but don't fail
|
|
@ -604,10 +541,6 @@ export async function runMigrations<T>( |
|
|
`⚠️ [Migration] Could not record ${migration.name} as applied:`, |
|
|
`⚠️ [Migration] Could not record ${migration.name} as applied:`, |
|
|
insertError, |
|
|
insertError, |
|
|
); |
|
|
); |
|
|
logger.warn( |
|
|
|
|
|
`[MigrationService] Could not record migration ${migration.name} as applied:`, |
|
|
|
|
|
insertError, |
|
|
|
|
|
); |
|
|
|
|
|
} |
|
|
} |
|
|
} else { |
|
|
} else { |
|
|
// For other types of errors, still fail the migration
|
|
|
// For other types of errors, still fail the migration
|
|
@ -615,25 +548,14 @@ export async function runMigrations<T>( |
|
|
`❌ [Migration] Failed to apply ${migration.name}:`, |
|
|
`❌ [Migration] Failed to apply ${migration.name}:`, |
|
|
error, |
|
|
error, |
|
|
); |
|
|
); |
|
|
logger.error( |
|
|
|
|
|
`[MigrationService] Failed to apply migration ${migration.name}:`, |
|
|
|
|
|
error, |
|
|
|
|
|
); |
|
|
|
|
|
throw new Error(`Migration ${migration.name} failed: ${error}`); |
|
|
throw new Error(`Migration ${migration.name} failed: ${error}`); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// Step 5: Final validation - verify all migrations are properly recorded
|
|
|
// Step 5: Final validation - verify all migrations are properly recorded
|
|
|
logger.log( |
|
|
|
|
|
"\n🔍 [Migration] Final validation - checking migrations table...", |
|
|
|
|
|
); |
|
|
|
|
|
const finalMigrationsResult = await sqlQuery("SELECT name FROM migrations"); |
|
|
const finalMigrationsResult = await sqlQuery("SELECT name FROM migrations"); |
|
|
const finalAppliedMigrations = extractMigrationNames(finalMigrationsResult); |
|
|
const finalAppliedMigrations = extractMigrationNames(finalMigrationsResult); |
|
|
logger.log( |
|
|
|
|
|
"📋 [Migration] Final applied migrations:", |
|
|
|
|
|
Array.from(finalAppliedMigrations), |
|
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
// Check that all expected migrations are recorded
|
|
|
// Check that all expected migrations are recorded
|
|
|
const expectedMigrations = new Set(migrations.map((m) => m.name)); |
|
|
const expectedMigrations = new Set(migrations.map((m) => m.name)); |
|
@ -645,17 +567,10 @@ export async function runMigrations<T>( |
|
|
logger.warn( |
|
|
logger.warn( |
|
|
`⚠️ [Migration] Missing migration records: ${missingMigrations.join(", ")}`, |
|
|
`⚠️ [Migration] Missing migration records: ${missingMigrations.join(", ")}`, |
|
|
); |
|
|
); |
|
|
logger.warn( |
|
|
|
|
|
`[MigrationService] Missing migration records: ${missingMigrations.join(", ")}`, |
|
|
|
|
|
); |
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
logger.log(`\n🎉 [Migration] Migration process complete!`); |
|
|
|
|
|
logger.log( |
|
|
logger.log( |
|
|
`📊 [Migration] Summary: Applied: ${appliedCount}, Skipped: ${skippedCount}, Total: ${migrations.length}`, |
|
|
`🎉 [Migration] Migration process complete! Summary: ${appliedCount} applied, ${skippedCount} skipped`, |
|
|
); |
|
|
|
|
|
logger.info( |
|
|
|
|
|
`[MigrationService] Migration process complete. Applied: ${appliedCount}, Skipped: ${skippedCount}`, |
|
|
|
|
|
); |
|
|
); |
|
|
} catch (error) { |
|
|
} catch (error) { |
|
|
logger.error("\n💥 [Migration] Migration process failed:", error); |
|
|
logger.error("\n💥 [Migration] Migration process failed:", error); |
|
|