var redis = require('redis'); var winston = require('winston'); // For storing in redis or KeyDB (Redis API compatible) // options[type] = redis // options[host] - The host to connect to (default localhost) // options[port] - The port to connect to (default 6379) // options[db] - The db to use (default 0) // options[password] - The password to use (if any) // options[expire] - The time to live for each key set (default never) // options[connectionTimeout] - Connection timeout in ms (default 5000) var RedisDocumentStore = function(options, client) { this.expire = options.expire; if (client) { winston.info('using predefined redis client'); RedisDocumentStore.client = client; // Check if we need to promisify the client (for redis-url 0.1.0) if (!RedisDocumentStore.client.connect && !RedisDocumentStore.isLegacyClient) { winston.info('using legacy redis client from redis-url'); RedisDocumentStore.isLegacyClient = true; } } else if (!RedisDocumentStore.client) { winston.info('configuring redis/keydb client'); RedisDocumentStore.connect(options); } }; // Create a connection according to config RedisDocumentStore.connect = async function(options) { var host = options.host || '127.0.0.1'; var port = options.port || 6379; var index = options.db || 0; var connectionTimeout = options.connectionTimeout || 5000; const redisOptions = { socket: { host: host, port: port, connectTimeout: connectionTimeout }, database: index }; // Add password if provided if (options.password) { redisOptions.password = options.password; } // Add URL if provided (overrides other options) if (options.url) { delete redisOptions.socket; delete redisOptions.database; redisOptions.url = options.url; } try { RedisDocumentStore.client = redis.createClient(redisOptions); // Set up error handling RedisDocumentStore.client.on('error', (err) => { winston.error('Redis/KeyDB error', { error: err }); }); // Connect the client await RedisDocumentStore.client.connect(); winston.info('connected to redis/keydb on ' + host + ':' + port + '/' + index); } catch (err) { winston.error('error connecting to redis/keydb', { error: err }); process.exit(1); } }; // Save file in a key RedisDocumentStore.prototype.set = async function(key, data, callback, skipExpire) { var _this = this; try { // Handle legacy client (redis-url 0.1.0) if (RedisDocumentStore.isLegacyClient) { RedisDocumentStore.client.set(key, data, function(err) { if (err) { winston.error('error setting key', { error: err }); callback(false); return; } if (!skipExpire) { _this.setExpiration(key); } callback(true); }); return; } // Modern redis 4.x client await RedisDocumentStore.client.set(key, data); if (!skipExpire) { _this.setExpiration(key); } callback(true); } catch (err) { winston.error('error setting key', { error: err }); callback(false); } }; // Expire a key in expire time if set RedisDocumentStore.prototype.setExpiration = async function(key) { if (this.expire) { try { if (RedisDocumentStore.isLegacyClient) { RedisDocumentStore.client.expire(key, this.expire, function(err) { if (err) { winston.error('failed to set expiry on key: ' + key, { error: err }); } }); return; } await RedisDocumentStore.client.expire(key, this.expire); } catch (err) { winston.error('failed to set expiry on key: ' + key, { error: err }); } } }; // Get a file from a key RedisDocumentStore.prototype.get = async function(key, callback, skipExpire) { var _this = this; try { // Handle legacy client (redis-url 0.1.0) if (RedisDocumentStore.isLegacyClient) { RedisDocumentStore.client.get(key, function(err, reply) { if (err) { winston.error('error getting key', { error: err }); callback(false); return; } if (reply && !skipExpire) { _this.setExpiration(key); } callback(reply); }); return; } // Modern redis 4.x client const reply = await RedisDocumentStore.client.get(key); if (reply && !skipExpire) { _this.setExpiration(key); } callback(reply); } catch (err) { winston.error('error getting key', { error: err }); callback(false); } }; module.exports = RedisDocumentStore;