diff --git a/lib/utils/migration-utils.js b/lib/utils/migration-utils.js --- a/lib/utils/migration-utils.js +++ b/lib/utils/migration-utils.js @@ -192,7 +192,7 @@ threadPermissions.MANAGE_INVITE_LINKS, ]; -type LegacyMigrationManifest> = { +export type LegacyMigrationManifest> = { +[number | string]: (T) => Promise, }; type PersistedState> = T | void; @@ -204,7 +204,7 @@ debug: boolean, ) => Promise>; -type MigrationManifest> = { +export type MigrationManifest> = { +[number | string]: (PersistedState) => Promise<{ +state: T, +ops: StoreOperations, @@ -270,7 +270,7 @@ inboundVersion: number, currentVersion: number, debug: boolean, - handleException: (error: Error, state: T) => T, + handleException?: (error: Error, state: T) => T, ): Promise> { const migrationKeys = [ ...Object.keys(legacyMigrations), @@ -320,7 +320,10 @@ try { await getConfig().sqliteAPI.processDBStoreOperations(dbOps); } catch (exception) { - return handleException(exception, state); + if (handleException) { + return handleException(exception, state); + } + throw exception; } } } diff --git a/native/backup/use-client-backup.js b/native/backup/use-client-backup.js --- a/native/backup/use-client-backup.js +++ b/native/backup/use-client-backup.js @@ -6,10 +6,16 @@ import { accountHasPassword } from 'lib/shared/account-utils.js'; import type { SIWEBackupSecrets } from 'lib/types/siwe-types.js'; import { getContentSigningKey } from 'lib/utils/crypto-utils.js'; +import { runMigrations } from 'lib/utils/migration-utils.js'; import { fetchNativeKeychainCredentials } from '../account/native-credentials.js'; import { commCoreModule } from '../native-modules.js'; -import { persistConfig } from '../redux/persist.js'; +import { defaultState } from '../redux/default-state.js'; +import { + legacyMigrations, + migrations, + persistConfig, +} from '../redux/persist.js'; import { useSelector } from '../redux/redux-utils.js'; type ClientBackup = { @@ -91,10 +97,27 @@ await commCoreModule.restoreBackup(backupSecret); const backupVersion = await commCoreModule.getSyncedDatabaseVersion(); - if (!backupVersion || parseInt(backupVersion) > persistConfig.version) { + const backupVersionNumber = parseInt(backupVersion); + if (!backupVersion || backupVersionNumber > persistConfig.version) { throw new Error(`Incompatible backup version ${backupVersion ?? -1}`); } + console.info('Running backup migrations...'); + await runMigrations( + legacyMigrations, + migrations, + { + ...defaultState, + _persist: { + version: backupVersionNumber, + rehydrated: true, + }, + }, + backupVersionNumber, + persistConfig.version, + process.env.NODE_ENV !== 'production', + ); + console.info('Backup restored.'); }, [currentUserID, loggedIn, setMockCommServicesAuthMetadata]); diff --git a/native/redux/persist.js b/native/redux/persist.js --- a/native/redux/persist.js +++ b/native/redux/persist.js @@ -103,6 +103,8 @@ convertMessageStoreThreadsToNewIDSchema, convertThreadStoreThreadInfosToNewIDSchema, createAsyncMigrate, + type LegacyMigrationManifest, + type MigrationManifest, } from 'lib/utils/migration-utils.js'; import { entries } from 'lib/utils/objects.js'; import { @@ -133,11 +135,12 @@ import { unshimClientDB } from './unshim-utils.js'; import { authoritativeKeyserverID } from '../authoritative-keyserver.js'; import { commCoreModule } from '../native-modules.js'; +import type { NavInfo } from '../navigation/default-state.js'; import { defaultDeviceCameraInfo } from '../types/camera.js'; import { isTaskCancelledError } from '../utils/error-handling.js'; import { defaultURLPrefix } from '../utils/url-utils.js'; -const legacyMigrations = { +const legacyMigrations: LegacyMigrationManifest = { [1]: (state: AppState) => ({ ...state, notifPermissionAlertInfo: defaultAlertInfo, @@ -1305,7 +1308,7 @@ { whitelist: ['reportStore'] }, ); -const migrations = { +const migrations: MigrationManifest = { // This migration doesn't change the store but sets a persisted version // in the DB [75]: (state: AppState) => ({ @@ -1353,4 +1356,11 @@ return storedPersistor; } -export { persistConfig, codeVersion, setPersistor, getPersistor }; +export { + persistConfig, + codeVersion, + setPersistor, + getPersistor, + migrations, + legacyMigrations, +};