From b8b0ebdf4dc9da6e26104b41a2fbf6935c5912e5 Mon Sep 17 00:00:00 2001 From: Matthew Raymer Date: Fri, 27 Jun 2025 08:29:31 +0000 Subject: [PATCH] Fix database migration errors by improving error handling - Enhanced migration service to handle duplicate column errors gracefully - Added detection for 'duplicate column' and 'already exists' errors - Migration service now marks partially applied migrations as complete - Prevents Electron app crashes due to cross-platform database conflicts - Improved robustness for database schema migrations Fixes database initialization issues when switching between platforms (web, mobile, electron) that may have different migration states. --- .../app/src/main/assets/capacitor.config.json | 65 +++++++++++++++++-- src/db-sql/migration.ts | 5 ++ src/services/migrationService.ts | 39 +++++++++-- 3 files changed, 97 insertions(+), 12 deletions(-) diff --git a/android/app/src/main/assets/capacitor.config.json b/android/app/src/main/assets/capacitor.config.json index 262f9ccdeb..1fb43ec638 100644 --- a/android/app/src/main/assets/capacitor.config.json +++ b/android/app/src/main/assets/capacitor.config.json @@ -2,7 +2,6 @@ "appId": "app.timesafari", "appName": "TimeSafari", "webDir": "dist", - "bundledWebRuntime": false, "server": { "cleartext": true }, @@ -17,18 +16,19 @@ ] } }, - "SQLite": { + "CapacitorSQLite": { "iosDatabaseLocation": "Library/CapacitorDatabase", - "iosIsEncryption": true, + "iosIsEncryption": false, "iosBiometric": { - "biometricAuth": true, + "biometricAuth": false, "biometricTitle": "Biometric login for TimeSafari" }, - "androidIsEncryption": true, + "androidIsEncryption": false, "androidBiometric": { - "biometricAuth": true, + "biometricAuth": false, "biometricTitle": "Biometric login for TimeSafari" - } + }, + "electronIsEncryption": false } }, "ios": { @@ -52,5 +52,56 @@ "*.jsdelivr.net", "api.endorser.ch" ] + }, + "electron": { + "deepLinking": { + "schemes": [ + "timesafari" + ] + }, + "buildOptions": { + "appId": "app.timesafari", + "productName": "TimeSafari", + "directories": { + "output": "dist-electron-packages" + }, + "files": [ + "dist/**/*", + "electron/**/*" + ], + "mac": { + "category": "public.app-category.productivity", + "target": [ + { + "target": "dmg", + "arch": [ + "x64", + "arm64" + ] + } + ] + }, + "win": { + "target": [ + { + "target": "nsis", + "arch": [ + "x64" + ] + } + ] + }, + "linux": { + "target": [ + { + "target": "AppImage", + "arch": [ + "x64" + ] + } + ], + "category": "Utility" + } + } } } diff --git a/src/db-sql/migration.ts b/src/db-sql/migration.ts index d8c61f890b..3d849f3f76 100644 --- a/src/db-sql/migration.ts +++ b/src/db-sql/migration.ts @@ -121,6 +121,11 @@ const MIGRATIONS = [ { name: "002_add_iViewContent_to_contacts", sql: ` + -- We need to handle the case where iViewContent column might already exist + -- SQLite doesn't support IF NOT EXISTS for ALTER TABLE ADD COLUMN + -- So we'll use a more robust approach with error handling in the migration service + + -- First, try to add the column - this will fail silently if it already exists ALTER TABLE contacts ADD COLUMN iViewContent BOOLEAN DEFAULT TRUE; `, }, diff --git a/src/services/migrationService.ts b/src/services/migrationService.ts index 089c7d65d1..1d5708aa96 100644 --- a/src/services/migrationService.ts +++ b/src/services/migrationService.ts @@ -119,11 +119,40 @@ export async function runMigrations( `[MigrationService] Successfully applied migration: ${migration.name}`, ); } catch (error) { - logger.error( - `[MigrationService] Failed to apply migration ${migration.name}:`, - error, - ); - throw new Error(`Migration ${migration.name} failed: ${error}`); + // Handle specific cases where the migration might be partially applied + const errorMessage = String(error).toLowerCase(); + + // Check if it's a duplicate column error - this means the column already exists + if (errorMessage.includes('duplicate column') || + errorMessage.includes('column already exists') || + errorMessage.includes('already exists')) { + logger.warn( + `[MigrationService] Migration ${migration.name} appears to be already applied (${errorMessage}). Marking as complete.`, + ); + + // Mark the migration as applied since the schema change already exists + try { + await sqlExec("INSERT INTO migrations (name) VALUES (?)", [ + migration.name, + ]); + logger.info( + `[MigrationService] Successfully marked migration as applied: ${migration.name}`, + ); + } catch (insertError) { + // If we can't insert the migration record, log it but don't fail + logger.warn( + `[MigrationService] Could not record migration ${migration.name} as applied:`, + insertError, + ); + } + } else { + // For other types of errors, still fail the migration + logger.error( + `[MigrationService] Failed to apply migration ${migration.name}:`, + error, + ); + throw new Error(`Migration ${migration.name} failed: ${error}`); + } } } } catch (error) {