| 
									
										
										
										
											2013-12-02 16:19:30 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-03-01 16:59:04 -05:00
										 |  |  | 'use strict'; | 
					
						
							| 
									
										
										
										
											2013-12-02 16:19:30 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-10-03 16:31:53 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-22 00:19:23 -04:00
										 |  |  | var winston = require('winston'); | 
					
						
							|  |  |  | var async = require('async'); | 
					
						
							|  |  |  | var nconf = require('nconf'); | 
					
						
							|  |  |  | var session = require('express-session'); | 
					
						
							| 
									
										
										
										
											2017-05-26 01:39:40 -06:00
										 |  |  | var _ = require('lodash'); | 
					
						
							| 
									
										
										
										
											2017-04-22 00:19:23 -04:00
										 |  |  | var semver = require('semver'); | 
					
						
							|  |  |  | var db; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var mongoModule = module.exports; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | mongoModule.questions = [ | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		name: 'mongo:host', | 
					
						
							|  |  |  | 		description: 'Host IP or address of your MongoDB instance', | 
					
						
							|  |  |  | 		default: nconf.get('mongo:host') || '127.0.0.1', | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		name: 'mongo:port', | 
					
						
							|  |  |  | 		description: 'Host port of your MongoDB instance', | 
					
						
							|  |  |  | 		default: nconf.get('mongo:port') || 27017, | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		name: 'mongo:username', | 
					
						
							|  |  |  | 		description: 'MongoDB username', | 
					
						
							|  |  |  | 		default: nconf.get('mongo:username') || '', | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		name: 'mongo:password', | 
					
						
							|  |  |  | 		description: 'Password of your MongoDB database', | 
					
						
							|  |  |  | 		hidden: true, | 
					
						
							|  |  |  | 		default: nconf.get('mongo:password') || '', | 
					
						
							|  |  |  | 		before: function (value) { value = value || nconf.get('mongo:password') || ''; return value; }, | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		name: 'mongo:database', | 
					
						
							|  |  |  | 		description: 'MongoDB database name', | 
					
						
							|  |  |  | 		default: nconf.get('mongo:database') || 'nodebb', | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | ]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | mongoModule.helpers = mongoModule.helpers || {}; | 
					
						
							|  |  |  | mongoModule.helpers.mongo = require('./mongo/helpers'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | mongoModule.init = function (callback) { | 
					
						
							|  |  |  | 	callback = callback || function () { }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	var mongoClient = require('mongodb').MongoClient; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	var usernamePassword = ''; | 
					
						
							|  |  |  | 	if (nconf.get('mongo:username') && nconf.get('mongo:password')) { | 
					
						
							|  |  |  | 		usernamePassword = nconf.get('mongo:username') + ':' + encodeURIComponent(nconf.get('mongo:password')) + '@'; | 
					
						
							| 
									
										
										
										
											2017-05-12 15:40:09 -04:00
										 |  |  | 	} else { | 
					
						
							|  |  |  | 		winston.warn('You have no mongo username/password setup!'); | 
					
						
							| 
									
										
										
										
											2017-04-22 00:19:23 -04:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Sensible defaults for Mongo, if not set
 | 
					
						
							|  |  |  | 	if (!nconf.get('mongo:host')) { | 
					
						
							|  |  |  | 		nconf.set('mongo:host', '127.0.0.1'); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if (!nconf.get('mongo:port')) { | 
					
						
							|  |  |  | 		nconf.set('mongo:port', 27017); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if (!nconf.get('mongo:database')) { | 
					
						
							|  |  |  | 		nconf.set('mongo:database', 'nodebb'); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	var hosts = nconf.get('mongo:host').split(','); | 
					
						
							|  |  |  | 	var ports = nconf.get('mongo:port').toString().split(','); | 
					
						
							|  |  |  | 	var servers = []; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for (var i = 0; i < hosts.length; i += 1) { | 
					
						
							|  |  |  | 		servers.push(hosts[i] + ':' + ports[i]); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-12 15:53:07 -04:00
										 |  |  | 	var connString = nconf.get('mongo:uri') || 'mongodb://' + usernamePassword + servers.join() + '/' + nconf.get('mongo:database'); | 
					
						
							| 
									
										
										
										
											2017-04-22 00:19:23 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	var connOptions = { | 
					
						
							|  |  |  | 		poolSize: 10, | 
					
						
							|  |  |  | 		reconnectTries: 3600, | 
					
						
							|  |  |  | 		reconnectInterval: 1000, | 
					
						
							|  |  |  | 		autoReconnect: true, | 
					
						
							|  |  |  | 	}; | 
					
						
							| 
									
										
										
										
											2015-03-24 11:08:02 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-26 01:39:40 -06:00
										 |  |  | 	connOptions = _.merge(connOptions, nconf.get('mongo:options') || {}); | 
					
						
							| 
									
										
										
										
											2015-04-25 08:05:33 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-22 00:19:23 -04:00
										 |  |  | 	mongoClient.connect(connString, connOptions, function (err, _db) { | 
					
						
							|  |  |  | 		if (err) { | 
					
						
							|  |  |  | 			winston.error('NodeBB could not connect to your Mongo database. Mongo returned the following error: ' + err.message); | 
					
						
							|  |  |  | 			return callback(err); | 
					
						
							| 
									
										
										
										
											2015-04-27 11:08:58 -07:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2015-04-25 08:05:33 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-22 00:19:23 -04:00
										 |  |  | 		db = _db; | 
					
						
							| 
									
										
										
										
											2017-01-16 14:58:27 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-22 00:19:23 -04:00
										 |  |  | 		mongoModule.client = db; | 
					
						
							| 
									
										
										
										
											2017-01-16 14:58:27 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-22 00:19:23 -04:00
										 |  |  | 		require('./mongo/main')(db, mongoModule); | 
					
						
							|  |  |  | 		require('./mongo/hash')(db, mongoModule); | 
					
						
							|  |  |  | 		require('./mongo/sets')(db, mongoModule); | 
					
						
							|  |  |  | 		require('./mongo/sorted')(db, mongoModule); | 
					
						
							|  |  |  | 		require('./mongo/list')(db, mongoModule); | 
					
						
							| 
									
										
										
										
											2017-05-12 15:40:09 -04:00
										 |  |  | 		callback(); | 
					
						
							| 
									
										
										
										
											2017-04-22 00:19:23 -04:00
										 |  |  | 	}); | 
					
						
							|  |  |  | }; | 
					
						
							| 
									
										
										
										
											2017-01-11 10:51:41 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-22 00:19:23 -04:00
										 |  |  | mongoModule.initSessionStore = function (callback) { | 
					
						
							|  |  |  | 	var meta = require('../meta'); | 
					
						
							|  |  |  | 	var sessionStore; | 
					
						
							| 
									
										
										
										
											2017-01-10 11:48:27 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-22 00:19:23 -04:00
										 |  |  | 	var ttl = meta.getSessionTTLSeconds(); | 
					
						
							| 
									
										
										
										
											2016-12-08 23:51:56 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-22 00:19:23 -04:00
										 |  |  | 	if (nconf.get('redis')) { | 
					
						
							|  |  |  | 		sessionStore = require('connect-redis')(session); | 
					
						
							|  |  |  | 		var rdb = require('./redis'); | 
					
						
							|  |  |  | 		rdb.client = rdb.connect(); | 
					
						
							| 
									
										
										
										
											2015-12-11 10:57:13 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-22 00:19:23 -04:00
										 |  |  | 		mongoModule.sessionStore = new sessionStore({ | 
					
						
							|  |  |  | 			client: rdb.client, | 
					
						
							|  |  |  | 			ttl: ttl, | 
					
						
							| 
									
										
										
										
											2017-01-10 11:48:27 +00:00
										 |  |  | 		}); | 
					
						
							| 
									
										
										
										
											2017-04-22 00:19:23 -04:00
										 |  |  | 	} else if (nconf.get('mongo')) { | 
					
						
							|  |  |  | 		sessionStore = require('connect-mongo')(session); | 
					
						
							|  |  |  | 		mongoModule.sessionStore = new sessionStore({ | 
					
						
							|  |  |  | 			db: db, | 
					
						
							|  |  |  | 			ttl: ttl, | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	callback(); | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | mongoModule.createIndices = function (callback) { | 
					
						
							|  |  |  | 	function createIndex(collection, index, options, callback) { | 
					
						
							|  |  |  | 		mongoModule.client.collection(collection).createIndex(index, options, callback); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!mongoModule.client) { | 
					
						
							|  |  |  | 		winston.warn('[database/createIndices] database not initialized'); | 
					
						
							|  |  |  | 		return callback(); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	winston.info('[database] Checking database indices.'); | 
					
						
							|  |  |  | 	async.series([ | 
					
						
							|  |  |  | 		async.apply(createIndex, 'objects', { _key: 1, score: -1 }, { background: true }), | 
					
						
							|  |  |  | 		async.apply(createIndex, 'objects', { _key: 1, value: -1 }, { background: true, unique: true, sparse: true }), | 
					
						
							|  |  |  | 		async.apply(createIndex, 'objects', { expireAt: 1 }, { expireAfterSeconds: 0, background: true }), | 
					
						
							|  |  |  | 	], function (err) { | 
					
						
							|  |  |  | 		if (err) { | 
					
						
							|  |  |  | 			winston.error('Error creating index ' + err.message); | 
					
						
							|  |  |  | 			return callback(err); | 
					
						
							| 
									
										
										
										
											2015-06-05 13:33:58 -04:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2017-04-22 00:19:23 -04:00
										 |  |  | 		winston.info('[database] Checking database indices done!'); | 
					
						
							| 
									
										
										
										
											2016-12-09 00:18:25 +03:00
										 |  |  | 		callback(); | 
					
						
							| 
									
										
										
										
											2017-04-22 00:19:23 -04:00
										 |  |  | 	}); | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | mongoModule.checkCompatibility = function (callback) { | 
					
						
							|  |  |  | 	var mongoPkg = require('mongodb/package.json'); | 
					
						
							| 
									
										
										
										
											2017-05-19 20:24:54 -04:00
										 |  |  | 	mongoModule.checkCompatibilityVersion(mongoPkg.version, callback); | 
					
						
							|  |  |  | }; | 
					
						
							| 
									
										
										
										
											2017-04-22 00:19:23 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-19 20:24:54 -04:00
										 |  |  | mongoModule.checkCompatibilityVersion = function (version, callback) { | 
					
						
							|  |  |  | 	if (semver.lt(version, '2.0.0')) { | 
					
						
							| 
									
										
										
										
											2017-04-22 00:19:23 -04:00
										 |  |  | 		return callback(new Error('The `mongodb` package is out-of-date, please run `./nodebb setup` again.')); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	callback(); | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | mongoModule.info = function (db, callback) { | 
					
						
							|  |  |  | 	if (!db) { | 
					
						
							|  |  |  | 		return callback(); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-05-19 20:24:54 -04:00
										 |  |  | 	async.waterfall([ | 
					
						
							|  |  |  | 		function (next) { | 
					
						
							|  |  |  | 			async.parallel({ | 
					
						
							|  |  |  | 				serverStatus: function (next) { | 
					
						
							|  |  |  | 					db.command({ serverStatus: 1 }, next); | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 				stats: function (next) { | 
					
						
							|  |  |  | 					db.command({ dbStats: 1 }, next); | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 				listCollections: function (next) { | 
					
						
							|  |  |  | 					getCollectionStats(db, next); | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}, next); | 
					
						
							| 
									
										
										
										
											2017-04-22 00:19:23 -04:00
										 |  |  | 		}, | 
					
						
							| 
									
										
										
										
											2017-05-19 20:24:54 -04:00
										 |  |  | 		function (results, next) { | 
					
						
							|  |  |  | 			var stats = results.stats; | 
					
						
							|  |  |  | 			var scale = 1024 * 1024 * 1024; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			results.listCollections = results.listCollections.map(function (collectionInfo) { | 
					
						
							|  |  |  | 				return { | 
					
						
							|  |  |  | 					name: collectionInfo.ns, | 
					
						
							|  |  |  | 					count: collectionInfo.count, | 
					
						
							|  |  |  | 					size: collectionInfo.size, | 
					
						
							|  |  |  | 					avgObjSize: collectionInfo.avgObjSize, | 
					
						
							|  |  |  | 					storageSize: collectionInfo.storageSize, | 
					
						
							|  |  |  | 					totalIndexSize: collectionInfo.totalIndexSize, | 
					
						
							|  |  |  | 					indexSizes: collectionInfo.indexSizes, | 
					
						
							|  |  |  | 				}; | 
					
						
							| 
									
										
										
										
											2015-10-17 18:34:04 -04:00
										 |  |  | 			}); | 
					
						
							| 
									
										
										
										
											2015-05-08 15:36:06 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-19 20:24:54 -04:00
										 |  |  | 			stats.mem = results.serverStatus.mem; | 
					
						
							|  |  |  | 			stats.mem = results.serverStatus.mem; | 
					
						
							| 
									
										
										
										
											2017-06-20 16:34:34 -04:00
										 |  |  | 			stats.mem.resident = (stats.mem.resident / 1024).toFixed(3); | 
					
						
							|  |  |  | 			stats.mem.virtual = (stats.mem.virtual / 1024).toFixed(3); | 
					
						
							|  |  |  | 			stats.mem.mapped = (stats.mem.mapped / 1024).toFixed(3); | 
					
						
							| 
									
										
										
										
											2017-05-19 20:24:54 -04:00
										 |  |  | 			stats.collectionData = results.listCollections; | 
					
						
							|  |  |  | 			stats.network = results.serverStatus.network; | 
					
						
							|  |  |  | 			stats.raw = JSON.stringify(stats, null, 4); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			stats.avgObjSize = stats.avgObjSize.toFixed(2); | 
					
						
							| 
									
										
										
										
											2017-06-20 16:34:34 -04:00
										 |  |  | 			stats.dataSize = (stats.dataSize / scale).toFixed(3); | 
					
						
							|  |  |  | 			stats.storageSize = (stats.storageSize / scale).toFixed(3); | 
					
						
							|  |  |  | 			stats.fileSize = stats.fileSize ? (stats.fileSize / scale).toFixed(3) : 0; | 
					
						
							|  |  |  | 			stats.indexSize = (stats.indexSize / scale).toFixed(3); | 
					
						
							| 
									
										
										
										
											2017-05-19 20:24:54 -04:00
										 |  |  | 			stats.storageEngine = results.serverStatus.storageEngine ? results.serverStatus.storageEngine.name : 'mmapv1'; | 
					
						
							|  |  |  | 			stats.host = results.serverStatus.host; | 
					
						
							|  |  |  | 			stats.version = results.serverStatus.version; | 
					
						
							|  |  |  | 			stats.uptime = results.serverStatus.uptime; | 
					
						
							|  |  |  | 			stats.mongo = true; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			next(null, stats); | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 	], callback); | 
					
						
							| 
									
										
										
										
											2017-04-22 00:19:23 -04:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-19 20:24:54 -04:00
										 |  |  | function getCollectionStats(db, callback) { | 
					
						
							|  |  |  | 	async.waterfall([ | 
					
						
							|  |  |  | 		function (next) { | 
					
						
							|  |  |  | 			db.listCollections().toArray(next); | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		function (items, next) { | 
					
						
							|  |  |  | 			async.map(items, function (collection, next) { | 
					
						
							|  |  |  | 				db.collection(collection.name).stats(next); | 
					
						
							|  |  |  | 			}, next); | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 	], callback); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | mongoModule.close = function (callback) { | 
					
						
							|  |  |  | 	callback = callback || function () {}; | 
					
						
							|  |  |  | 	db.close(callback); | 
					
						
							| 
									
										
										
										
											2017-04-22 00:19:23 -04:00
										 |  |  | }; |