|
@ -26,10 +26,19 @@ import "dexie-export-import"; |
|
|
import { PlatformServiceFactory } from "./PlatformServiceFactory"; |
|
|
import { PlatformServiceFactory } from "./PlatformServiceFactory"; |
|
|
import { db, accountsDBPromise } from "../db/index"; |
|
|
import { db, accountsDBPromise } from "../db/index"; |
|
|
import { Contact, ContactMethod } from "../db/tables/contacts"; |
|
|
import { Contact, ContactMethod } from "../db/tables/contacts"; |
|
|
import { Settings, MASTER_SETTINGS_KEY, BoundingBox } from "../db/tables/settings"; |
|
|
import { |
|
|
|
|
|
Settings, |
|
|
|
|
|
MASTER_SETTINGS_KEY, |
|
|
|
|
|
BoundingBox, |
|
|
|
|
|
} from "../db/tables/settings"; |
|
|
import { Account } from "../db/tables/accounts"; |
|
|
import { Account } from "../db/tables/accounts"; |
|
|
import { logger } from "../utils/logger"; |
|
|
import { logger } from "../utils/logger"; |
|
|
import { mapColumnsToValues, parseJsonField, generateUpdateStatement, generateInsertStatement } from "../db/databaseUtil"; |
|
|
import { |
|
|
|
|
|
mapColumnsToValues, |
|
|
|
|
|
parseJsonField, |
|
|
|
|
|
generateUpdateStatement, |
|
|
|
|
|
generateInsertStatement, |
|
|
|
|
|
} from "../db/databaseUtil"; |
|
|
import { importFromMnemonic } from "../libs/util"; |
|
|
import { importFromMnemonic } from "../libs/util"; |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
@ -186,12 +195,18 @@ export async function getSqliteContacts(): Promise<Contact[]> { |
|
|
|
|
|
|
|
|
let contacts: Contact[] = []; |
|
|
let contacts: Contact[] = []; |
|
|
if (result?.values?.length) { |
|
|
if (result?.values?.length) { |
|
|
const preContacts = mapColumnsToValues(result.columns, result.values) as unknown as Contact[]; |
|
|
const preContacts = mapColumnsToValues( |
|
|
|
|
|
result.columns, |
|
|
|
|
|
result.values, |
|
|
|
|
|
) as unknown as Contact[]; |
|
|
// This is redundant since absurd-sql auto-parses JSON strings to objects.
|
|
|
// This is redundant since absurd-sql auto-parses JSON strings to objects.
|
|
|
// But we started it, and it should be known everywhere, so we're keeping it.
|
|
|
// But we started it, and it should be known everywhere, so we're keeping it.
|
|
|
contacts = preContacts.map((contact) => { |
|
|
contacts = preContacts.map((contact) => { |
|
|
if (contact.contactMethods) { |
|
|
if (contact.contactMethods) { |
|
|
contact.contactMethods = parseJsonField(contact.contactMethods, []) as ContactMethod[]; |
|
|
contact.contactMethods = parseJsonField( |
|
|
|
|
|
contact.contactMethods, |
|
|
|
|
|
[], |
|
|
|
|
|
) as ContactMethod[]; |
|
|
} |
|
|
} |
|
|
return contact; |
|
|
return contact; |
|
|
}); |
|
|
}); |
|
@ -212,7 +227,7 @@ export async function getSqliteContacts(): Promise<Contact[]> { |
|
|
* |
|
|
* |
|
|
* This function connects to the Dexie database and retrieves all settings |
|
|
* This function connects to the Dexie database and retrieves all settings |
|
|
* records. |
|
|
* records. |
|
|
* |
|
|
* |
|
|
* Settings include both master settings (id=1) and account-specific settings |
|
|
* Settings include both master settings (id=1) and account-specific settings |
|
|
* that override the master settings for particular user accounts. |
|
|
* that override the master settings for particular user accounts. |
|
|
* |
|
|
* |
|
@ -275,16 +290,21 @@ export async function getSqliteSettings(): Promise<Settings[]> { |
|
|
|
|
|
|
|
|
let settings: Settings[] = []; |
|
|
let settings: Settings[] = []; |
|
|
if (result?.values?.length) { |
|
|
if (result?.values?.length) { |
|
|
const presettings = |
|
|
const presettings = mapColumnsToValues( |
|
|
mapColumnsToValues(result.columns, result.values) as Settings[]; |
|
|
result.columns, |
|
|
|
|
|
result.values, |
|
|
|
|
|
) as Settings[]; |
|
|
// This is redundant since absurd-sql auto-parses JSON strings to objects.
|
|
|
// This is redundant since absurd-sql auto-parses JSON strings to objects.
|
|
|
// But we started it, and it should be known everywhere, so we're keeping it.
|
|
|
// But we started it, and it should be known everywhere, so we're keeping it.
|
|
|
settings = presettings.map((setting) => { |
|
|
settings = presettings.map((setting) => { |
|
|
if (setting.searchBoxes) { |
|
|
if (setting.searchBoxes) { |
|
|
setting.searchBoxes = parseJsonField(setting.searchBoxes, []) as Array<{ name: string, bbox: BoundingBox }>; |
|
|
setting.searchBoxes = parseJsonField( |
|
|
|
|
|
setting.searchBoxes, |
|
|
|
|
|
[], |
|
|
|
|
|
) as Array<{ name: string; bbox: BoundingBox }>; |
|
|
} |
|
|
} |
|
|
return setting; |
|
|
return setting; |
|
|
}) |
|
|
}); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
logger.info( |
|
|
logger.info( |
|
@ -557,7 +577,9 @@ function compareSettings( |
|
|
|
|
|
|
|
|
// Find settings that exist in Dexie but not in SQLite
|
|
|
// Find settings that exist in Dexie but not in SQLite
|
|
|
for (const dexieSetting of dexieSettings) { |
|
|
for (const dexieSetting of dexieSettings) { |
|
|
const sqliteSetting = sqliteSettings.find((s) => s.accountDid == dexieSetting.accountDid); |
|
|
const sqliteSetting = sqliteSettings.find( |
|
|
|
|
|
(s) => s.accountDid == dexieSetting.accountDid, |
|
|
|
|
|
); |
|
|
if (!sqliteSetting) { |
|
|
if (!sqliteSetting) { |
|
|
added.push(dexieSetting); |
|
|
added.push(dexieSetting); |
|
|
} else if (!settingsEqual(dexieSetting, sqliteSetting)) { |
|
|
} else if (!settingsEqual(dexieSetting, sqliteSetting)) { |
|
@ -569,7 +591,9 @@ function compareSettings( |
|
|
|
|
|
|
|
|
// Find settings that exist in SQLite but not in Dexie
|
|
|
// Find settings that exist in SQLite but not in Dexie
|
|
|
for (const sqliteSetting of sqliteSettings) { |
|
|
for (const sqliteSetting of sqliteSettings) { |
|
|
const dexieSetting = dexieSettings.find((s) => s.accountDid == sqliteSetting.accountDid); |
|
|
const dexieSetting = dexieSettings.find( |
|
|
|
|
|
(s) => s.accountDid == sqliteSetting.accountDid, |
|
|
|
|
|
); |
|
|
if (!dexieSetting) { |
|
|
if (!dexieSetting) { |
|
|
missing.push(sqliteSetting); |
|
|
missing.push(sqliteSetting); |
|
|
} |
|
|
} |
|
@ -654,22 +678,29 @@ function compareAccounts(dexieAccounts: Account[], sqliteDids: string[]) { |
|
|
* ``` |
|
|
* ``` |
|
|
*/ |
|
|
*/ |
|
|
function contactsEqual(contact1: Contact, contact2: Contact): boolean { |
|
|
function contactsEqual(contact1: Contact, contact2: Contact): boolean { |
|
|
const ifEmpty = (arg: any, def: any) => !!arg ? arg : def; |
|
|
const ifEmpty = (arg: any, def: any) => (arg ? arg : def); |
|
|
const contact1Methods = |
|
|
const contact1Methods = |
|
|
contact1.contactMethods && Array.isArray(contact1.contactMethods) && contact1.contactMethods.length > 0 |
|
|
contact1.contactMethods && |
|
|
? JSON.stringify(contact1.contactMethods) |
|
|
Array.isArray(contact1.contactMethods) && |
|
|
: "[]"; |
|
|
contact1.contactMethods.length > 0 |
|
|
|
|
|
? JSON.stringify(contact1.contactMethods) |
|
|
|
|
|
: "[]"; |
|
|
const contact2Methods = |
|
|
const contact2Methods = |
|
|
contact2.contactMethods && Array.isArray(contact2.contactMethods) && contact2.contactMethods.length > 0 |
|
|
contact2.contactMethods && |
|
|
? JSON.stringify(contact2.contactMethods) |
|
|
Array.isArray(contact2.contactMethods) && |
|
|
: "[]"; |
|
|
contact2.contactMethods.length > 0 |
|
|
|
|
|
? JSON.stringify(contact2.contactMethods) |
|
|
|
|
|
: "[]"; |
|
|
return ( |
|
|
return ( |
|
|
ifEmpty(contact1.did, "") == ifEmpty(contact2.did, "") && |
|
|
ifEmpty(contact1.did, "") == ifEmpty(contact2.did, "") && |
|
|
ifEmpty(contact1.name, "") == ifEmpty(contact2.name, "") && |
|
|
ifEmpty(contact1.name, "") == ifEmpty(contact2.name, "") && |
|
|
ifEmpty(contact1.notes, "") == ifEmpty(contact2.notes, "") && |
|
|
ifEmpty(contact1.notes, "") == ifEmpty(contact2.notes, "") && |
|
|
ifEmpty(contact1.profileImageUrl, "") == ifEmpty(contact2.profileImageUrl, "") && |
|
|
ifEmpty(contact1.profileImageUrl, "") == |
|
|
ifEmpty(contact1.publicKeyBase64, "") == ifEmpty(contact2.publicKeyBase64, "") && |
|
|
ifEmpty(contact2.profileImageUrl, "") && |
|
|
ifEmpty(contact1.nextPubKeyHashB64, "") == ifEmpty(contact2.nextPubKeyHashB64, "") && |
|
|
ifEmpty(contact1.publicKeyBase64, "") == |
|
|
|
|
|
ifEmpty(contact2.publicKeyBase64, "") && |
|
|
|
|
|
ifEmpty(contact1.nextPubKeyHashB64, "") == |
|
|
|
|
|
ifEmpty(contact2.nextPubKeyHashB64, "") && |
|
|
!!contact1.seesMe == !!contact2.seesMe && |
|
|
!!contact1.seesMe == !!contact2.seesMe && |
|
|
!!contact1.registered == !!contact2.registered && |
|
|
!!contact1.registered == !!contact2.registered && |
|
|
contact1Methods == contact2Methods |
|
|
contact1Methods == contact2Methods |
|
@ -762,18 +793,21 @@ function settingsEqual(settings1: Settings, settings2: Settings): boolean { |
|
|
* } |
|
|
* } |
|
|
* ``` |
|
|
* ``` |
|
|
*/ |
|
|
*/ |
|
|
function accountsEqual(account1: Account, account2: Account): boolean { |
|
|
//
|
|
|
return ( |
|
|
// unused
|
|
|
account1.id === account2.id && |
|
|
//
|
|
|
account1.dateCreated === account2.dateCreated && |
|
|
// function accountsEqual(account1: Account, account2: Account): boolean {
|
|
|
account1.derivationPath === account2.derivationPath && |
|
|
// return (
|
|
|
account1.did === account2.did && |
|
|
// account1.id === account2.id &&
|
|
|
account1.identity === account2.identity && |
|
|
// account1.dateCreated === account2.dateCreated &&
|
|
|
account1.mnemonic === account2.mnemonic && |
|
|
// account1.derivationPath === account2.derivationPath &&
|
|
|
account1.passkeyCredIdHex === account2.passkeyCredIdHex && |
|
|
// account1.did === account2.did &&
|
|
|
account1.publicKeyHex === account2.publicKeyHex |
|
|
// account1.identity === account2.identity &&
|
|
|
); |
|
|
// account1.mnemonic === account2.mnemonic &&
|
|
|
} |
|
|
// account1.passkeyCredIdHex === account2.passkeyCredIdHex &&
|
|
|
|
|
|
// account1.publicKeyHex === account2.publicKeyHex
|
|
|
|
|
|
// );
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* Generates YAML-formatted comparison data |
|
|
* Generates YAML-formatted comparison data |
|
@ -801,58 +835,64 @@ export function generateComparisonYaml(comparison: DataComparison): string { |
|
|
const yaml = { |
|
|
const yaml = { |
|
|
summary: { |
|
|
summary: { |
|
|
dexieContacts: comparison.dexieContacts.length, |
|
|
dexieContacts: comparison.dexieContacts.length, |
|
|
sqliteContacts: comparison.sqliteContacts.filter(c => c.did).length, |
|
|
sqliteContacts: comparison.sqliteContacts.filter((c) => c.did).length, |
|
|
dexieSettings: comparison.dexieSettings.length, |
|
|
dexieSettings: comparison.dexieSettings.length, |
|
|
sqliteSettings: comparison.sqliteSettings.filter(s => s.accountDid || s.activeDid).length, |
|
|
sqliteSettings: comparison.sqliteSettings.filter( |
|
|
|
|
|
(s) => s.accountDid || s.activeDid, |
|
|
|
|
|
).length, |
|
|
dexieAccounts: comparison.dexieAccounts.length, |
|
|
dexieAccounts: comparison.dexieAccounts.length, |
|
|
sqliteAccounts: comparison.sqliteAccounts.filter(a => a).length, |
|
|
sqliteAccounts: comparison.sqliteAccounts.filter((a) => a).length, |
|
|
}, |
|
|
}, |
|
|
differences: { |
|
|
differences: { |
|
|
contacts: { |
|
|
contacts: { |
|
|
added: comparison.differences.contacts.added.length, |
|
|
added: comparison.differences.contacts.added.length, |
|
|
modified: comparison.differences.contacts.modified.length, |
|
|
modified: comparison.differences.contacts.modified.length, |
|
|
unmodified: comparison.differences.contacts.unmodified.length, |
|
|
unmodified: comparison.differences.contacts.unmodified.length, |
|
|
missing: comparison.differences.contacts.missing.filter(c => c.did).length, |
|
|
missing: comparison.differences.contacts.missing.filter((c) => c.did) |
|
|
|
|
|
.length, |
|
|
}, |
|
|
}, |
|
|
settings: { |
|
|
settings: { |
|
|
added: comparison.differences.settings.added.length, |
|
|
added: comparison.differences.settings.added.length, |
|
|
modified: comparison.differences.settings.modified.length, |
|
|
modified: comparison.differences.settings.modified.length, |
|
|
unmodified: comparison.differences.settings.unmodified.length, |
|
|
unmodified: comparison.differences.settings.unmodified.length, |
|
|
missing: comparison.differences.settings.missing.filter(s => s.accountDid || s.activeDid).length, |
|
|
missing: comparison.differences.settings.missing.filter( |
|
|
|
|
|
(s) => s.accountDid || s.activeDid, |
|
|
|
|
|
).length, |
|
|
}, |
|
|
}, |
|
|
accounts: { |
|
|
accounts: { |
|
|
added: comparison.differences.accounts.added.length, |
|
|
added: comparison.differences.accounts.added.length, |
|
|
unmodified: comparison.differences.accounts.unmodified.length, |
|
|
unmodified: comparison.differences.accounts.unmodified.length, |
|
|
missing: comparison.differences.accounts.missing.filter(a => a).length, |
|
|
missing: comparison.differences.accounts.missing.filter((a) => a) |
|
|
|
|
|
.length, |
|
|
}, |
|
|
}, |
|
|
}, |
|
|
}, |
|
|
details: { |
|
|
details: { |
|
|
contacts: { |
|
|
contacts: { |
|
|
dexie: comparison.dexieContacts.map((c) => ({ |
|
|
dexie: comparison.dexieContacts.map((c) => ({ |
|
|
did: c.did, |
|
|
did: c.did, |
|
|
name: c.name || '<empty>', |
|
|
name: c.name || "<empty>", |
|
|
contactMethods: (c.contactMethods || []).length, |
|
|
contactMethods: (c.contactMethods || []).length, |
|
|
})), |
|
|
})), |
|
|
sqlite: comparison.sqliteContacts |
|
|
sqlite: comparison.sqliteContacts |
|
|
.filter(c => c.did) |
|
|
.filter((c) => c.did) |
|
|
.map((c) => ({ |
|
|
.map((c) => ({ |
|
|
did: c.did, |
|
|
did: c.did, |
|
|
name: c.name || '<empty>', |
|
|
name: c.name || "<empty>", |
|
|
contactMethods: (c.contactMethods || []).length, |
|
|
contactMethods: (c.contactMethods || []).length, |
|
|
})), |
|
|
})), |
|
|
}, |
|
|
}, |
|
|
settings: { |
|
|
settings: { |
|
|
dexie: comparison.dexieSettings.map((s) => ({ |
|
|
dexie: comparison.dexieSettings.map((s) => ({ |
|
|
id: s.id, |
|
|
id: s.id, |
|
|
type: s.id === MASTER_SETTINGS_KEY ? 'master' : 'account', |
|
|
type: s.id === MASTER_SETTINGS_KEY ? "master" : "account", |
|
|
did: s.activeDid || s.accountDid, |
|
|
did: s.activeDid || s.accountDid, |
|
|
isRegistered: s.isRegistered || false, |
|
|
isRegistered: s.isRegistered || false, |
|
|
})), |
|
|
})), |
|
|
sqlite: comparison.sqliteSettings |
|
|
sqlite: comparison.sqliteSettings |
|
|
.filter(s => s.accountDid || s.activeDid) |
|
|
.filter((s) => s.accountDid || s.activeDid) |
|
|
.map((s) => ({ |
|
|
.map((s) => ({ |
|
|
id: s.id, |
|
|
id: s.id, |
|
|
type: s.id === MASTER_SETTINGS_KEY ? 'master' : 'account', |
|
|
type: s.id === MASTER_SETTINGS_KEY ? "master" : "account", |
|
|
did: s.activeDid || s.accountDid, |
|
|
did: s.activeDid || s.accountDid, |
|
|
isRegistered: s.isRegistered || false, |
|
|
isRegistered: s.isRegistered || false, |
|
|
})), |
|
|
})), |
|
@ -1039,20 +1079,22 @@ export async function migrateSettings(): Promise<MigrationResult> { |
|
|
dexieSettings: dexieSettings.length, |
|
|
dexieSettings: dexieSettings.length, |
|
|
}); |
|
|
}); |
|
|
const platformService = PlatformServiceFactory.getInstance(); |
|
|
const platformService = PlatformServiceFactory.getInstance(); |
|
|
|
|
|
|
|
|
// Create an array of promises for all settings migrations
|
|
|
// Create an array of promises for all settings migrations
|
|
|
const migrationPromises = dexieSettings.map(async (setting) => { |
|
|
const migrationPromises = dexieSettings.map(async (setting) => { |
|
|
logger.info("[MigrationService] Starting to migrate settings", setting); |
|
|
logger.info("[MigrationService] Starting to migrate settings", setting); |
|
|
let sqliteSettingRaw: { columns: string[], values: unknown[][] } | undefined; |
|
|
let sqliteSettingRaw: |
|
|
|
|
|
| { columns: string[]; values: unknown[][] } |
|
|
|
|
|
| undefined; |
|
|
if (!setting.accountDid) { |
|
|
if (!setting.accountDid) { |
|
|
sqliteSettingRaw = await platformService.dbQuery( |
|
|
sqliteSettingRaw = await platformService.dbQuery( |
|
|
"SELECT * FROM settings WHERE accountDid is null" |
|
|
"SELECT * FROM settings WHERE accountDid is null", |
|
|
); |
|
|
); |
|
|
} else { |
|
|
} else { |
|
|
sqliteSettingRaw = await platformService.dbQuery( |
|
|
sqliteSettingRaw = await platformService.dbQuery( |
|
|
"SELECT * FROM settings WHERE accountDid = ?", |
|
|
"SELECT * FROM settings WHERE accountDid = ?", |
|
|
[setting.accountDid] |
|
|
[setting.accountDid], |
|
|
) |
|
|
); |
|
|
} |
|
|
} |
|
|
logger.info("[MigrationService] Migrating one set of settings:", { |
|
|
logger.info("[MigrationService] Migrating one set of settings:", { |
|
|
setting, |
|
|
setting, |
|
@ -1060,9 +1102,11 @@ export async function migrateSettings(): Promise<MigrationResult> { |
|
|
}); |
|
|
}); |
|
|
if (sqliteSettingRaw?.values?.length) { |
|
|
if (sqliteSettingRaw?.values?.length) { |
|
|
// should cover the master settings, were accountDid is null
|
|
|
// should cover the master settings, were accountDid is null
|
|
|
const sqliteSettings = mapColumnsToValues(sqliteSettingRaw.columns, sqliteSettingRaw.values) as unknown as Settings[]; |
|
|
const sqliteSettings = mapColumnsToValues( |
|
|
|
|
|
sqliteSettingRaw.columns, |
|
|
|
|
|
sqliteSettingRaw.values, |
|
|
|
|
|
) as unknown as Settings[]; |
|
|
const sqliteSetting = sqliteSettings[0]; |
|
|
const sqliteSetting = sqliteSettings[0]; |
|
|
console.log('sqliteSetting', sqliteSetting) |
|
|
|
|
|
let conditional: string; |
|
|
let conditional: string; |
|
|
let preparams: unknown[]; |
|
|
let preparams: unknown[]; |
|
|
if (!setting.accountDid) { |
|
|
if (!setting.accountDid) { |
|
@ -1076,16 +1120,16 @@ export async function migrateSettings(): Promise<MigrationResult> { |
|
|
sqliteSetting as unknown as Record<string, unknown>, |
|
|
sqliteSetting as unknown as Record<string, unknown>, |
|
|
"settings", |
|
|
"settings", |
|
|
conditional, |
|
|
conditional, |
|
|
preparams |
|
|
preparams, |
|
|
); |
|
|
); |
|
|
await platformService.dbExec(sql, params) |
|
|
await platformService.dbExec(sql, params); |
|
|
result.settingsMigrated++; |
|
|
result.settingsMigrated++; |
|
|
} else { |
|
|
} else { |
|
|
// insert new setting
|
|
|
// insert new setting
|
|
|
delete setting.activeDid; // ensure we don't set the activeDid (since master settings are an update and don't hit this case)
|
|
|
delete setting.activeDid; // ensure we don't set the activeDid (since master settings are an update and don't hit this case)
|
|
|
const { sql, params } = generateInsertStatement( |
|
|
const { sql, params } = generateInsertStatement( |
|
|
setting as unknown as Record<string, unknown>, |
|
|
setting as unknown as Record<string, unknown>, |
|
|
"settings" |
|
|
"settings", |
|
|
); |
|
|
); |
|
|
await platformService.dbExec(sql, params); |
|
|
await platformService.dbExec(sql, params); |
|
|
result.settingsMigrated++; |
|
|
result.settingsMigrated++; |
|
@ -1094,12 +1138,19 @@ export async function migrateSettings(): Promise<MigrationResult> { |
|
|
|
|
|
|
|
|
// Wait for all migrations to complete
|
|
|
// Wait for all migrations to complete
|
|
|
const updatedSettings = await Promise.all(migrationPromises); |
|
|
const updatedSettings = await Promise.all(migrationPromises); |
|
|
|
|
|
|
|
|
logger.info("[MigrationService] Finished migrating settings", updatedSettings, result); |
|
|
logger.info( |
|
|
|
|
|
"[MigrationService] Finished migrating settings", |
|
|
|
|
|
updatedSettings, |
|
|
|
|
|
result, |
|
|
|
|
|
); |
|
|
|
|
|
|
|
|
return result; |
|
|
return result; |
|
|
} catch (error) { |
|
|
} catch (error) { |
|
|
logger.error("[MigrationService] Complete settings migration failed:", error); |
|
|
logger.error( |
|
|
|
|
|
"[MigrationService] Complete settings migration failed:", |
|
|
|
|
|
error, |
|
|
|
|
|
); |
|
|
const errorMessage = `Settings migration failed: ${error}`; |
|
|
const errorMessage = `Settings migration failed: ${error}`; |
|
|
result.errors.push(errorMessage); |
|
|
result.errors.push(errorMessage); |
|
|
result.success = false; |
|
|
result.success = false; |
|
@ -1158,12 +1209,17 @@ export async function migrateAccounts(): Promise<MigrationResult> { |
|
|
|
|
|
|
|
|
// Group accounts by DID and keep only the most recent one
|
|
|
// Group accounts by DID and keep only the most recent one
|
|
|
const accountsByDid = new Map<string, Account>(); |
|
|
const accountsByDid = new Map<string, Account>(); |
|
|
dexieAccounts.forEach(account => { |
|
|
dexieAccounts.forEach((account) => { |
|
|
const existingAccount = accountsByDid.get(account.did); |
|
|
const existingAccount = accountsByDid.get(account.did); |
|
|
if (!existingAccount || new Date(account.dateCreated) > new Date(existingAccount.dateCreated)) { |
|
|
if ( |
|
|
|
|
|
!existingAccount || |
|
|
|
|
|
new Date(account.dateCreated) > new Date(existingAccount.dateCreated) |
|
|
|
|
|
) { |
|
|
accountsByDid.set(account.did, account); |
|
|
accountsByDid.set(account.did, account); |
|
|
if (existingAccount) { |
|
|
if (existingAccount) { |
|
|
result.warnings.push(`Found duplicate account for DID ${account.did}, keeping most recent`); |
|
|
result.warnings.push( |
|
|
|
|
|
`Found duplicate account for DID ${account.did}, keeping most recent`, |
|
|
|
|
|
); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
}); |
|
|
}); |
|
@ -1174,30 +1230,34 @@ export async function migrateAccounts(): Promise<MigrationResult> { |
|
|
// Check if account already exists
|
|
|
// Check if account already exists
|
|
|
const existingResult = await platformService.dbQuery( |
|
|
const existingResult = await platformService.dbQuery( |
|
|
"SELECT did FROM accounts WHERE did = ?", |
|
|
"SELECT did FROM accounts WHERE did = ?", |
|
|
[did] |
|
|
[did], |
|
|
); |
|
|
); |
|
|
|
|
|
|
|
|
if (existingResult?.values?.length) { |
|
|
if (existingResult?.values?.length) { |
|
|
result.warnings.push(`Account with DID ${did} already exists, skipping`); |
|
|
result.warnings.push( |
|
|
|
|
|
`Account with DID ${did} already exists, skipping`, |
|
|
|
|
|
); |
|
|
continue; |
|
|
continue; |
|
|
} |
|
|
} |
|
|
if (account.mnemonic) { |
|
|
if (account.mnemonic) { |
|
|
await importFromMnemonic(account.mnemonic, account.derivationPath); |
|
|
await importFromMnemonic(account.mnemonic, account.derivationPath); |
|
|
result.accountsMigrated++; |
|
|
result.accountsMigrated++; |
|
|
} else { |
|
|
} else { |
|
|
result.errors.push(`Account with DID ${did} has no mnemonic, skipping`); |
|
|
result.errors.push( |
|
|
|
|
|
`Account with DID ${did} has no mnemonic, skipping`, |
|
|
|
|
|
); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
logger.info("[MigrationService] Successfully migrated account", { |
|
|
logger.info("[MigrationService] Successfully migrated account", { |
|
|
did, |
|
|
did, |
|
|
dateCreated: account.dateCreated |
|
|
dateCreated: account.dateCreated, |
|
|
}); |
|
|
}); |
|
|
} catch (error) { |
|
|
} catch (error) { |
|
|
const errorMessage = `Failed to migrate account ${did}: ${error}`; |
|
|
const errorMessage = `Failed to migrate account ${did}: ${error}`; |
|
|
result.errors.push(errorMessage); |
|
|
result.errors.push(errorMessage); |
|
|
logger.error("[MigrationService] Account migration failed:", { |
|
|
logger.error("[MigrationService] Account migration failed:", { |
|
|
error, |
|
|
error, |
|
|
did |
|
|
did, |
|
|
}); |
|
|
}); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
@ -1211,7 +1271,10 @@ export async function migrateAccounts(): Promise<MigrationResult> { |
|
|
const errorMessage = `Account migration failed: ${error}`; |
|
|
const errorMessage = `Account migration failed: ${error}`; |
|
|
result.errors.push(errorMessage); |
|
|
result.errors.push(errorMessage); |
|
|
result.success = false; |
|
|
result.success = false; |
|
|
logger.error("[MigrationService] Complete account migration failed:", error); |
|
|
logger.error( |
|
|
|
|
|
"[MigrationService] Complete account migration failed:", |
|
|
|
|
|
error, |
|
|
|
|
|
); |
|
|
return result; |
|
|
return result; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|