@notross/redis-hub
Version:
A minimal connection hub for Redis in Node.js
123 lines • 4.39 kB
JavaScript
import { createClient } from 'redis';
import { Logger } from './utils';
const logger = new Logger();
/**
* Central hub managing named Redis clients. Each
* name gets one shared connection.
*
* Pub/sub roles or per-namespace/user connections
* are just distinct names.
*/
export class RedisHub {
constructor(loggerConfig) {
this.defaultClientName = 'default';
this.clients = {};
this.clientOptions = {};
this.defaultOptions = {};
this.error = null;
this.status = null;
this.connect = this.client.bind(this);
this.configureLogger(loggerConfig);
}
configureLogger(config = { logs: true }) {
logger.setup(config);
}
async getDefaultClient() {
return await this.client(this.defaultClientName);
}
setDefaultOptions(options) {
this.defaultOptions = options;
}
/**
* Set the global default Redis options used when no per-client override exists.
* @param options RedisClientOptions
* @param options.defaultClientName string
*/
init(options) {
const { defaultClientName, ...redisClientOptions } = options;
this.setDefaultOptions(redisClientOptions);
if (defaultClientName) {
this.defaultClientName = defaultClientName;
}
}
getClientById(clientId) {
var _a;
return (_a = this.clients[clientId]) !== null && _a !== void 0 ? _a : null;
}
createClient(clientId, options) {
options = options !== null && options !== void 0 ? options : this.defaultOptions;
if (!options) {
throw new Error(`No options provided for '${clientId}' and no default options exist.`);
}
const client = createClient(options);
this.handleClientEvents(client, clientId);
this.clients[clientId] = client;
this.clientOptions[clientId] = options;
return client;
}
conflictingOptions(clientId, options) {
const clientOptions = JSON.stringify(this.clientOptions[clientId]);
return JSON.stringify(options) !== clientOptions;
}
/**
* Get or create a named Redis client. Lazy-connects on first call.
* @param clientId Logical name (e.g., "publisher", "user-123-subscriber").
* @param options Optional per-client options; only applied on first creation.
* @returns Connected Redis client.
*/
async client(clientId, options) {
if (this.clients[clientId]) {
if (options && this.conflictingOptions(clientId, options)) {
logger.warn(`Options for '${clientId}' were passed again and ignored.`);
}
return this.clients[clientId];
}
const client = this.createClient(clientId, options);
await client.connect();
return client;
}
/**
* Disconnects all managed clients and clears internal state.
*/
async disconnectAll() {
await Promise.all(Object.values(this.clients).map((client) => client.destroy()));
this.clients = {};
}
/**
* List all logs; useful if logging is disabled
*/
logs() {
return logger.logs;
}
handleClientEvents(client, clientId) {
// Prevent double-binding: Redis client instances are
// new per name so safe to bind unconditionally here.
client.on('connect', () => {
this.status = `[${clientId}]: client connected.`;
logger.log(this.status);
this.clients[clientId] = client;
});
client.on('ready', () => {
this.status = `[${clientId}]: client ready.`;
logger.log(this.status);
this.clients[clientId] = client;
});
client.on('reconnecting', () => {
this.status = `[${clientId}]: client reconnecting...`;
logger.log(this.status);
this.clients[clientId] = client;
});
client.on('end', () => {
this.status = `[${clientId}]: client closed.`;
logger.log(this.status);
this.clients[clientId] = client;
});
client.on('error', (err) => {
this.status = `[${clientId}]: client error:`;
this.error = err;
logger.error(this.status, this.error);
this.clients[clientId] = client;
});
}
}
//# sourceMappingURL=redis-hub.js.map