Metzploreur/node_modules/connect-mongodb-session/index.js
clement callaert 244d45ceb8 Version 2
2023-11-01 17:33:25 +01:00

273 lines
7.1 KiB
JavaScript

'use strict';
const Archetype = require('archetype');
const EventEmitter = require('events').EventEmitter;
const mongodb = require('mongodb');
const OptionsType = new Archetype({
uri: {
$type: 'string',
$required: true,
$default: 'mongodb://localhost:27017/test'
},
collection: {
$type: 'string',
$required: true,
$default: 'sessions'
},
connectionOptions: {
$type: Object,
$default: () => ({ useNewUrlParser: true, useUnifiedTopology : true })
},
expires: {
$type: 'number',
$required: true,
$default: 1000 * 60 * 60 * 24 * 14 // 2 weeks
},
idField: {
$type: 'string',
$required: true,
$default: '_id'
},
databaseName: {
$type: 'string',
$required: false,
$default: null
},
expiresKey: {
$type: 'string',
$required: true,
$default: 'expires'
},
expiresAfterSeconds: {
$type: 'number',
$required: true,
$default: 0
}
}).compile('OptionsType');
/**
* Returns a constructor with the specified connect middleware's Store
* class as its prototype
*
* ####Example:
*
* connectMongoDBSession(require('express-session'));
*
* @param {Function} connect connect-compatible session middleware (e.g. Express 3, express-session)
* @api public
*/
module.exports = function(connect) {
const Store = connect.Store || connect.session.Store;
const MongoDBStore = function(options, callback) {
if (!(this instanceof MongoDBStore)) {
return new MongoDBStore(options, callback);
}
const _this = this;
this._emitter = new EventEmitter();
this._errorHandler = handleError.bind(this);
this.client = null;
this.db = null;
if (typeof options === 'function') {
callback = options;
options = {};
} else {
options = options || {};
}
options = new OptionsType(options);
Store.call(this, options);
this.options = options;
const connOptions = options.connectionOptions;
mongodb.MongoClient.connect(options.uri, connOptions, function(error, client) {
if (error) {
var e = new Error('Error connecting to db: ' + error.message);
return _this._errorHandler(e, callback);
}
const db = options.databaseName == null ?
client.db() :
client.db(options.databaseName);
_this.client = client;
_this.db = db;
const expiresIndex = {};
expiresIndex[options.expiresKey] = 1
db.
collection(options.collection).
createIndex(expiresIndex, { expireAfterSeconds: options.expiresAfterSeconds }, function(error) {
if (error) {
const e = new Error('Error creating index: ' + error.message);
return _this._errorHandler(e, callback);
}
_this._emitter.emit('connected');
return callback && callback();
});
});
};
MongoDBStore.prototype = Object.create(Store.prototype);
MongoDBStore.prototype._generateQuery = function(id) {
const ret = {};
ret[this.options.idField] = id;
return ret;
};
MongoDBStore.prototype.get = function(id, callback) {
const _this = this;
if (!this.db) {
return this._emitter.once('connected', function() {
_this.get.call(_this, id, callback);
});
}
this.db.collection(this.options.collection).
findOne(this._generateQuery(id), function(error, session) {
if (error) {
const e = new Error('Error finding ' + id + ': ' + error.message);
return _this._errorHandler(e, callback);
} else if (session) {
if (!session.expires || new Date < session.expires) {
return callback(null, session.session);
} else {
return _this.destroy(id, callback);
}
} else {
return callback();
}
});
};
// new store.all() for all sessions
MongoDBStore.prototype.all = function(callback) {
const _this = this;
if (!this.db) {
return this._emitter.once('connected', function() {
_this.all.call(_this, callback);
});
}
this.db.collection(this.options.collection).
find({}).toArray(function(error, sessions) {
if (error) {
const e = new Error('Error gathering sessions');
return _this._errorHandler(e, callback);
} else if (sessions) {
if (sessions) {
return callback(null, sessions);
}
} else {
return callback();
}
});
};
MongoDBStore.prototype.destroy = function(id, callback) {
const _this = this;
if (!this.db) {
return this._emitter.once('connected', function() {
_this.destroy.call(_this, id, callback);
});
}
this.db.collection(this.options.collection).
deleteOne(this._generateQuery(id), function(error) {
if (error) {
const e = new Error('Error destroying ' + id + ': ' + error.message);
return _this._errorHandler(e, callback);
}
callback && callback();
});
};
MongoDBStore.prototype.clear = function(callback) {
const _this = this;
if (!this.db) {
return this._emitter.once('connected', function() {
_this.clear.call(_this, callback);
});
}
this.db.collection(this.options.collection).
deleteMany({}, function(error) {
if (error) {
const e = new Error('Error clearing all sessions: ' + error.message);
return _this._errorHandler(e, callback);
}
callback && callback();
});
};
MongoDBStore.prototype.set = function(id, session, callback) {
const _this = this;
if (!this.db) {
return this._emitter.once('connected', function() {
_this.set.call(_this, id, session, callback);
});
}
const sess = {};
for (const key in session) {
if (key === 'cookie') {
sess[key] = session[key].toJSON ? session[key].toJSON() : session[key];
} else {
sess[key] = session[key];
}
}
const s = this._generateQuery(id);
s.session = sess;
if (session && session.cookie && session.cookie.expires) {
s[this.options.expiresKey] = new Date(session.cookie.expires);
} else {
const now = new Date();
s[this.options.expiresKey] = new Date(now.getTime() + this.options.expires);
}
this.db.collection(this.options.collection).
updateOne(this._generateQuery(id), { $set: s }, { upsert: true }, function(error) {
if (error) {
const e = new Error('Error setting ' + id + ' to ' +
require('util').inspect(session) + ': ' + error.message);
return _this._errorHandler(e, callback);
}
callback && callback();
});
};
MongoDBStore.prototype.on = function() {
this._emitter.on.apply(this._emitter, arguments);
};
MongoDBStore.prototype.once = function() {
this._emitter.once.apply(this._emitter, arguments);
};
return MongoDBStore;
};
function handleError(error, callback) {
if (this._emitter.listeners('error').length) {
this._emitter.emit('error', error);
}
if (callback) {
callback(error);
}
if (!this._emitter.listeners('error').length && !callback) {
throw error;
}
}