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' ) ;
2017-10-23 15:09:13 -04:00
var prompt = require ( 'prompt' ) ;
2017-04-22 00:19:23 -04:00
var db ;
2018-05-10 16:31:16 -04:00
var client ;
2017-04-22 00:19:23 -04:00
var mongoModule = module . exports ;
2017-10-23 15:09:13 -04:00
function isUriNotSpecified ( ) {
return ! prompt . history ( 'mongo:uri' ) . value ;
}
2017-04-22 00:19:23 -04:00
mongoModule . questions = [
2017-10-23 15:09:13 -04:00
{
name : 'mongo:uri' ,
description : 'MongoDB connection URI: (leave blank if you wish to specify host, port, username/password and database individually)\nFormat: mongodb://[username:password@]host1[:port1][,host2[:port2],...[,hostN[:portN]]][/[database][?options]]' ,
default : nconf . get ( 'mongo:uri' ) || '' ,
2017-11-22 12:19:08 -05:00
hideOnWebInstall : true ,
2017-10-23 15:09:13 -04:00
} ,
2017-04-22 00:19:23 -04:00
{
name : 'mongo:host' ,
description : 'Host IP or address of your MongoDB instance' ,
default : nconf . get ( 'mongo:host' ) || '127.0.0.1' ,
2017-10-23 15:09:13 -04:00
ask : isUriNotSpecified ,
2017-04-22 00:19:23 -04:00
} ,
{
name : 'mongo:port' ,
description : 'Host port of your MongoDB instance' ,
default : nconf . get ( 'mongo:port' ) || 27017 ,
2017-10-23 15:09:13 -04:00
ask : isUriNotSpecified ,
2017-04-22 00:19:23 -04:00
} ,
{
name : 'mongo:username' ,
description : 'MongoDB username' ,
default : nconf . get ( 'mongo:username' ) || '' ,
2017-10-23 15:09:13 -04:00
ask : isUriNotSpecified ,
2017-04-22 00:19:23 -04:00
} ,
{
name : 'mongo:password' ,
description : 'Password of your MongoDB database' ,
default : nconf . get ( 'mongo:password' ) || '' ,
2017-10-23 15:09:13 -04:00
hidden : true ,
ask : isUriNotSpecified ,
2017-04-22 00:19:23 -04:00
before : function ( value ) { value = value || nconf . get ( 'mongo:password' ) || '' ; return value ; } ,
} ,
{
name : 'mongo:database' ,
description : 'MongoDB database name' ,
default : nconf . get ( 'mongo:database' ) || 'nodebb' ,
2017-10-23 15:09:13 -04:00
ask : isUriNotSpecified ,
2017-04-22 00:19:23 -04:00
} ,
] ;
mongoModule . helpers = mongoModule . helpers || { } ;
mongoModule . helpers . mongo = require ( './mongo/helpers' ) ;
2018-05-25 11:56:40 -04:00
mongoModule . getConnectionString = function ( ) {
2017-04-22 00:19:23 -04:00
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 ) ;
}
2018-10-16 20:27:14 -04:00
const dbName = nconf . get ( 'mongo:database' ) ;
if ( dbName === undefined || dbName === '' ) {
winston . warn ( 'You have no database name, using "nodebb"' ) ;
2017-04-22 00:19:23 -04:00
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 ] ) ;
}
2018-01-18 12:02:56 -06:00
return nconf . get ( 'mongo:uri' ) || 'mongodb://' + usernamePassword + servers . join ( ) + '/' + nconf . get ( 'mongo:database' ) ;
2018-05-25 11:56:40 -04:00
} ;
2017-04-22 00:19:23 -04:00
2018-05-25 11:56:40 -04:00
mongoModule . getConnectionOptions = function ( ) {
2017-04-22 00:19:23 -04:00
var connOptions = {
poolSize : 10 ,
reconnectTries : 3600 ,
reconnectInterval : 1000 ,
autoReconnect : true ,
2018-10-27 06:26:50 -04:00
connectTimeoutMS : 90000 ,
2018-09-26 15:02:57 -04:00
useNewUrlParser : true ,
2017-04-22 00:19:23 -04:00
} ;
2015-03-24 11:08:02 -04:00
2018-05-25 11:56:40 -04:00
return _ . merge ( connOptions , nconf . get ( 'mongo:options' ) || { } ) ;
} ;
mongoModule . init = function ( callback ) {
callback = callback || function ( ) { } ;
var mongoClient = require ( 'mongodb' ) . MongoClient ;
var connString = mongoModule . getConnectionString ( ) ;
var connOptions = mongoModule . getConnectionOptions ( ) ;
2015-04-25 08:05:33 -07:00
2018-05-10 16:31:16 -04:00
mongoClient . connect ( connString , connOptions , function ( err , _client ) {
2017-04-22 00:19:23 -04:00
if ( err ) {
2017-11-01 18:58:44 -06:00
winston . error ( 'NodeBB could not connect to your Mongo database. Mongo returned the following error' , err ) ;
2017-04-22 00:19:23 -04:00
return callback ( err ) ;
2015-04-27 11:08:58 -07:00
}
2018-05-10 16:31:16 -04:00
client = _client ;
db = client . db ( ) ;
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 ) ;
2018-08-08 14:13:48 -05:00
require ( './mongo/transaction' ) ( db , mongoModule ) ;
2018-08-31 11:04:42 -04:00
mongoModule . async = require ( '../promisify' ) ( mongoModule , [ 'client' , 'sessionStore' ] ) ;
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 ) {
2017-11-01 18:58:44 -06:00
winston . error ( 'Error creating index' , err ) ;
2017-04-22 00:19:23 -04:00
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 ( ) { } ;
2018-05-10 16:31:16 -04:00
client . close ( function ( err ) {
2018-03-22 16:36:18 -04:00
callback ( err ) ;
} ) ;
2017-04-22 00:19:23 -04:00
} ;
2018-01-18 12:02:56 -06:00
mongoModule . socketAdapter = function ( ) {
var mongoAdapter = require ( 'socket.io-adapter-mongo' ) ;
2018-05-25 11:56:40 -04:00
return mongoAdapter ( mongoModule . getConnectionString ( ) ) ;
2018-01-18 12:02:56 -06:00
} ;