@condor-labs/redis
Version:
This module provide and usefull helper to use the official Node_redis library.
126 lines (121 loc) • 5.01 kB
JavaScript
const Joi = require('joi');
const fs = require('fs');
const os = require("os")
const redis = require('redis');
const logger = require('@condor-labs/logger');
const { extractClientValue, removeIpPort } = require('../utils');
const self = {
_settings: null,
_client: undefined,
_errorMsgInvalidSetting: 'INVALID_SETTINGS',
_errorMsgUndefinedSetting: 'SETTINGS_NOT_DEFINED',
_schema: Joi.object().keys({
socket: Joi.object().keys({
port: Joi.number().default(6379),
host: Joi.string().default('127.0.0.1').required(),
tls: Joi.boolean().default(false),
key: Joi.string().when('tls', {
is: true,
then: Joi.string().required()
}),
ca: Joi.string().when('tls', {
is: true,
then: Joi.string().required()
}),
cert: Joi.string().when('tls', {
is: true,
then: Joi.string().required()
})
}).unknown(true),
clientName: Joi.string(),
name: Joi.string(),
password: Joi.string().default(null).allow(null).allow(''),
prefix: Joi.string().default(null)
}),
_setSettings: settings => {
//Validate the settings
if (!self._validateSettings(settings)) {
throw new Error(self._errorMsgInvalidSetting);
}
if (settings) {
settings.clientName = settings.clientName || os.hostname()
settings.clientName = settings.clientName.replace(new RegExp(`_pid${process.pid}|\\s`), '')
settings.name = `${settings.clientName}_pid${process.pid}`
}
self._settings = settings;
},
_validateSettings: settings => {
if (!settings) {
logger.error(self._errorMsgUndefinedSetting);
return false;
}
const { error, value } = self._schema.validate(settings);
if (error === void 0) {
return true;
} else {
logger.error(`${error} - ${value}`);
return false;
}
},
_clientList: async () => {
const list = await self._client.sendCommand(['CLIENT', 'LIST'])
return list.split(`\n`)
},
_clientInfo: async () => {
const info = await self._client.sendCommand(['CLIENT', 'INFO'])
return info
},
_filterclient: async (name, addr) => {
const list = await self._clientList()
let clients = list.filter((client) => (client.includes(`addr=${addr}`) && (client.includes(`name=${name}`) || client.includes(`name= `))))
return clients
},
_verifyUniqueConnection: async (verifyUniqueConnection) => {
if (verifyUniqueConnection) {
const currenClient = await self._clientInfo()
const actualID = extractClientValue('id', currenClient)
const actualAddr = extractClientValue('addr', currenClient)
const clients = await self._filterclient(self._settings.name, removeIpPort(actualAddr))
if (clients.length > 1) {
for (const client of clients) {
const clientId = extractClientValue('id', client)
const ageConnection = extractClientValue('age', client) || 0
if (actualID !== clientId) {
const clientName = extractClientValue('name', client)
if (clientName === '' && Number(ageConnection) < 5) continue
const del = await self._killClient(clientId)
}
}
}
}
},
_killClient: async (id) => {
if (id) {
return await self._client.clientKill({ filter: 'ID', id })
}
},
getClient: async (options = {}) => {
const { forceReconnect = false, verifyUniqueConnection = true } = options
if (forceReconnect || !self._client) {
if (!self._validateSettings(self._settings)) {
throw new Error(self._errorMsgInvalidSetting);
}
if (self._settings.socket.tls === true) {
self._settings.socket.key = fs.readFileSync(self._settings.socket.key, { 'encoding': 'ascii' })
self._settings.socket.cert = fs.readFileSync(self._settings.socket.cert, { 'encoding': 'ascii' })
self._settings.socket.ca = [fs.readFileSync(self._settings.socket.ca, { 'encoding': 'ascii' })]
};
self._client = await redis.createClient(self._settings);
await self._client.connect()
await self._verifyUniqueConnection(verifyUniqueConnection);
self._client.on("connect", async () => {
logger.info(`REDIS_CONNECTED`, self._settings.name);
});
self._client.on("error", (err) => {
logger.error('REDIS_ERROR_CONNECTION', err);
});
}
return self._client;
}
};
module.exports = self;