@roadmunk/connect-mongo
Advanced tools
Comparing version 1.3.2 to 2.0.1
@@ -0,1 +1,18 @@ | ||
2.0.1 / 2018-01-04 | ||
================ | ||
* __Fix__ #271 TypeError: cb is not a function (brainthinks) | ||
2.0.0 / 2017-10-09 | ||
================= | ||
* __Drop__ Node.js 0.12 and io.js support | ||
* __Drop__ MongoDB 2.x support | ||
* __Drop__ mongodb driver < 2.0.36 support | ||
* __Drop__ mongoose < 4.1.2 support | ||
* __Fix__ `ensureIndex` deprecation warning ([#268](https://github.com/jdesboeufs/connect-mongo/issues/268), [#269](https://github.com/jdesboeufs/connect-mongo/pulls/269), [#270](https://github.com/jdesboeufs/connect-mongo/pulls/270)) | ||
* Improve `get()` ([#246](https://github.com/jdesboeufs/connect-mongo/pulls/246)) | ||
* Pass session in `touch` event | ||
* Remove `bluebird` from dependencies | ||
1.3.2 / 2016-07-27 | ||
@@ -14,3 +31,3 @@ ================= | ||
* __Add__ `create` and `update` events ([#215](https://github.com/kcbanner/connect-mongo/issues/215)) | ||
* __Add__ `create` and `update` events ([#215](https://github.com/jdesboeufs/connect-mongo/issues/215)) | ||
* Extend `mongodb` compatibility to `2.x` | ||
@@ -17,0 +34,0 @@ |
@@ -1,1 +0,1 @@ | ||
module.exports = require('./src'); | ||
module.exports = require('./src') |
{ | ||
"name": "@roadmunk/connect-mongo", | ||
"version": "1.3.2", | ||
"version": "2.0.1", | ||
"description": "MongoDB session store for Express and Connect", | ||
@@ -19,37 +19,28 @@ "keywords": [ | ||
"type": "git", | ||
"url": "git+https://github.com/kcbanner/connect-mongo.git" | ||
"url": "git+https://github.com/jdesboeufs/connect-mongo.git" | ||
}, | ||
"bugs": { | ||
"url": "https://github.com/kcbanner/connect-mongo/issues" | ||
"url": "https://github.com/jdesboeufs/connect-mongo/issues" | ||
}, | ||
"dependencies": { | ||
"bluebird": "^3.0", | ||
"mongodb": ">= 1.2.0 <3.0.0" | ||
"mongodb": "^2.0.36" | ||
}, | ||
"devDependencies": { | ||
"babel-cli": "^6.3.17", | ||
"babel-plugin-transform-es2015-arrow-functions": "^6.3.13", | ||
"babel-plugin-transform-es2015-block-scoping": "^6.3.13", | ||
"babel-plugin-transform-es2015-classes": "^6.3.15", | ||
"babel-plugin-transform-es2015-object-super": "^6.3.13", | ||
"babel-plugin-transform-object-assign": "^6.8.0", | ||
"babel-register": "^6.3.13", | ||
"eslint": "^3.1.1", | ||
"expect.js": "^0.3.1", | ||
"express-session": ">= 1.0.0", | ||
"istanbul": "^0.4.1", | ||
"mocha": "^2.3.4", | ||
"mongoose": ">= 2.6.0 < 5.0" | ||
"express-session": "^1.0.0", | ||
"mocha": "^5.0.1", | ||
"mongoose": "^4.1.2", | ||
"nyc": "^11.2.1", | ||
"xo": "^0.20.3" | ||
}, | ||
"scripts": { | ||
"test-unit-es5": "mocha --compilers js:babel-register", | ||
"test-unit": "mocha", | ||
"cover": "istanbul cover -x 'src-es5/**' _mocha", | ||
"lint": "eslint src test", | ||
"test": "npm run lint && npm run transpile && npm run cover", | ||
"test-es5": "npm run transpile && npm run test-unit-es5", | ||
"transpile": "babel src --out-dir src-es5", | ||
"prepublish": "npm run transpile" | ||
"lint": "xo src", | ||
"cover": "nyc report --reporter=text-lcov", | ||
"test": "nyc mocha" | ||
}, | ||
"homepage": "https://github.com/kcbanner/connect-mongo#readme", | ||
"xo": { | ||
"space": 2, | ||
"semicolon": false | ||
}, | ||
"homepage": "https://github.com/jdesboeufs/connect-mongo#readme", | ||
"main": "index.js", | ||
@@ -56,0 +47,0 @@ "directories": { |
@@ -7,5 +7,7 @@ # connect-mongo | ||
[![downloads](https://img.shields.io/npm/dm/connect-mongo.svg)](https://www.npmjs.com/package/connect-mongo) | ||
[![Build Status](https://travis-ci.org/kcbanner/connect-mongo.svg?branch=master)](https://travis-ci.org/kcbanner/connect-mongo) | ||
[![Coverage Status](https://coveralls.io/repos/kcbanner/connect-mongo/badge.svg?branch=master&service=github)](https://coveralls.io/github/kcbanner/connect-mongo?branch=master) | ||
[![Dependency Status](https://david-dm.org/kcbanner/connect-mongo.svg?style=flat)](https://david-dm.org/kcbanner/connect-mongo) | ||
[![Build Status](https://travis-ci.org/jdesboeufs/connect-mongo.svg?branch=master)](https://travis-ci.org/jdesboeufs/connect-mongo) | ||
[![Coverage Status](https://coveralls.io/repos/jdesboeufs/connect-mongo/badge.svg?branch=master&service=github)](https://coveralls.io/github/jdesboeufs/connect-mongo?branch=master) | ||
[![Dependency Status](https://david-dm.org/jdesboeufs/connect-mongo.svg?style=flat)](https://david-dm.org/jdesboeufs/connect-mongo) | ||
[![Greenkeeper badge](https://badges.greenkeeper.io/jdesboeufs/connect-mongo.svg)](https://greenkeeper.io/) | ||
[![XO code style](https://img.shields.io/badge/code_style-XO-5ed9c7.svg)](https://github.com/sindresorhus/xo) | ||
@@ -16,8 +18,8 @@ ## Compatibility | ||
* Support all Connect versions | ||
* Support [Mongoose](http://mongoosejs.com/index.html) `>= 2.6`, `3.x` and `4.x` | ||
* Support [native MongoDB driver](http://mongodb.github.io/node-mongodb-native/) `>= 1.2`, `2.x` | ||
* Support Node.js `0.10`, `0.12`, `4.x`, `5.x`, `6.x` and all [io.js](https://iojs.org) versions | ||
* Support [MongoDB](https://www.mongodb.com/) up to `3.2` | ||
* Support [Mongoose](http://mongoosejs.com/index.html) `>= 4.1.2+` | ||
* Support [native MongoDB driver](http://mongodb.github.io/node-mongodb-native/) `>= 2.0.36` | ||
* Support Node.js 4, 6 and 8 | ||
* Support [MongoDB](https://www.mongodb.com/) `>= 3.0` | ||
For older Node.js versions `0.10`, `0.12` and io.js, please read the [Node.js compatibility section](#old-nodejs-versions-compatibility) | ||
For extended compatibility, see previous versions. | ||
@@ -134,9 +136,2 @@ ## Usage | ||
## Old Node.js versions compatibility | ||
For versions `0.10`, `0.12` and io.js, you must use the ES5 fallback: | ||
```js | ||
var MongoStore = require('connect-mongo/es5')(session); | ||
``` | ||
## Session expiration | ||
@@ -248,4 +243,10 @@ | ||
## Showcase | ||
Open source projects and production apps using `connect-mongo`. Feel free to add yours in a pull request. | ||
* [Builder Book](https://github.com/builderbook/builderbook): Open source web app to write and host documentation or sell books. Built with React, Material-UI, Next, Express, Mongoose, MongoDB. | ||
## License | ||
The MIT License |
266
src/index.js
@@ -1,11 +0,20 @@ | ||
'use strict'; | ||
/* eslint indent: [error, 4] */ | ||
'use strict' | ||
const Promise = require('bluebird'); | ||
const MongoClient = require('mongodb'); | ||
const MongoClient = require('mongodb') | ||
function withCallback(promise, cb) { | ||
// Assume that cb is a function - type checks and handling type errors | ||
// can be done by caller | ||
if (cb) { | ||
promise | ||
.then(res => cb(null, res)) | ||
.catch(cb) | ||
} | ||
return promise | ||
} | ||
function defaultSerializeFunction(session) { | ||
// Copy each property of the session to a new object | ||
const obj = {}; | ||
let prop; | ||
const obj = {} | ||
let prop | ||
@@ -16,9 +25,9 @@ for (prop in session) { | ||
// This gets rid of the duplicate object under session.cookie.data property | ||
obj.cookie = session.cookie.toJSON ? session.cookie.toJSON() : session.cookie; | ||
obj.cookie = session.cookie.toJSON ? session.cookie.toJSON() : session.cookie | ||
} else { | ||
obj[prop] = session[prop]; | ||
obj[prop] = session[prop] | ||
} | ||
} | ||
return obj; | ||
return obj | ||
} | ||
@@ -30,5 +39,5 @@ | ||
serialize: options.serialize || defaultSerializeFunction, | ||
unserialize: options.unserialize || (x => x), | ||
}; | ||
unserialize: options.unserialize || (x => x) | ||
} | ||
} | ||
@@ -38,5 +47,5 @@ if (options.stringify === false || defaultStringify === false) { | ||
serialize: defaultSerializeFunction, | ||
unserialize: x => x, | ||
}; | ||
unserialize: x => x | ||
} | ||
} | ||
@@ -46,58 +55,57 @@ if (options.stringify === true || defaultStringify === true) { | ||
serialize: JSON.stringify, | ||
unserialize: JSON.parse, | ||
}; | ||
unserialize: JSON.parse | ||
} | ||
} | ||
} | ||
module.exports = function connectMongo(connect) { | ||
const Store = connect.Store || connect.session.Store; | ||
const MemoryStore = connect.MemoryStore || connect.session.MemoryStore; | ||
module.exports = function (connect) { | ||
const Store = connect.Store || connect.session.Store | ||
const MemoryStore = connect.MemoryStore || connect.session.MemoryStore | ||
class MongoStore extends Store { | ||
constructor(options) { | ||
options = options || {}; | ||
options = options || {} | ||
/* Fallback */ | ||
if (options.fallbackMemory && MemoryStore) { | ||
return new MemoryStore(); | ||
return new MemoryStore() | ||
} | ||
super(options); | ||
super(options) | ||
/* Options */ | ||
this.ttl = options.ttl || 1209600; // 14 days | ||
this.collectionName = options.collection || 'sessions'; | ||
this.autoRemove = options.autoRemove || 'native'; | ||
this.autoRemoveInterval = options.autoRemoveInterval || 10; | ||
this.transformFunctions = computeTransformFunctions(options, true); | ||
this.ttl = options.ttl || 1209600 // 14 days | ||
this.collectionName = options.collection || 'sessions' | ||
this.autoRemove = options.autoRemove || 'native' | ||
this.autoRemoveInterval = options.autoRemoveInterval || 10 | ||
this.transformFunctions = computeTransformFunctions(options, true) | ||
this.options = options; | ||
this.options = options | ||
this.changeState('init'); | ||
this.changeState('init') | ||
const newConnectionCallback = (err, db) => { | ||
if (err) { | ||
this.connectionFailed(err); | ||
this.connectionFailed(err) | ||
} else { | ||
this.handleNewConnectionAsync(db); | ||
this.handleNewConnectionAsync(db) | ||
} | ||
} | ||
}; | ||
if (options.url) { | ||
// New native connection using url + mongoOptions | ||
MongoClient.connect(options.url, options.mongoOptions || {}, newConnectionCallback); | ||
MongoClient.connect(options.url, options.mongoOptions || {}, newConnectionCallback) | ||
} else if (options.mongooseConnection) { | ||
// Re-use existing or upcoming mongoose connection | ||
if (options.mongooseConnection.readyState === 1) { | ||
this.handleNewConnectionAsync(options.mongooseConnection.db); | ||
this.handleNewConnectionAsync(options.mongooseConnection.db) | ||
} else { | ||
options.mongooseConnection.once('open', () => this.handleNewConnectionAsync(options.mongooseConnection.db)); | ||
options.mongooseConnection.once('open', () => this.handleNewConnectionAsync(options.mongooseConnection.db)) | ||
} | ||
} else if (options.db && options.db.listCollections) { | ||
// Re-use existing or upcoming native connection | ||
if (options.db.openCalled || options.db.openCalled === undefined) { // openCalled is undefined in mongodb@2.x | ||
this.handleNewConnectionAsync(options.db); | ||
if (options.db.openCalled || options.db.openCalled === undefined) { // OpenCalled is undefined in mongodb@2.x | ||
this.handleNewConnectionAsync(options.db) | ||
} else { | ||
options.db.open(newConnectionCallback); | ||
options.db.open(newConnectionCallback) | ||
} | ||
@@ -107,35 +115,34 @@ } else if (options.dbPromise) { | ||
.then(db => this.handleNewConnectionAsync(db)) | ||
.catch(err => this.connectionFailed(err)); | ||
.catch(err => this.connectionFailed(err)) | ||
} else { | ||
throw new Error('Connection strategy not found'); | ||
throw new Error('Connection strategy not found') | ||
} | ||
this.changeState('connecting'); | ||
this.changeState('connecting') | ||
} | ||
connectionFailed(err) { | ||
this.changeState('disconnected'); | ||
throw err; | ||
this.changeState('disconnected') | ||
throw err | ||
} | ||
handleNewConnectionAsync(db) { | ||
this.db = db; | ||
this.db = db | ||
return this | ||
.setCollection(db.collection(this.collectionName)) | ||
.setAutoRemoveAsync() | ||
.then(() => this.changeState('connected')); | ||
.then(() => this.changeState('connected')) | ||
} | ||
setAutoRemoveAsync() { | ||
let removeQuery = { expires: { $lt: new Date() } }; | ||
const removeQuery = {expires: {$lt: new Date()}} | ||
switch (this.autoRemove) { | ||
case 'native': | ||
return this.collection.ensureIndexAsync({ expires: 1 }, { expireAfterSeconds: 0 }); | ||
return this.collection.createIndex({expires: 1}, {expireAfterSeconds: 0}) | ||
case 'interval': | ||
this.timer = setInterval(() => this.collection.remove(removeQuery, { w: 0 }), this.autoRemoveInterval * 1000 * 60); | ||
this.timer.unref(); | ||
return Promise.resolve(); | ||
this.timer = setInterval(() => this.collection.remove(removeQuery, {w: 0}), this.autoRemoveInterval * 1000 * 60) | ||
this.timer.unref() | ||
return Promise.resolve() | ||
default: | ||
return Promise.resolve(); | ||
return Promise.resolve() | ||
} | ||
@@ -146,4 +153,4 @@ } | ||
if (newState !== this.state) { | ||
this.state = newState; | ||
this.emit(newState); | ||
this.state = newState | ||
this.emit(newState) | ||
} | ||
@@ -154,35 +161,25 @@ } | ||
if (this.timer) { | ||
clearInterval(this.timer); | ||
clearInterval(this.timer) | ||
} | ||
this.collectionReadyPromise = undefined; | ||
this.collection = collection; | ||
this.collectionReadyPromise = undefined | ||
this.collection = collection | ||
// Promisify used collection methods | ||
['count', 'findOne', 'remove', 'drop', 'ensureIndex'].forEach(method => { | ||
collection[method + 'Async'] = Promise.promisify(collection[method], { context: collection }); | ||
}); | ||
collection.updateAsync = Promise.promisify(collection.update, { context: collection, multiArgs: true }); | ||
return this; | ||
return this | ||
} | ||
collectionReady() { | ||
let promise = this.collectionReadyPromise; | ||
let promise = this.collectionReadyPromise | ||
if (!promise) { | ||
promise = new Promise((resolve, reject) => { | ||
switch (this.state) { | ||
case 'connected': | ||
resolve(this.collection); | ||
break; | ||
case 'connecting': | ||
this.once('connected', () => resolve(this.collection)); | ||
break; | ||
case 'disconnected': | ||
reject(new Error('Not connected')); | ||
break; | ||
if (this.state === 'connected') { | ||
return resolve(this.collection) | ||
} | ||
if (this.state === 'connecting') { | ||
return this.once('connected', () => resolve(this.collection)) | ||
} | ||
}); | ||
this.collectionReadyPromise = promise; | ||
reject(new Error('Not connected')) | ||
}) | ||
this.collectionReadyPromise = promise | ||
} | ||
return promise; | ||
return promise | ||
} | ||
@@ -192,6 +189,5 @@ | ||
if (this.options.transformId && typeof this.options.transformId === 'function') { | ||
return this.options.transformId(sessionId); | ||
} else { | ||
return sessionId; | ||
return this.options.transformId(sessionId) | ||
} | ||
return sessionId | ||
} | ||
@@ -202,28 +198,27 @@ | ||
get(sid, callback) { | ||
return this.collectionReady() | ||
.then(collection => collection.findOneAsync({ | ||
return withCallback(this.collectionReady() | ||
.then(collection => collection.findOne({ | ||
_id: this.computeStorageId(sid), | ||
$or: [ | ||
{ expires: { $exists: false } }, | ||
{ expires: { $gt: new Date() } }, | ||
], | ||
{expires: {$exists: false}}, | ||
{expires: {$gt: new Date()}} | ||
] | ||
})) | ||
.then(session => { | ||
if (session) { | ||
const s = this.transformFunctions.unserialize(session.session); | ||
const s = this.transformFunctions.unserialize(session.session) | ||
if (this.options.touchAfter > 0 && session.lastModified) { | ||
s.lastModified = session.lastModified; | ||
s.lastModified = session.lastModified | ||
} | ||
this.emit('get', sid); | ||
return s; | ||
this.emit('get', sid) | ||
return s | ||
} | ||
}) | ||
.asCallback(callback); | ||
, callback) | ||
} | ||
set(sid, session, callback) { | ||
// removing the lastModified prop from the session object before update | ||
// Removing the lastModified prop from the session object before update | ||
if (this.options.touchAfter > 0 && session && session.lastModified) { | ||
delete session.lastModified; | ||
delete session.lastModified | ||
} | ||
@@ -237,7 +232,7 @@ | ||
} catch (err) { | ||
return callback(err); | ||
return callback(err) | ||
} | ||
if (session && session.cookie && session.cookie.expires) { | ||
s.expires = new Date(session.cookie.expires); | ||
s.expires = new Date(session.cookie.expires) | ||
} else { | ||
@@ -251,48 +246,47 @@ // If there's no expiration date specified, it is | ||
// or the default specified in the options. | ||
s.expires = new Date(Date.now() + this.ttl * 1000); | ||
s.expires = new Date(Date.now() + (this.ttl * 1000)) | ||
} | ||
if (this.options.touchAfter > 0) { | ||
s.lastModified = new Date(); | ||
s.lastModified = new Date() | ||
} | ||
return this.collectionReady() | ||
.then(collection => collection.updateAsync({ _id: this.computeStorageId(sid) }, s, { upsert: true })) | ||
.then(responseArray => { | ||
const rawResponse = responseArray.length === 2 ? responseArray[1] : responseArray[0].result; | ||
return withCallback(this.collectionReady() | ||
.then(collection => collection.update({_id: this.computeStorageId(sid)}, s, {upsert: true})) | ||
.then(rawResponse => { | ||
if (rawResponse.result) { | ||
rawResponse = rawResponse.result | ||
} | ||
if (rawResponse && rawResponse.upserted) { | ||
this.emit('create', sid); | ||
this.emit('create', sid) | ||
} else { | ||
this.emit('update', sid); | ||
this.emit('update', sid) | ||
} | ||
this.emit('set', sid); | ||
this.emit('set', sid) | ||
}) | ||
.asCallback(callback); | ||
, callback) | ||
} | ||
touch(sid, session, callback) { | ||
const updateFields = {}, | ||
touchAfter = this.options.touchAfter * 1000, | ||
lastModified = session.lastModified ? session.lastModified.getTime() : 0, | ||
currentDate = new Date(); | ||
const updateFields = {} | ||
const touchAfter = this.options.touchAfter * 1000 | ||
const lastModified = session.lastModified ? session.lastModified.getTime() : 0 | ||
const currentDate = new Date() | ||
// if the given options has a touchAfter property, check if the | ||
// If the given options has a touchAfter property, check if the | ||
// current timestamp - lastModified timestamp is bigger than | ||
// the specified, if it's not, don't touch the session | ||
if (touchAfter > 0 && lastModified > 0) { | ||
const timeElapsed = currentDate.getTime() - session.lastModified | ||
const timeElapsed = currentDate.getTime() - session.lastModified; | ||
if (timeElapsed < touchAfter) { | ||
return callback(); | ||
} else { | ||
updateFields.lastModified = currentDate; | ||
return callback() | ||
} | ||
updateFields.lastModified = currentDate | ||
} | ||
if (session && session.cookie && session.cookie.expires) { | ||
updateFields.expires = new Date(session.cookie.expires); | ||
updateFields.expires = new Date(session.cookie.expires) | ||
} else { | ||
updateFields.expires = new Date(Date.now() + this.ttl * 1000); | ||
updateFields.expires = new Date(Date.now() + (this.ttl * 1000)) | ||
} | ||
@@ -308,30 +302,30 @@ | ||
} | ||
return this.collectionReady() | ||
.then(collection => collection.updateAsync({ _id: this.computeStorageId(sid), rmlastModified: oldRmlastModified }, { $set: updateFields })) | ||
.then(commandResults => { | ||
return withCallback(this.collectionReady() | ||
.then(collection => collection.update({ _id: this.computeStorageId(sid), rmlastModified: oldRmlastModified }, { $set: updateFields })) | ||
.then(commandResult => { | ||
// silent failure | ||
if (!(commandResults.length > 0 && commandResults[0].result.nModified === 0)) { | ||
this.emit('touch', sid); | ||
this.emit('touch', sid, session); | ||
} | ||
}) | ||
.asCallback(callback); | ||
, callback) | ||
} | ||
destroy(sid, callback) { | ||
return this.collectionReady() | ||
.then(collection => collection.removeAsync({ _id: this.computeStorageId(sid) })) | ||
return withCallback(this.collectionReady() | ||
.then(collection => collection.remove({_id: this.computeStorageId(sid)})) | ||
.then(() => this.emit('destroy', sid)) | ||
.asCallback(callback); | ||
, callback) | ||
} | ||
length(callback) { | ||
return this.collectionReady() | ||
.then(collection => collection.countAsync({})) | ||
.asCallback(callback); | ||
return withCallback(this.collectionReady() | ||
.then(collection => collection.count({})) | ||
, callback) | ||
} | ||
clear(callback) { | ||
return this.collectionReady() | ||
.then(collection => collection.dropAsync()) | ||
.asCallback(callback); | ||
return withCallback(this.collectionReady() | ||
.then(collection => collection.drop()) | ||
, callback) | ||
} | ||
@@ -341,3 +335,3 @@ | ||
if (this.db) { | ||
this.db.close(); | ||
this.db.close() | ||
} | ||
@@ -347,3 +341,3 @@ } | ||
return MongoStore; | ||
}; | ||
return MongoStore | ||
} |
Sorry, the diff of this file is not supported yet
160991
1
6
249
9
624
- Removedbluebird@^3.0
- Removedbluebird@3.7.2(transitive)
Updatedmongodb@^2.0.36