UNPKG

@minimaltech/node-infra

Version:

Minimal Technology NodeJS Infrastructure - Loopback 4 Framework

372 lines 16.1 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.RedisClusterHelper = exports.RedisHelper = exports.DefaultRedisHelper = void 0; const base_helper_1 = require("../base/base.helper"); const utilities_1 = require("../utilities"); const ioredis_1 = require("ioredis"); const isEmpty_1 = __importDefault(require("lodash/isEmpty")); const node_zlib_1 = __importDefault(require("node:zlib")); // ----------------------------------------------------------------------------------------------- class DefaultRedisHelper extends base_helper_1.BaseHelper { constructor(opts) { super({ scope: opts.scope, identifier: opts.identifier }); this.name = opts.identifier; this.client = opts.client; const { onInitialized, onConnected, onReady, onError } = opts; this.client.on('connect', () => { this.logger.info('[%s][connect] Redis CONNECTED', this.name); onConnected === null || onConnected === void 0 ? void 0 : onConnected({ name: this.name, helper: this }); }); this.client.on('ready', () => { this.logger.info('[%s][ready] Redis READY', this.name); onReady === null || onReady === void 0 ? void 0 : onReady({ name: this.name, helper: this }); }); this.client.on('error', error => { this.logger.error('[%s][error] Redis ERROR | Error: %s', this.name, error); onError === null || onError === void 0 ? void 0 : onError({ name: this.name, helper: this, error }); }); this.client.on('reconnecting', () => { this.logger.warn('[%s][reconnecting] Redis client RECONNECTING', this.name); }); onInitialized === null || onInitialized === void 0 ? void 0 : onInitialized({ name: this.name, helper: this }); } getClient() { return this.client; } ping() { return this.client.ping(); } connect() { return new Promise((resolve, reject) => { const invalidStatuses = [ 'ready', 'reconnecting', 'connecting', ]; if (!this.client || invalidStatuses.includes(this.client.status)) { this.logger.info('[connect] status: %s | Invalid redis status to invoke connect', this.client.status); resolve(false); return; } this.client .connect() .then(() => { resolve(this.client.status === 'ready'); }) .catch(reject); }); } // --------------------------------------------------------------------------------- disconnect() { return new Promise((resolve, reject) => { const invalidStatuses = ['end', 'close']; if (!this.client || invalidStatuses.includes(this.client.status)) { this.logger.info('[disconnect] status: %s | Invalid redis status to invoke connect', this.client.status); resolve(false); return; } this.client .quit() .then(rs => { resolve(rs === 'OK'); }) .catch(reject); }); } // --------------------------------------------------------------------------------- set(opts) { return __awaiter(this, void 0, void 0, function* () { const { key, value, options = { log: false } } = opts; if (!this.client) { this.logger.info('[set] No valid Redis connection!'); return; } const serialized = JSON.stringify(value); yield this.client.set(key, serialized); if (!(options === null || options === void 0 ? void 0 : options.log)) { return; } this.logger.info(`[set] Set key: ${key} | value: ${serialized}`); }); } // --------------------------------------------------------------------------------- get(opts) { return __awaiter(this, void 0, void 0, function* () { const { key, transform = (input) => input } = opts; if (!this.client) { this.logger.info('[get] No valid Redis connection!'); return null; } const value = yield this.client.get(key); if (!value) { return null; } return transform(value); }); } // --------------------------------------------------------------------------------- del(opts) { const { keys } = opts; return this.client.del(keys); } // --------------------------------------------------------------------------------- getString(opts) { return this.get(opts); } // --------------------------------------------------------------------------------- getStrings(opts) { return this.mget(opts); } // --------------------------------------------------------------------------------- getObject(opts) { return this.get(Object.assign(Object.assign({}, opts), { transform: (cached) => JSON.parse(cached) })); } // --------------------------------------------------------------------------------- getObjects(opts) { return this.mget(Object.assign(Object.assign({}, opts), { transform: (cached) => JSON.parse(cached) })); } // --------------------------------------------------------------------------------- hset(opts) { return __awaiter(this, void 0, void 0, function* () { if (!this.client) { this.logger.info('[hset] No valid Redis connection!'); return; } const { key, value, options } = opts; const rs = yield this.client.hset(key, value); if (!(options === null || options === void 0 ? void 0 : options.log)) { return rs; } this.logger.info('[hset] Result: %j', rs); return rs; }); } // --------------------------------------------------------------------------------- hSet(opts) { return this.hset(opts); } // --------------------------------------------------------------------------------- hgetall(opts) { return __awaiter(this, void 0, void 0, function* () { const { key, transform } = opts; if (!this.client) { this.logger.info('[get] No valid Redis connection!'); return null; } const value = yield this.client.hgetall(key); if (!transform || !value) { return value; } return transform(value); }); } // --------------------------------------------------------------------------------- hGetAll(opts) { return this.hgetall(opts); } // --------------------------------------------------------------------------------- mset(opts) { return __awaiter(this, void 0, void 0, function* () { if (!this.client) { this.logger.info('[set] No valid Redis connection!'); return; } const { payload, options } = opts; const serialized = payload === null || payload === void 0 ? void 0 : payload.reduce((current, el) => { const { key, value } = el; return Object.assign(Object.assign({}, current), { [key]: JSON.stringify(value) }); }, {}); yield this.client.mset(serialized); if (!(options === null || options === void 0 ? void 0 : options.log)) { return; } this.logger.info('[mset] Payload: %j', serialized); }); } // --------------------------------------------------------------------------------- mSet(opts) { return this.mset(opts); } // --------------------------------------------------------------------------------- mget(opts) { return __awaiter(this, void 0, void 0, function* () { const { keys, transform = (input) => input } = opts; if (!this.client) { this.logger.info('[get] No valid Redis connection!'); return null; } const values = yield this.client.mget(keys); if (!(values === null || values === void 0 ? void 0 : values.length)) { return null; } return values === null || values === void 0 ? void 0 : values.map(el => (el ? transform(el) : el)); }); } // --------------------------------------------------------------------------------- mGet(opts) { return this.mget(opts); } // --------------------------------------------------------------------------------- keys(opts) { return __awaiter(this, void 0, void 0, function* () { const { key } = opts; if (!this.client) { this.logger.info('[keys] No valid Redis connection!'); return []; } const existedKeys = yield this.client.keys(key); return existedKeys; }); } // --------------------------------------------------------------------------------- jSet(opts) { const { key, path, value } = opts; return this.execute('JSON.SET', [key, path, JSON.stringify(value)]); } // --------------------------------------------------------------------------------- jGet(opts) { const { key, path = '$' } = opts; return this.execute('JSON.GET', [key, path]); } // --------------------------------------------------------------------------------- jDelete(opts) { const { key, path = '$' } = opts; return this.execute('JSON.DEL', [key, path]); } // --------------------------------------------------------------------------------- jNumberIncreaseBy(opts) { const { key, path, value } = opts; return this.execute('JSON.NUMINCRBY', [key, path, value]); } // --------------------------------------------------------------------------------- jStringAppend(opts) { const { key, path, value } = opts; return this.execute('JSON.STRAPPEND', [key, path, value]); } // --------------------------------------------------------------------------------- jPush(opts) { const { key, path, value } = opts; return this.execute('JSON.ARRAPPEND', [key, path, JSON.stringify(value)]); } // --------------------------------------------------------------------------------- jPop(opts) { const { key, path } = opts; return this.execute('JSON.ARRPOP', [key, path]); } // --------------------------------------------------------------------------------- execute(command, parameters) { if (!(parameters === null || parameters === void 0 ? void 0 : parameters.length)) { return this.client.call(command); } return this.client.call(command, parameters); } // --------------------------------------------------------------------------------- publish(opts) { return __awaiter(this, void 0, void 0, function* () { const { topics, payload, useCompress = false } = opts; const validTopics = topics === null || topics === void 0 ? void 0 : topics.filter(topic => !(0, isEmpty_1.default)(topic)); if (!(validTopics === null || validTopics === void 0 ? void 0 : validTopics.length)) { this.logger.error('[publish] No topic(s) to publish!'); return; } if (!payload) { this.logger.error('[publish] Invalid payload to publish!'); return; } if (!this.client) { this.logger.error('[publish] No valid Redis connection!'); return; } yield Promise.all(validTopics.map(topic => { let packet; if (useCompress) { packet = node_zlib_1.default.deflateSync(Buffer.from(JSON.stringify(payload))); } else { packet = Buffer.from(JSON.stringify(payload)); } return this.client.publish(topic, packet); })); }); } // --------------------------------------------------------------------------------- subscribe(opts) { const { topic } = opts; if (!topic || (0, isEmpty_1.default)(topic)) { this.logger.error('[subscribe] No topic to subscribe!'); return; } if (!this.client) { this.logger.error('[subscribe] No valid Redis connection!'); return; } this.client.subscribe(topic, (error, count) => { if (error) { throw (0, utilities_1.getError)({ statusCode: 500, message: `[subscribe] Failed to subscribe to topic: ${topic}`, }); } this.logger.info('[subscribe] Subscribed to %s channel(s). Listening to channel: %s', count, topic); }); } } exports.DefaultRedisHelper = DefaultRedisHelper; // ----------------------------------------------------------------------------------------------- class RedisHelper extends DefaultRedisHelper { constructor(opts) { const { name, host, port, password, // Optional database = 0, autoConnect = true, maxRetry = 0, } = opts; super(Object.assign(Object.assign({}, opts), { scope: RedisHelper.name, identifier: name, client: new ioredis_1.Redis({ name, host, port: (0, utilities_1.int)(port), password, db: database, lazyConnect: !autoConnect, showFriendlyErrorStack: true, retryStrategy: (attemptCounter) => { if (maxRetry > -1 && attemptCounter > maxRetry) { return undefined; } const strategy = Math.max(Math.min(attemptCounter * 2000, 5000), 1000); return strategy; }, maxRetriesPerRequest: null, }) })); } getClient() { return this.client; } } exports.RedisHelper = RedisHelper; // ----------------------------------------------------------------------------------------------- class RedisClusterHelper extends DefaultRedisHelper { constructor(opts) { super(Object.assign(Object.assign({}, opts), { scope: RedisClusterHelper.name, identifier: opts.name, client: new ioredis_1.Cluster(opts.nodes.map(node => { return { host: node.host, port: (0, utilities_1.int)(node.port), password: node.password, }; }), opts.clusterOptions) })); } getClient() { return this.client; } } exports.RedisClusterHelper = RedisClusterHelper; //# sourceMappingURL=redis.helper.js.map