@ -24,7 +24,7 @@
import { PlatformServiceFactory } from "./PlatformServiceFactory" ;
import { db , accountsDBPromise } from "../db/index" ;
import { Contact , ContactMethod } from "../db/tables/contacts" ;
import { Settings } from "../db/tables/settings" ;
import { Settings , MASTER_SETTINGS_KEY } from "../db/tables/settings" ;
import { Account } from "../db/tables/accounts" ;
import { logger } from "../utils/logger" ;
import { parseJsonField } from "../db/databaseUtil" ;
@ -285,42 +285,47 @@ export async function getSqliteSettings(): Promise<Settings[]> {
return [ ] ;
}
const settings = result . values . map ( ( row ) = > {
const setting = parseJsonField ( row , { } ) as Settings ;
return {
id : setting.id ,
accountDid : setting.accountDid || "" ,
activeDid : setting.activeDid || "" ,
apiServer : setting.apiServer || "" ,
filterFeedByNearby : setting.filterFeedByNearby || false ,
filterFeedByVisible : setting.filterFeedByVisible || false ,
finishedOnboarding : setting.finishedOnboarding || false ,
firstName : setting.firstName || "" ,
hideRegisterPromptOnNewContact :
setting . hideRegisterPromptOnNewContact || false ,
isRegistered : setting.isRegistered || false ,
lastName : setting.lastName || "" ,
lastAckedOfferToUserJwtId : setting.lastAckedOfferToUserJwtId || "" ,
lastAckedOfferToUserProjectsJwtId :
setting . lastAckedOfferToUserProjectsJwtId || "" ,
lastNotifiedClaimId : setting.lastNotifiedClaimId || "" ,
lastViewedClaimId : setting.lastViewedClaimId || "" ,
notifyingNewActivityTime : setting.notifyingNewActivityTime || "" ,
notifyingReminderMessage : setting.notifyingReminderMessage || "" ,
notifyingReminderTime : setting.notifyingReminderTime || "" ,
partnerApiServer : setting.partnerApiServer || "" ,
passkeyExpirationMinutes : setting.passkeyExpirationMinutes ,
profileImageUrl : setting.profileImageUrl || "" ,
searchBoxes : parseJsonField ( setting . searchBoxes , [ ] ) ,
showContactGivesInline : setting.showContactGivesInline || false ,
showGeneralAdvanced : setting.showGeneralAdvanced || false ,
showShortcutBvc : setting.showShortcutBvc || false ,
vapid : setting.vapid || "" ,
warnIfProdServer : setting.warnIfProdServer || false ,
warnIfTestServer : setting.warnIfTestServer || false ,
webPushServer : setting.webPushServer || "" ,
} as Settings ;
} ) ;
const settings = result . values
. map ( ( row ) = > {
const setting = parseJsonField ( row , { } ) as Settings ;
return {
id : setting.id ,
accountDid : setting.accountDid || null ,
activeDid : setting.activeDid || null ,
apiServer : setting.apiServer || "" ,
filterFeedByNearby : setting.filterFeedByNearby || false ,
filterFeedByVisible : setting.filterFeedByVisible || false ,
finishedOnboarding : setting.finishedOnboarding || false ,
firstName : setting.firstName || "" ,
hideRegisterPromptOnNewContact :
setting . hideRegisterPromptOnNewContact || false ,
isRegistered : setting.isRegistered || false ,
lastName : setting.lastName || "" ,
lastAckedOfferToUserJwtId : setting.lastAckedOfferToUserJwtId || "" ,
lastAckedOfferToUserProjectsJwtId :
setting . lastAckedOfferToUserProjectsJwtId || "" ,
lastNotifiedClaimId : setting.lastNotifiedClaimId || "" ,
lastViewedClaimId : setting.lastViewedClaimId || "" ,
notifyingNewActivityTime : setting.notifyingNewActivityTime || "" ,
notifyingReminderMessage : setting.notifyingReminderMessage || "" ,
notifyingReminderTime : setting.notifyingReminderTime || "" ,
partnerApiServer : setting.partnerApiServer || "" ,
passkeyExpirationMinutes : setting.passkeyExpirationMinutes ,
profileImageUrl : setting.profileImageUrl || "" ,
searchBoxes : parseJsonField ( setting . searchBoxes , [ ] ) ,
showContactGivesInline : setting.showContactGivesInline || false ,
showGeneralAdvanced : setting.showGeneralAdvanced || false ,
showShortcutBvc : setting.showShortcutBvc || false ,
vapid : setting.vapid || "" ,
warnIfProdServer : setting.warnIfProdServer || false ,
warnIfTestServer : setting.warnIfTestServer || false ,
webPushServer : setting.webPushServer || "" ,
} as Settings ;
} )
. filter ( ( setting ) = > {
// Only include settings that have either accountDid or activeDid set
return setting . accountDid || setting . activeDid ;
} ) ;
logger . info (
` [MigrationService] Retrieved ${ settings . length } settings from SQLite ` ,
@ -837,95 +842,79 @@ function accountsEqual(account1: Account, account2: Account): boolean {
* /
export function generateComparisonYaml ( comparison : DataComparison ) : string {
const yaml = {
comparison : {
summary : {
dexieContacts : comparison.dexieContacts.length ,
sqliteContacts : comparison.sqliteContacts.length ,
dexieSettings : comparison.dexieSettings.length ,
sqliteSettings : comparison.sqliteSettings.length ,
dexieAccounts : comparison.dexieAccounts.length ,
sqliteAccounts : comparison.sqliteAccounts.length ,
summary : {
dexieContacts : comparison.dexieContacts.length ,
sqliteContacts : comparison.sqliteContacts.filter ( c = > c . did ) . length ,
dexieSettings : comparison.dexieSettings.length ,
sqliteSettings : comparison.sqliteSettings.filter ( s = > s . accountDid || s . activeDid ) . length ,
dexieAccounts : comparison.dexieAccounts.length ,
sqliteAccounts : comparison.sqliteAccounts.filter ( a = > a . did ) . length ,
} ,
differences : {
contacts : {
added : comparison.differences.contacts.added.length ,
modified : comparison.differences.contacts.modified.length ,
missing : comparison.differences.contacts.missing.filter ( c = > c . did ) . length ,
} ,
differences : {
contacts : {
added : comparison.differences.contacts.added.length ,
modified : comparison.differences.contacts.modified.length ,
missing : comparison.differences.contacts.missing.length ,
} ,
settings : {
added : comparison.differences.settings.added.length ,
modified : comparison.differences.settings.modified.length ,
missing : comparison.differences.settings.missing.length ,
} ,
accounts : {
added : comparison.differences.accounts.added.length ,
modified : comparison.differences.accounts.modified.length ,
missing : comparison.differences.accounts.missing.length ,
} ,
settings : {
added : comparison.differences.settings.added.length ,
modified : comparison.differences.settings.modified.length ,
missing : comparison.differences.settings.missing.filter ( s = > s . accountDid || s . activeDid ) . length ,
} ,
accounts : {
added : comparison.differences.accounts.added.length ,
modified : comparison.differences.accounts.modified.length ,
missing : comparison.differences.accounts.missing.filter ( a = > a . did ) . length ,
} ,
} ,
details : {
contacts : {
dexie : comparison.dexieContacts.map ( ( c ) = > ( {
did : c.did ,
name : c.name ,
notes : c.notes ,
profileImageUrl : c.profileImageUrl ,
seesMe : c.seesMe ,
registered : c.registered ,
contactMethods : c.contactMethods ,
} ) ) ,
sqlite : comparison.sqliteContacts.map ( ( c ) = > ( {
did : c.did ,
name : c.name ,
notes : c.notes ,
profileImageUrl : c.profileImageUrl ,
seesMe : c.seesMe ,
registered : c.registered ,
contactMethods : c.contactMethods ,
name : c.name || '<empty>' ,
contactMethods : ( c . contactMethods || [ ] ) . length ,
} ) ) ,
sqlite : comparison.sqliteContacts
. filter ( c = > c . did )
. map ( ( c ) = > ( {
did : c.did ,
name : c.name || '<empty>' ,
contactMethods : ( c . contactMethods || [ ] ) . length ,
} ) ) ,
} ,
settings : {
dexie : comparison.dexieSettings.map ( ( s ) = > ( {
id : s.id ,
accountDid : s.accountDid ,
activeDid : s.activeDid ,
firstName : s.firstName ,
isRegistered : s.isRegistered ,
profileImageUrl : s.profileImageUrl ,
showShortcutBvc : s.showShortcutBvc ,
searchBoxes : s.searchBoxes ,
} ) ) ,
sqlite : comparison.sqliteSettings.map ( ( s ) = > ( {
id : s.id ,
accountDid : s.accountDid ,
activeDid : s.activeDid ,
firstName : s.firstName ,
isRegistered : s.isRegistered ,
profileImageUrl : s.profileImageUrl ,
showShortcutBvc : s.showShortcutBvc ,
searchBoxes : s.searchBoxes ,
type : s . id === MASTER_SETTINGS_KEY ? 'master' : 'account' ,
did : s.activeDid || s . accountDid ,
isRegistered : s.isRegistered || false ,
} ) ) ,
sqlite : comparison.sqliteSettings
. filter ( s = > s . accountDid || s . activeDid )
. map ( ( s ) = > ( {
id : s.id ,
type : s . id === MASTER_SETTINGS_KEY ? 'master' : 'account' ,
did : s.activeDid || s . accountDid ,
isRegistered : s.isRegistered || false ,
} ) ) ,
} ,
accounts : {
dexie : comparison.dexieAccounts.map ( ( a ) = > ( {
id : a.id ,
dateCreated : a.dateCreated ,
derivationPath : a.derivationPath ,
did : a.did ,
identity : a.identity ,
mnemonic : a.mnemonic ,
passkeyCredIdHex : a.passkeyCredIdHex ,
publicKeyHex : a.publicKeyHex ,
} ) ) ,
sqlite : comparison.sqliteAccounts.map ( ( a ) = > ( {
id : a.id ,
dateCreated : a.dateCreated ,
derivationPath : a.derivationPath ,
did : a.did ,
identity : a.identity ,
mnemonic : a.mnemonic ,
passkeyCredIdHex : a.passkeyCredIdHex ,
publicKeyHex : a.publicKeyHex ,
hasIdentity : ! ! a . identity ,
hasMnemonic : ! ! a . mnemonic ,
} ) ) ,
sqlite : comparison.sqliteAccounts
. filter ( a = > a . did )
. map ( ( a ) = > ( {
id : a.id ,
did : a.did ,
dateCreated : a.dateCreated ,
hasIdentity : ! ! a . identity ,
hasMnemonic : ! ! a . mnemonic ,
} ) ) ,
} ,
} ,
} ;
@ -1111,29 +1100,45 @@ export async function migrateSettings(
[ dexieSetting . id ] ,
) ;
// Prepare the data object, handling DIDs based on whether this is the master settings
const settingData : Record < string , unknown > = { } ;
fieldsToMigrate . forEach ( ( field ) = > {
if ( dexieSetting [ field as keyof Settings ] !== undefined ) {
settingData [ field ] = dexieSetting [ field as keyof Settings ] ;
}
} ) ;
// Handle DIDs based on whether this is the master settings
if ( dexieSetting . id === MASTER_SETTINGS_KEY ) {
// Master settings should only use activeDid
if ( dexieSetting . activeDid ) {
settingData . activeDid = dexieSetting . activeDid ;
}
// Ensure accountDid is null for master settings
settingData . accountDid = null ;
} else {
// Non-master settings should only use accountDid
if ( dexieSetting . accountDid ) {
settingData . accountDid = dexieSetting . accountDid ;
}
// Ensure activeDid is null for non-master settings
settingData . activeDid = null ;
}
if ( existingResult ? . values ? . length ) {
if ( overwriteExisting ) {
// Update existing setting with only the specified fields
const updateData : Record < string , unknown > = { } ;
fieldsToMigrate . forEach ( ( field ) = > {
if ( dexieSetting [ field as keyof Settings ] !== undefined ) {
updateData [ field ] = dexieSetting [ field as keyof Settings ] ;
}
} ) ;
if ( Object . keys ( updateData ) . length > 0 ) {
const { sql , params } = generateUpdateStatement (
updateData as unknown as Record < string , unknown > ,
"settings" ,
"id = ?" ,
[ dexieSetting . id ] ,
) ;
await platformService . dbExec ( sql , params ) ;
result . settingsMigrated ++ ;
logger . info (
` [MigrationService] Updated settings: ${ dexieSetting . id } ` ,
) ;
}
// Update existing setting
const { sql , params } = generateUpdateStatement (
settingData ,
"settings" ,
"id = ?" ,
[ dexieSetting . id ] ,
) ;
await platformService . dbExec ( sql , params ) ;
result . settingsMigrated ++ ;
logger . info (
` [MigrationService] Updated settings: ${ dexieSetting . id } ` ,
) ;
} else {
result . warnings . push (
` Settings ${ dexieSetting . id } already exists, skipping ` ,
@ -1141,8 +1146,9 @@ export async function migrateSettings(
}
} else {
// Insert new setting
settingData . id = dexieSetting . id ;
const { sql , params } = generateInsertStatement (
dexieSetting as unknown as Record < string , unknown > ,
settingData ,
"settings" ,
) ;
await platformService . dbExec ( sql , params ) ;