diff --git a/server/db/JembaConnManager.js b/server/db/JembaConnManager.js index 8d3e736d..08d4f876 100644 --- a/server/db/JembaConnManager.js +++ b/server/db/JembaConnManager.js @@ -1,9 +1,15 @@ const fs = require('fs-extra'); +const _ = require('lodash'); const ayncExit = new (require('../core/AsyncExit'))();//singleton const { JembaDb, JembaDbThread } = require('./JembaDb'); const log = new (require('../core/AppLogger'))().log;//singleton +const jembaMigrations = { + //'app': require('./jembaMigrations/app'), + 'reader-storage': require('./jembaMigrations/reader-storage'), +}; + let instance = null; //singleton @@ -25,6 +31,8 @@ class JembaConnManager { this.config = config; this._db = {}; + const force = null;//(config.branch == 'development' ? 'last' : null); + for (const dbConfig of this.config.db) { const dbPath = `${this.config.dataDir}/db/${dbConfig.dbName}`; @@ -69,18 +77,91 @@ class JembaConnManager { log(`Open "${dbConfig.dbName}" finish`); //миграции - /*const migs = migrations[dbConfig.poolName]; - if (migs && migs.data.length) { - const applied = await connPool.migrate(migs.data, migs.table, force); + const mig = jembaMigrations[dbConfig.dbName]; + if (mig && mig.data) { + const applied = await this.migrate(dbConn, mig.data, mig.table, force); if (applied.length) - log(`${applied.length} migrations applied to "${dbConfig.poolName}"`); - }*/ + log(`${applied.length} migrations applied to "${dbConfig.dbName}"`); + } this._db[dbConfig.dbName] = dbConn; } this.inited = true; } + async migrate(db, migs, table, force) { + const migrations = _.cloneDeep(migs).sort((a, b) => a.id - b.id); + + if (!migrations.length) { + throw new Error('No migration data'); + } + + migrations.map(migration => { + const data = migration.data; + if (!data.up || !data.down) { + throw new Error(`The ${migration.id}:${migration.name} does not contain 'up' or 'down' instructions`); + } else { + migration.up = data.up; + migration.down = data.down; + } + delete migration.data; + }); + + // Create a database table for migrations meta data if it doesn't exist + // id, name, up, down + await db.create({ + table, + quietIfExists: true, + hash: {field: 'id', type: 'number'} + }); + + // Get the list of already applied migrations + let dbMigrations = await db.select({ + table, + sort: '(a, b) => a.id - b.id' + }); + + const execUpDown = async(items) => { + for (const item of items) { + const action = item[0]; + await db[action](item[1]); + } + }; + + // Undo migrations that exist only in the database but not in migs, + // also undo the last migration if the `force` option was set to `last`. + const lastMigration = migrations[migrations.length - 1]; + for (const migration of dbMigrations.slice().sort((a, b) => b.id - a.id)) { + if (!migrations.some(x => x.id === migration.id) || + (force === 'last' && migration.id === lastMigration.id)) { + await execUpDown(migration.down); + await db.delete({ + table, + where: `@@hash('id', ${db.esc(migration.id)})` + }); + dbMigrations = dbMigrations.filter(x => x.id !== migration.id); + } else { + break; + } + } + + // Apply pending migrations + let applied = []; + const lastMigrationId = dbMigrations.length ? dbMigrations[dbMigrations.length - 1].id : 0; + for (const migration of migrations) { + if (migration.id > lastMigrationId) { + await execUpDown(migration.up); + await db.insert({ + table, + rows: [migration], + }); + applied.push(migration.id); + } + } + + return applied; + } + get db() { return this._db; } diff --git a/server/db/jembaMigrations/reader-storage/001-create.js b/server/db/jembaMigrations/reader-storage/001-create.js new file mode 100644 index 00000000..99e7b9b1 --- /dev/null +++ b/server/db/jembaMigrations/reader-storage/001-create.js @@ -0,0 +1,14 @@ +module.exports = { + up: [ + //CREATE TABLE storage (id TEXT PRIMARY KEY, rev INTEGER, time INTEGER, data TEXT); + ['create', { + table: 'storage', + hash: {field: 'id', type: 'string', depth: 100} + }], + ], + down: [ + ['drop', { + table: 'storage' + }], + ] +}; diff --git a/server/db/jembaMigrations/reader-storage/index.js b/server/db/jembaMigrations/reader-storage/index.js new file mode 100644 index 00000000..85941af0 --- /dev/null +++ b/server/db/jembaMigrations/reader-storage/index.js @@ -0,0 +1,6 @@ +module.exports = { + table: 'migration1', + data: [ + {id: 1, name: 'create', data: require('./001-create')} + ] +} \ No newline at end of file