@minimaltech/node-infra
Version:
Minimal Technology NodeJS Infrastructure - Loopback 4 Framework
372 lines • 16.1 kB
JavaScript
"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