UNPKG

moleculer-iam

Version:

Centralized IAM module for moleculer. Including a certified OIDC provider and an Identity provider for user profile, credentials, and custom claims management. Custom claims could be defined/updated by declarative schema which contains claims validation a

206 lines 9.01 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.RDBMSManager = void 0; const tslib_1 = require("tslib"); const kleur = tslib_1.__importStar(require("kleur")); const _ = tslib_1.__importStar(require("lodash")); const readline_1 = tslib_1.__importDefault(require("readline")); const umzug_1 = tslib_1.__importDefault(require("umzug")); const sequelize_1 = require("sequelize"); // ignore deprecation warning for string operator alias // tslint:disable-next-line:no-var-requires require("sequelize/lib/utils/deprecations").noStringOperators = () => { }; // ref: https://sequelize.org/v5/manual/ tslib_1.__exportStar(require("sequelize"), exports); const DontSync = (() => { throw new Error("shall not use sync: try to create migration scripts!"); }); let RDBMSManager = /** @class */ (() => { class RDBMSManager { constructor(props, opts = {}) { this.props = props; this.opts = opts; this.models = new Map(); this.rl = readline_1.default.createInterface(process.stdin, process.stdout); this.logger = props.logger || console; // apply default options const log = opts.sqlLogLevel === "none" ? () => { } : (this.logger[opts.sqlLogLevel || "debug"] || this.logger.debug); const defaults = { logging: (sql) => log(sql), logQueryParameters: true, benchmark: true, migrationLockTimeoutSeconds: 10, operatorsAliases: RDBMSManager.operatorsAliases, }; _.defaultsDeep(opts, defaults); // get sequelize instance const seq = this.seq = new sequelize_1.Sequelize(opts); // create migrator this.migrator = new umzug_1.default({ storage: "sequelize", storageOptions: { sequelize: this.seq, tableName: this.props.migrationTableName || "sequelize", }, migrations: { params: [ this.seq.getQueryInterface(), sequelize_1.Sequelize, ], path: this.props.migrationDirPath || __dirname, }, }); } get sequelize() { return this.seq; } define(name, attr, opts) { const model = this.seq.define(name, attr, opts); model.sync = DontSync; this.models.set(name, model); return model; } getModel(name) { return this.models.get(name); } async migrate(opts) { await this.acquireLock(async () => { const results = await this.migrator.up(opts); for (const r of results) { this.logger.info(`${this.migrationTableLabel}: ${kleur.green(r.file)} migrated`); } }); } async rollback(opts) { // safety bar console.log(kleur.bgRed(` ============================[ROLLBACK COMMAND INVOKED]==================================== ROLLBACK IS A DESTRUCTIVE COMMAND. BE CAREFUL TO NOT TO BEING DEPLOYED AS IS ==========================================================================================`) + "\n"); return new Promise((resolve, reject) => { this.rl.question(`Rollback ${this.migrationTableLabel} with option ${opts ? JSON.stringify(opts) : "(ALL)"}? (yes/y)\n`, async (answer) => { try { if (typeof answer === "string" && ["yes", "y"].includes(answer.toLowerCase())) { await this.acquireLock(async () => { const results = await this.migrator.down(opts); for (const r of results) { this.logger.info(`${this.migrationTableLabel}: ${kleur.yellow(r.file)} rollbacked`); } }); } else { this.logger.info(`${this.migrationTableLabel}: rollback canceled`); } resolve(); } catch (error) { reject(error); } }); }); } async dispose() { await this.releaseLock(); await this.seq.close(); this.rl.close(); } /* migration locking for distributed envrionment */ get lockTableName() { return this.props.migrationTableName + "_LOCK"; } get migrationTableLabel() { return kleur.green(this.props.migrationTableName); } async acquireLock(task, deadLockTimer = this.opts.migrationLockTimeoutSeconds * 1000) { // acquire lock try { const table = await this.seq.getQueryInterface().describeTable(this.lockTableName); // check deadlock try { const [rows] = await this.seq.getQueryInterface().sequelize.query(`select * from ${this.lockTableName}`); const row = rows[0]; if (!row || new Date(row.tableCreatedAt).getTime() < Date.now() - 1000 * 30 || deadLockTimer <= 0) { this.logger.info(`${this.migrationTableLabel} will release previous lock which is incomplete or dead for ${this.opts.migrationLockTimeoutSeconds}s`); await this.releaseLock(); return this.acquireLock(task); } } catch { } // if lock table exists, retry after 5-10s const waitTime = Math.ceil(5000 * (Math.random() + 1)); deadLockTimer -= waitTime; this.logger.warn(`${this.migrationTableLabel} failed to acquire migration lock, retry after ${waitTime}ms, force release lock in ${Math.ceil(deadLockTimer / 1000)}s`); await new Promise(resolve => setTimeout(resolve, waitTime)); return this.acquireLock(task, deadLockTimer); } catch (error) { // there are no lock table, try to create table await this.seq.getQueryInterface().createTable(this.lockTableName, { tableCreatedAt: sequelize_1.STRING, }); await this.seq.getQueryInterface().sequelize.query(`insert into ${this.lockTableName} values("${new Date().toISOString()}")`); this.logger.info(`${this.migrationTableLabel} lock acquired`); } // do task and release lock try { await task(); } catch (error) { throw error; } finally { await this.releaseLock(); } } async releaseLock(silent = false) { try { await this.seq.getQueryInterface().dropTable(this.lockTableName); this.logger.info(`${this.migrationTableLabel} lock released`); } catch (error) { this.logger.error(`${this.migrationTableLabel} failed to release migration lock`, error); } } } RDBMSManager.operatorsAliases = { $eq: sequelize_1.Op.eq, $ne: sequelize_1.Op.ne, $gte: sequelize_1.Op.gte, $gt: sequelize_1.Op.gt, $lte: sequelize_1.Op.lte, $lt: sequelize_1.Op.lt, $not: sequelize_1.Op.not, $in: sequelize_1.Op.in, $notIn: sequelize_1.Op.notIn, $is: sequelize_1.Op.is, $like: sequelize_1.Op.like, $notLike: sequelize_1.Op.notLike, $iLike: sequelize_1.Op.iLike, $notILike: sequelize_1.Op.notILike, $regexp: sequelize_1.Op.regexp, $notRegexp: sequelize_1.Op.notRegexp, $iRegexp: sequelize_1.Op.iRegexp, $notIRegexp: sequelize_1.Op.notIRegexp, $between: sequelize_1.Op.between, $notBetween: sequelize_1.Op.notBetween, $overlap: sequelize_1.Op.overlap, $contains: sequelize_1.Op.contains, $contained: sequelize_1.Op.contained, $adjacent: sequelize_1.Op.adjacent, $strictLeft: sequelize_1.Op.strictLeft, $strictRight: sequelize_1.Op.strictRight, $noExtendRight: sequelize_1.Op.noExtendRight, $noExtendLeft: sequelize_1.Op.noExtendLeft, $and: sequelize_1.Op.and, $or: sequelize_1.Op.or, $any: sequelize_1.Op.any, $all: sequelize_1.Op.all, $values: sequelize_1.Op.values, $col: sequelize_1.Op.col, }; return RDBMSManager; })(); exports.RDBMSManager = RDBMSManager; //# sourceMappingURL=rdbms.js.map