diff --git a/src/helpers/db/db.js b/src/helpers/db/db.js index eabeff86..1162dd22 100644 --- a/src/helpers/db/db.js +++ b/src/helpers/db/db.js @@ -7,7 +7,7 @@ import get from 'lodash/get' import { decodeAccountsModel, encodeAccountsModel } from 'reducers/accounts' -type DBKey = 'settings' | 'accounts' | 'countervalues' | 'user' +type DBKey = 'settings' | 'accounts' | 'countervalues' | 'user' | 'migrations' const encryptionKey = {} diff --git a/src/migrations/README.md b/src/migrations/README.md new file mode 100644 index 00000000..8b856e7f --- /dev/null +++ b/src/migrations/README.md @@ -0,0 +1,15 @@ +## Migrations system + +this folder contains all the migration scripts. + +Migrations works with a nonce, number we increment. +An app instance saves the nonce after running the migrations. +To know what migrations need to be performed, we simply need to run all migrations that have an index higher that this nonce and then save it. + +The migration are run before the app starts and the idea is you need to perform everything on the db and maybe on the libcore with the commands. + +### Add a migration + +To add a migration, simply add one more item in the migrations array of index.js + +If a migration throw an exception, it's considered to be a critical error and app will crash forever. so make sure you only throw if necessary (user can always Hard Reset at the end) diff --git a/src/migrations/index.js b/src/migrations/index.js new file mode 100644 index 00000000..cba381f7 --- /dev/null +++ b/src/migrations/index.js @@ -0,0 +1,59 @@ +// @flow + +import logger from 'logger' +import db from 'helpers/db' +import { delay } from 'helpers/promise' +import type { Migration } from './types' + +export const migrations: Migration[] = [ + /* + // TODO release when libcore will fix the issue (ensure it does everyting that is needed) + { + doc: 'libcore fixed an important bug on BCH that needs a cache clear', + run: async () => { + // Clear out accounts operations because will need a full refresh + const accounts: mixed = db.get('accounts') + if (accounts && Array.isArray(accounts)) { + for (const acc of accounts) { + if (acc && typeof acc === 'object') { + acc.operations = [] + acc.pendingOperations = [] + } + } + db.set('accounts', accounts) + } + + db.cleanCache() + // await delay(500) + }, + }, + */ +] + +// Logic to run all the migrations based on what was not yet run: +export const runMigrations = async (): Promise => { + const current = db.get('migrations') + let { nonce } = current || { nonce: migrations.length } + const outdated = migrations.length - nonce + + if (!outdated) { + if (!current) { + db.set('migrations', { nonce }) + } + return + } + + try { + await delay(1000) // wait a bit the logger to be ready. + + while (nonce < migrations.length) { + const m = migrations[nonce] + logger.log(`migration ${nonce}: ${m.doc}`) + await m.run() + nonce++ + } + logger.log(`${outdated} migration(s) performed.`) + } finally { + db.set('migrations', { nonce }) + } +} diff --git a/src/migrations/types.js b/src/migrations/types.js new file mode 100644 index 00000000..0b437bf2 --- /dev/null +++ b/src/migrations/types.js @@ -0,0 +1,6 @@ +// @flow + +export type Migration = { + doc: string, + run: () => Promise, +} diff --git a/src/renderer/init.js b/src/renderer/init.js index 3b63d849..d5214e7d 100644 --- a/src/renderer/init.js +++ b/src/renderer/init.js @@ -7,6 +7,7 @@ import { render } from 'react-dom' import { AppContainer } from 'react-hot-loader' import createHistory from 'history/createHashHistory' import moment from 'moment' +import { runMigrations } from 'migrations' import createStore from 'renderer/createStore' import events from 'renderer/events' @@ -42,6 +43,8 @@ async function init() { await hardReset() } + await runMigrations() + // Init db with defaults if needed db.init('settings', {})