diff --git a/src/database/index.js b/src/database/index.js index 21484c5492..818efb4fe0 100644 --- a/src/database/index.js +++ b/src/database/index.js @@ -19,4 +19,24 @@ primaryDB.parseIntFields = function (data, intFields, requestedFields) { }); }; +primaryDB.initSessionStore = function (callback) { + const sessionStoreConfig = nconf.get('session_store') || nconf.get('redis') || nconf.get(databaseName); + let sessionStoreDB = primaryDB; + + if (nconf.get('session_store')) { + sessionStoreDB = require('./' + sessionStoreConfig.name); + } else if (nconf.get('redis')) { + // if redis is specified, use it as session store over others + sessionStoreDB = require('./redis'); + } + + sessionStoreDB.createSessionStore(sessionStoreConfig, function (err, sessionStore) { + if (err) { + return callback(err); + } + primaryDB.sessionStore = sessionStore; + callback(); + }); +}; + module.exports = primaryDB; diff --git a/src/database/mongo.js b/src/database/mongo.js index 5970f53b94..f7a9438593 100644 --- a/src/database/mongo.js +++ b/src/database/mongo.js @@ -9,7 +9,6 @@ var session = require('express-session'); var _ = require('lodash'); var semver = require('semver'); var prompt = require('prompt'); -var db; var client; var mongoModule = module.exports; @@ -62,40 +61,42 @@ mongoModule.questions = [ mongoModule.helpers = mongoModule.helpers || {}; mongoModule.helpers.mongo = require('./mongo/helpers'); -mongoModule.getConnectionString = function () { +mongoModule.getConnectionString = function (mongo) { + mongo = mongo || nconf.get('mongo'); var usernamePassword = ''; - var uri = nconf.get('mongo:uri') || ''; - if (nconf.get('mongo:username') && nconf.get('mongo:password')) { + var uri = mongo.uri || ''; + if (mongo.username && mongo.password) { usernamePassword = nconf.get('mongo:username') + ':' + encodeURIComponent(nconf.get('mongo:password')) + '@'; } else if (!uri.includes('@') || !uri.slice(uri.indexOf('://') + 3, uri.indexOf('@'))) { winston.warn('You have no mongo username/password setup!'); } // Sensible defaults for Mongo, if not set - if (!nconf.get('mongo:host')) { - nconf.set('mongo:host', '127.0.0.1'); + if (!mongo.host) { + mongo.host = '127.0.0.1'; } - if (!nconf.get('mongo:port')) { - nconf.set('mongo:port', 27017); + if (!mongo.port) { + mongo.port = 27017; } - const dbName = nconf.get('mongo:database'); + const dbName = mongo.database; if (dbName === undefined || dbName === '') { winston.warn('You have no database name, using "nodebb"'); - nconf.set('mongo:database', 'nodebb'); + mongo.database = 'nodebb'; } - var hosts = nconf.get('mongo:host').split(','); - var ports = nconf.get('mongo:port').toString().split(','); + var hosts = mongo.host.split(','); + var ports = mongo.port.toString().split(','); var servers = []; for (var i = 0; i < hosts.length; i += 1) { servers.push(hosts[i] + ':' + ports[i]); } - return nconf.get('mongo:uri') || 'mongodb://' + usernamePassword + servers.join() + '/' + nconf.get('mongo:database'); + return uri || 'mongodb://' + usernamePassword + servers.join() + '/' + mongo.database; }; -mongoModule.getConnectionOptions = function () { +mongoModule.getConnectionOptions = function (mongo) { + mongo = mongo || nconf.get('mongo'); var connOptions = { poolSize: 10, reconnectTries: 3600, @@ -105,24 +106,19 @@ mongoModule.getConnectionOptions = function () { useNewUrlParser: true, }; - return _.merge(connOptions, nconf.get('mongo:options') || {}); + return _.merge(connOptions, mongo.options || {}); }; mongoModule.init = function (callback) { callback = callback || function () { }; - var mongoClient = require('mongodb').MongoClient; - - var connString = mongoModule.getConnectionString(); - var connOptions = mongoModule.getConnectionOptions(); - - mongoClient.connect(connString, connOptions, function (err, _client) { + mongoModule.connect(nconf.get('mongo'), function (err, _client) { if (err) { winston.error('NodeBB could not connect to your Mongo database. Mongo returned the following error', err); return callback(err); } client = _client; - db = client.db(); + var db = client.db(); mongoModule.client = db; require('./mongo/main')(db, mongoModule); @@ -138,30 +134,31 @@ mongoModule.init = function (callback) { }); }; -mongoModule.initSessionStore = function (callback) { - var meta = require('../meta'); - var sessionStore; +mongoModule.connect = function (options, callback) { + callback = callback || function () { }; - var ttl = meta.getSessionTTLSeconds(); + var mongoClient = require('mongodb').MongoClient; - if (nconf.get('redis')) { - sessionStore = require('connect-redis')(session); - var rdb = require('./redis'); - rdb.client = rdb.connect(); + var connString = mongoModule.getConnectionString(options); + var connOptions = mongoModule.getConnectionOptions(options); - mongoModule.sessionStore = new sessionStore({ - client: rdb.client, - ttl: ttl, - }); - } else if (nconf.get('mongo')) { - sessionStore = require('connect-mongo')(session); - mongoModule.sessionStore = new sessionStore({ - db: db, - ttl: ttl, + mongoClient.connect(connString, connOptions, callback); +}; + +mongoModule.createSessionStore = function (options, callback) { + mongoModule.connect(options, function (err, client) { + if (err) { + return callback(err); + } + const meta = require('../meta'); + const sessionStore = require('connect-mongo')(session); + const store = new sessionStore({ + db: client.db(), + ttl: meta.getSessionTTLSeconds(), }); - } - callback(); + callback(null, store); + }); }; mongoModule.createIndices = function (callback) { diff --git a/src/database/postgres.js b/src/database/postgres.js index 463a493ab5..5284e76b68 100644 --- a/src/database/postgres.js +++ b/src/database/postgres.js @@ -7,7 +7,7 @@ var session = require('express-session'); var _ = require('lodash'); var semver = require('semver'); var dbNamespace = require('continuation-local-storage').createNamespace('postgres'); -var db; + var postgresModule = module.exports; @@ -44,29 +44,30 @@ postgresModule.questions = [ postgresModule.helpers = postgresModule.helpers || {}; postgresModule.helpers.postgres = require('./postgres/helpers'); -postgresModule.getConnectionOptions = function () { +postgresModule.getConnectionOptions = function (postgres) { + postgres = postgres || nconf.get('postgres'); // Sensible defaults for PostgreSQL, if not set - if (!nconf.get('postgres:host')) { - nconf.set('postgres:host', '127.0.0.1'); + if (!postgres.host) { + postgres.host = '127.0.0.1'; } - if (!nconf.get('postgres:port')) { - nconf.set('postgres:port', 5432); + if (!postgres.port) { + postgres.port = 5432; } - const dbName = nconf.get('postgres:database'); + const dbName = postgres.database; if (dbName === undefined || dbName === '') { winston.warn('You have no database name, using "nodebb"'); - nconf.set('postgres:database', 'nodebb'); + postgres.database = 'nodebb'; } var connOptions = { - host: nconf.get('postgres:host'), - port: nconf.get('postgres:port'), - user: nconf.get('postgres:username'), - password: nconf.get('postgres:password'), - database: nconf.get('postgres:database'), + host: postgres.host, + port: postgres.port, + user: postgres.username, + password: postgres.password, + database: postgres.database, }; - return _.merge(connOptions, nconf.get('postgres:options') || {}); + return _.merge(connOptions, postgres.options || {}); }; postgresModule.init = function (callback) { @@ -76,7 +77,7 @@ postgresModule.init = function (callback) { var connOptions = postgresModule.getConnectionOptions(); - db = new Pool(connOptions); + const db = new Pool(connOptions); db.on('connect', function (client) { var realQuery = client.query; @@ -132,6 +133,29 @@ postgresModule.init = function (callback) { }); }; +postgresModule.connect = function (options, callback) { + var Pool = require('pg').Pool; + + var connOptions = postgresModule.getConnectionOptions(options); + + const db = new Pool(connOptions); + + db.on('connect', function (client) { + var realQuery = client.query; + client.query = function () { + var args = Array.prototype.slice.call(arguments, 0); + if (dbNamespace.active && typeof args[args.length - 1] === 'function') { + args[args.length - 1] = dbNamespace.bind(args[args.length - 1]); + } + return realQuery.apply(client, args); + }; + }); + + db.connect(function (err) { + callback(err, db); + }); +}; + function checkUpgrade(client, callback) { client.query(` SELECT EXISTS(SELECT * @@ -347,41 +371,27 @@ SELECT "_key", "type" }); } -postgresModule.initSessionStore = function (callback) { +postgresModule.createSessionStore = function (options, callback) { var meta = require('../meta'); - var sessionStore; - - var ttl = meta.getSessionTTLSeconds(); - - if (nconf.get('redis')) { - sessionStore = require('connect-redis')(session); - var rdb = require('./redis'); - rdb.client = rdb.connect(); - postgresModule.sessionStore = new sessionStore({ - client: rdb.client, - ttl: ttl, - }); - - return callback(); - } - - function done() { - sessionStore = require('connect-pg-simple')(session); - postgresModule.sessionStore = new sessionStore({ + function done(db) { + const sessionStore = require('connect-pg-simple')(session); + const store = new sessionStore({ pool: db, - ttl: ttl, + ttl: meta.getSessionTTLSeconds(), pruneSessionInterval: nconf.get('isPrimary') === 'true' ? 60 : false, }); - - callback(); - } - - if (nconf.get('isPrimary') !== 'true') { - return done(); + callback(null, store); } - db.query(` + postgresModule.connect(options, function (err, db) { + if (err) { + return callback(err); + } + if (nconf.get('isPrimary') !== 'true') { + return done(db); + } + db.query(` CREATE TABLE IF NOT EXISTS "session" ( "sid" CHAR(32) NOT NULL COLLATE "C" @@ -395,11 +405,12 @@ CREATE INDEX IF NOT EXISTS "session_expire_idx" ON "session"("expire"); ALTER TABLE "session" ALTER "sid" SET STORAGE MAIN, CLUSTER ON "session_expire_idx";`, function (err) { - if (err) { - return callback(err); - } + if (err) { + return callback(err); + } - done(); + done(db); + }); }); }; @@ -456,7 +467,7 @@ SELECT true "postgres", postgresModule.close = function (callback) { callback = callback || function () {}; - db.end(callback); + postgresModule.pool.end(callback); }; postgresModule.socketAdapter = function () { diff --git a/src/database/redis.js b/src/database/redis.js index 6d780d0853..cbd66c1abf 100644 --- a/src/database/redis.js +++ b/src/database/redis.js @@ -36,9 +36,20 @@ redisModule.questions = [ }, ]; +redisModule.getConnectionOptions = function (redis) { + redis = redis || nconf.get('redis'); + let connOptions = {}; + if (redis.password) { + connOptions.auth_pass = redis.password; + } + + connOptions = _.merge(connOptions, redis.options || {}); + return connOptions; +}; + redisModule.init = function (callback) { callback = callback || function () { }; - redisClient = redisModule.connect({}, function (err) { + redisClient = redisModule.connect(nconf.get('redis'), function (err) { if (err) { winston.error('NodeBB could not connect to your Redis database. Redis returned the following error', err); return callback(err); @@ -58,39 +69,22 @@ redisModule.init = function (callback) { }); }; -redisModule.initSessionStore = function (callback) { - var meta = require('../meta'); - var sessionStore = require('connect-redis')(session); - - redisModule.sessionStore = new sessionStore({ - client: redisModule.client, - ttl: meta.getSessionTTLSeconds(), - }); - - if (typeof callback === 'function') { - callback(); - } -}; redisModule.connect = function (options, callback) { callback = callback || function () {}; - var redis_socket_or_host = nconf.get('redis:host'); + options = options || nconf.get('redis'); + var redis_socket_or_host = options.host; var cxn; var callbackCalled = false; - options = options || {}; - - if (nconf.get('redis:password')) { - options.auth_pass = nconf.get('redis:password'); - } - options = _.merge(options, nconf.get('redis:options') || {}); + const connOptions = redisModule.getConnectionOptions(options); if (redis_socket_or_host && redis_socket_or_host.indexOf('/') >= 0) { /* If redis.host contains a path name character, use the unix dom sock connection. ie, /tmp/redis.sock */ - cxn = redis.createClient(nconf.get('redis:host'), options); + cxn = redis.createClient(options.host, connOptions); } else { /* Else, connect over tcp/ip */ - cxn = redis.createClient(nconf.get('redis:port'), nconf.get('redis:host'), options); + cxn = redis.createClient(options.port, options.host, connOptions); } cxn.on('error', function (err) { @@ -108,11 +102,11 @@ redisModule.connect = function (options, callback) { } }); - if (nconf.get('redis:password')) { - cxn.auth(nconf.get('redis:password')); + if (options.password) { + cxn.auth(options.password); } - var dbIdx = parseInt(nconf.get('redis:database'), 10); + var dbIdx = parseInt(options.database, 10); if (dbIdx >= 0) { cxn.select(dbIdx, function (err) { if (err) { @@ -125,6 +119,20 @@ redisModule.connect = function (options, callback) { return cxn; }; +redisModule.createSessionStore = function (options, callback) { + const meta = require('../meta'); + const sessionStore = require('connect-redis')(session); + const client = redisModule.connect(options); + const store = new sessionStore({ + client: client, + ttl: meta.getSessionTTLSeconds(), + }); + + if (typeof callback === 'function') { + callback(null, store); + } +}; + redisModule.createIndices = function (callback) { setImmediate(callback); }; @@ -182,8 +190,8 @@ redisModule.info = function (cxn, callback) { redisModule.socketAdapter = function () { var redisAdapter = require('socket.io-redis'); - var pub = redisModule.connect(); - var sub = redisModule.connect(); + var pub = redisModule.connect(nconf.get('redis')); + var sub = redisModule.connect(nconf.get('redis')); return redisAdapter({ key: 'db:' + nconf.get('redis:database') + ':adapter_key', pubClient: pub,