From 069e6bf62adc7b9465069265cf0cb0faa2e995e9 Mon Sep 17 00:00:00 2001 From: Robert Vitonsky Date: Wed, 19 Jun 2024 21:21:14 +0200 Subject: [PATCH] fix: repair broken config once migration completed --- .../ConfigStorage/ConfigStorage.migrations.ts | 24 +++++++++++++++++-- .../PersistentMigrationsExecutor.ts | 5 ++++ src/lib/migrations/createMigrationTask.ts | 12 +++++++++- 3 files changed, 38 insertions(+), 3 deletions(-) diff --git a/src/app/ConfigStorage/ConfigStorage.migrations.ts b/src/app/ConfigStorage/ConfigStorage.migrations.ts index a65febf8..cc1d9b85 100644 --- a/src/app/ConfigStorage/ConfigStorage.migrations.ts +++ b/src/app/ConfigStorage/ConfigStorage.migrations.ts @@ -1,7 +1,9 @@ import browser from 'webextension-polyfill'; -import { DEFAULT_TRANSLATOR, DEFAULT_TTS } from '../../config'; +import { DEFAULT_TRANSLATOR, DEFAULT_TTS, defaultConfig } from '../../config'; import { createMigrationTask, Migration } from '../../lib/migrations/createMigrationTask'; +import { decodeStruct } from '../../lib/types'; +import { AppConfig } from '../../types/runtime'; const migrations: Migration[] = [ { @@ -131,6 +133,24 @@ const migrations: Migration[] = [ await browser.storage.local.set({ [storageName]: updatedConfig }); }, }, + { + version: 7, + async migrate() { + // Empty migration, to bump migration number and to trigger hook for repair config + }, + }, ]; -export const ConfigStorageMigration = createMigrationTask(migrations); +export const ConfigStorageMigration = createMigrationTask(migrations, { + onComplete: async () => { + // Repair config if necessary + const storageName = 'appConfig'; + const { [storageName]: config } = await browser.storage.local.get(storageName); + + const { errors } = decodeStruct(AppConfig, config); + if (errors === null) return; + + console.warn('Config object is invalid, fallback to default config', errors); + await browser.storage.local.set({ [storageName]: defaultConfig }); + }, +}); diff --git a/src/lib/migrations/MigrationsExecutor/PersistentMigrationsExecutor.ts b/src/lib/migrations/MigrationsExecutor/PersistentMigrationsExecutor.ts index 97f549da..f76ed96c 100644 --- a/src/lib/migrations/MigrationsExecutor/PersistentMigrationsExecutor.ts +++ b/src/lib/migrations/MigrationsExecutor/PersistentMigrationsExecutor.ts @@ -76,6 +76,11 @@ export class PersistentMigrationsExecutor { await migration.migrate(currentVersion, latestVersion); + const { hooks = {} } = migration; + if (hooks.onComplete) { + await hooks.onComplete(); + } + // Update storage version migrationsVersions[name] = latestVersion; await this.storage.setMigrationsVersions(migrationsVersions); diff --git a/src/lib/migrations/createMigrationTask.ts b/src/lib/migrations/createMigrationTask.ts index 9e0980d1..bd38e414 100644 --- a/src/lib/migrations/createMigrationTask.ts +++ b/src/lib/migrations/createMigrationTask.ts @@ -13,6 +13,10 @@ export type Migration = { migrate: (previousVersion: number) => Promise; }; +export type MigrationHooks = { + onComplete?: () => Promise; +}; + /** * Task to migrate from one version to another * Task may execute several migrations, so it may take a time @@ -29,12 +33,17 @@ export type MigrationTask = { * WARNING: previous version may contain `0` in case when data structure run migration first time */ migrate: (previousVersion: number, currentVersion: number) => Promise; + + hooks?: MigrationHooks; }; /** * Build migration task from migrations list */ -export const createMigrationTask = (migrations: Migration[]): MigrationTask => { +export const createMigrationTask = ( + migrations: Migration[], + hooks?: MigrationHooks, +): MigrationTask => { const sortedMigrations = migrations.sort( (migration1, migration2) => migration1.version - migration2.version, ); @@ -47,6 +56,7 @@ export const createMigrationTask = (migrations: Migration[]): MigrationTask => { return { version: lastMigrationVersion, + hooks, migrate: async (fromVersion: number, toVersion: number) => { const migrationsToApply = sortedMigrations.filter( (migration) =>