From 8e605d04d7fe6303e927837a7c2361cd94a0a09e Mon Sep 17 00:00:00 2001 From: Trent Larson <trent@trentlarson.com> Date: Thu, 19 Jun 2025 18:12:56 -0600 Subject: [PATCH] IndexedDB migration: fix settings update --- src/services/indexedDBMigrationService.ts | 93 ++++++++++++++--------- src/views/DatabaseMigration.vue | 25 +++--- 2 files changed, 66 insertions(+), 52 deletions(-) diff --git a/src/services/indexedDBMigrationService.ts b/src/services/indexedDBMigrationService.ts index 5aaa1241..f6061b8f 100644 --- a/src/services/indexedDBMigrationService.ts +++ b/src/services/indexedDBMigrationService.ts @@ -898,6 +898,10 @@ export function generateComparisonYaml(comparison: DataComparison): string { * } * ``` */ +/** + * + * I recommend using the existing contact import view to migrate contacts. + * export async function migrateContacts( overwriteExisting: boolean = false, ): Promise<MigrationResult> { @@ -976,6 +980,8 @@ export async function migrateContacts( return result; } } + * + */ /** * Migrates specific settings fields from Dexie to SQLite database @@ -991,13 +997,12 @@ export async function migrateContacts( * * @async * @function migrateSettings - * @param {boolean} [overwriteExisting=false] - Whether to overwrite existing settings in SQLite * @returns {Promise<MigrationResult>} Detailed results of the migration operation * @throws {Error} If the migration process fails completely * @example * ```typescript * try { - * const result = await migrateSettings(true); // Overwrite existing + * const result = await migrateSettings(); * if (result.success) { * console.log(`Successfully migrated ${result.settingsMigrated} settings`); * } else { @@ -1008,12 +1013,8 @@ export async function migrateContacts( * } * ``` */ -export async function migrateSettings( - overwriteExisting: boolean = false, -): Promise<MigrationResult> { - logger.info("[MigrationService] Starting settings migration", { - overwriteExisting, - }); +export async function migrateSettings(): Promise<MigrationResult> { + logger.info("[MigrationService] Starting settings migration"); const result: MigrationResult = { success: true, @@ -1026,19 +1027,34 @@ export async function migrateSettings( try { const dexieSettings = await getDexieSettings(); + logger.info("[MigrationService] Migrating settings", { + dexieSettings: dexieSettings.length, + }); const platformService = PlatformServiceFactory.getInstance(); - // loop through dexieSettings, - // load the one with the matching accountDid from sqlite, - // and if one doesn't exist then insert it, - // otherwise, update the fields - dexieSettings.forEach(async (setting) => { - const sqliteSettingRaw = await platformService.dbQuery( - "SELECT * FROM settings WHERE accountDid = ?", - [setting.accountDid] - ); + + // Create an array of promises for all settings migrations + const migrationPromises = dexieSettings.map(async (setting) => { + logger.info("[MigrationService] Starting to migrate settings", setting); + let sqliteSettingRaw: { columns: string[], values: unknown[][] } | undefined; + if (!setting.accountDid) { + sqliteSettingRaw = await platformService.dbQuery( + "SELECT * FROM settings WHERE accountDid is null" + ); + } else { + sqliteSettingRaw = await platformService.dbQuery( + "SELECT * FROM settings WHERE accountDid = ?", + [setting.accountDid] + ) + } + logger.info("[MigrationService] Migrating one set of settings:", { + setting, + sqliteSettingRaw, + }); if (sqliteSettingRaw?.values?.length) { // should cover the master settings, were accountDid is null - const sqliteSetting = mapColumnsToValues(sqliteSettingRaw.columns, sqliteSettingRaw.values) as unknown as Settings; + const sqliteSettings = mapColumnsToValues(sqliteSettingRaw.columns, sqliteSettingRaw.values) as unknown as Settings[]; + const sqliteSetting = sqliteSettings[0]; + console.log('sqliteSetting', sqliteSetting) let conditional: string; let preparams: unknown[]; if (!setting.accountDid) { @@ -1054,7 +1070,7 @@ export async function migrateSettings( conditional, preparams ); - await platformService.dbExec(sql, params); + await platformService.dbExec(sql, params) result.settingsMigrated++; } else { // insert new setting @@ -1068,12 +1084,17 @@ export async function migrateSettings( } }); + // Wait for all migrations to complete + const updatedSettings = await Promise.all(migrationPromises); + + logger.info("[MigrationService] Finished migrating settings", updatedSettings, result); + return result; } catch (error) { + logger.error("[MigrationService] Complete settings migration failed:", error); const errorMessage = `Settings migration failed: ${error}`; result.errors.push(errorMessage); result.success = false; - logger.error("[MigrationService] Complete settings migration failed:", error); return result; } } @@ -1083,7 +1104,7 @@ export async function migrateSettings( * * This function transfers all accounts from the Dexie database to the * SQLite database. It handles both new accounts (INSERT) and existing - * accounts (UPDATE) based on the overwriteExisting parameter. + * accounts (UPDATE). * * For accounts with mnemonic data, the function uses importFromMnemonic * to ensure proper key derivation and identity creation during migration. @@ -1100,7 +1121,7 @@ export async function migrateSettings( * @example * ```typescript * try { - * const result = await migrateAccounts(true); // Overwrite existing + * const result = await migrateAccounts(); * if (result.success) { * console.log(`Successfully migrated ${result.accountsMigrated} accounts`); * } else { @@ -1199,12 +1220,9 @@ export async function migrateAccounts(): Promise<MigrationResult> { * The migration runs within a transaction to ensure atomicity. If any step fails, * the entire migration is rolled back. * - * @param overwriteExisting - Whether to overwrite existing records in SQLite * @returns Promise<MigrationResult> - Detailed result of the migration operation */ -export async function migrateAll( - overwriteExisting: boolean = false, -): Promise<MigrationResult> { +export async function migrateAll(): Promise<MigrationResult> { const result: MigrationResult = { success: false, contactsMigrated: 0, @@ -1233,7 +1251,7 @@ export async function migrateAll( // Step 2: Migrate Settings (depends on accounts) logger.info("[MigrationService] Step 2: Migrating settings..."); - const settingsResult = await migrateSettings(overwriteExisting); + const settingsResult = await migrateSettings(); if (!settingsResult.success) { result.errors.push( `Settings migration failed: ${settingsResult.errors.join(", ")}`, @@ -1244,16 +1262,17 @@ export async function migrateAll( result.warnings.push(...settingsResult.warnings); // Step 3: Migrate Contacts (independent, but after accounts for consistency) - logger.info("[MigrationService] Step 3: Migrating contacts..."); - const contactsResult = await migrateContacts(overwriteExisting); - if (!contactsResult.success) { - result.errors.push( - `Contact migration failed: ${contactsResult.errors.join(", ")}`, - ); - return result; - } - result.contactsMigrated = contactsResult.contactsMigrated; - result.warnings.push(...contactsResult.warnings); + // ... but which is better done through the contact import view + // logger.info("[MigrationService] Step 3: Migrating contacts..."); + // const contactsResult = await migrateContacts(); + // if (!contactsResult.success) { + // result.errors.push( + // `Contact migration failed: ${contactsResult.errors.join(", ")}`, + // ); + // return result; + // } + // result.contactsMigrated = contactsResult.contactsMigrated; + // result.warnings.push(...contactsResult.warnings); // All migrations successful result.success = true; diff --git a/src/views/DatabaseMigration.vue b/src/views/DatabaseMigration.vue index 95917e12..bfd3caed 100644 --- a/src/views/DatabaseMigration.vue +++ b/src/views/DatabaseMigration.vue @@ -19,8 +19,9 @@ </a> </div> + <!-- Migration Options --> + <!-- <div class="mt-4"> - <!-- Migration Options --> <div class="bg-white shadow rounded-lg"> <div class="px-4 py-5 sm:p-6"> <h3 class="text-lg leading-6 font-medium text-gray-900 mb-4"> @@ -52,6 +53,7 @@ </div> </div> </div> + --> <div class="text-lg leading-6 font-medium text-red-900 mt-4" @@ -59,7 +61,7 @@ <p v-if="comparison && (comparison.sqliteAccounts.length > 1 || comparison.sqliteSettings.length > 1 || comparison.sqliteContacts.length > 0)" > - Beware: you have unexpected existing data in the SQLite database that will be overwritten. Talk with Trent. + Beware: you have unexpected existing data in the new database that will be overwritten. Talk with Trent. </p> <p v-if="cannotfindMainAccount" @@ -951,7 +953,6 @@ import { Router } from "vue-router"; import IconRenderer from "../components/IconRenderer.vue"; import { compareDatabases, - migrateContacts, migrateSettings, migrateAccounts, migrateAll, @@ -1004,7 +1005,6 @@ export default class DatabaseMigration extends Vue { private loadingMessage = ""; private error = ""; private exportedData: Record<string, any> | null = null; - private overwriteExisting = true; private successMessage = ""; useClipboard = useClipboard; @@ -1075,7 +1075,7 @@ export default class DatabaseMigration extends Vue { window.alert('Copied to clipboard!'); } } catch (error) { - console.error('Failed to copy to clipboard:', error); + logger.error('Failed to copy to clipboard:', error); if (typeof window !== 'undefined') { window.alert('Failed to copy to clipboard'); } @@ -1141,7 +1141,7 @@ export default class DatabaseMigration extends Vue { this.clearMessages(); try { - const result: MigrationResult = await migrateAll(this.overwriteExisting); + const result: MigrationResult = await migrateAll(); if (result.success) { const totalMigrated = @@ -1209,8 +1209,7 @@ export default class DatabaseMigration extends Vue { /** * Migrates contacts from Dexie to SQLite database * - * This method transfers contacts from the Dexie database to SQLite, - * with options to overwrite existing records. + * This method transfers contacts from the Dexie database to SQLite. * * @async * @returns {Promise<void>} @@ -1230,8 +1229,7 @@ export default class DatabaseMigration extends Vue { /** * Migrates settings from Dexie to SQLite database * - * This method transfers settings from the Dexie database to SQLite, - * with options to overwrite existing records. + * This method transfers settings from the Dexie database to SQLite. * * @async * @returns {Promise<void>} @@ -1241,9 +1239,7 @@ export default class DatabaseMigration extends Vue { this.clearMessages(); try { - const result: MigrationResult = await migrateSettings( - this.overwriteExisting, - ); + const result: MigrationResult = await migrateSettings(); if (result.success) { this.successMessage = `Successfully migrated ${result.settingsMigrated} settings.`; @@ -1276,8 +1272,7 @@ export default class DatabaseMigration extends Vue { * Migrates accounts from Dexie to SQLite database * * This method transfers accounts from the Dexie database to SQLite, - * with options to overwrite existing records. For accounts with mnemonic - * data, it uses the importFromMnemonic utility for proper key derivation. + * For accounts with mnemonic data, it uses the importFromMnemonic utility for proper key derivation. * * @async * @returns {Promise<void>}