├── .gitignore ├── examples └── app.js ├── package.json ├── LICENSE ├── README.md └── lib └── express-session-mongo.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /examples/app.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | //Needed for monit/upstart 4 | process.chdir(__dirname); 5 | 6 | var xp = require('express'), 7 | MongoStore = require('express-session-mongo'); 8 | 9 | var app = module.exports = xp.createServer(); 10 | 11 | app.configure(function(){ 12 | app.use(xp.cookieDecoder()); 13 | app.use(xp.session({ store: new MongoStore() })); 14 | app.use(app.router); 15 | }); 16 | 17 | app.get('/', function(req, res) { 18 | res.send('HERE'); 19 | }); 20 | 21 | app.listen(8080); 22 | 23 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "express-session-mongo", 3 | "version": "0.1.0", 4 | "description": "MongoDB Session Store for ExpressJS", 5 | "author": "Dav Glass ", 6 | "bugs": { 7 | "url": "http://github.com/davglass/express-session-mongo/issues" 8 | }, 9 | "engines": { 10 | "node": ">=0.2.0" 11 | }, 12 | "directories": { 13 | "lib": "lib" 14 | }, 15 | "main": "./lib/express-session-mongo", 16 | "dependencies": { 17 | "express-session": "~1.2.1", 18 | "mongodb": ">=0.7.9" 19 | }, 20 | "licenses": [ 21 | { 22 | "type": "BSD", 23 | "url": "http://github.com/davglass/express-session-mongo/blob/master/LICENSE" 24 | } 25 | ], 26 | "repository": { 27 | "type": "git", 28 | "url": "http://github.com/davglass/express-session-mongo.git" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Software License Agreement (BSD License) 2 | 3 | Copyright (c) 2007, Dav Glass . 4 | All rights reserved. 5 | 6 | Redistribution and use of this software in source and binary forms, with or without modification, are 7 | permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above 10 | copyright notice, this list of conditions and the 11 | following disclaimer. 12 | 13 | * Redistributions in binary form must reproduce the above 14 | copyright notice, this list of conditions and the 15 | following disclaimer in the documentation and/or other 16 | materials provided with the distribution. 17 | 18 | * The name of Dav Glass may not be used to endorse or promote products 19 | derived from this software without specific prior 20 | written permission of Dav Glass. 21 | 22 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 23 | WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 24 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 25 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 28 | TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 29 | ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MongoDB Session Storage for ExpressJS 2 | 3 | This module is an addon for ExpressJS that adds a new Session Storage device. 4 | 5 | ## Install 6 | 7 | npm install https://github.com/trottski/express-session-mongo/archive/master.tar.gz 8 | 9 | ## Usage 10 | 11 | The standard usage, is to just pass an instantiated `MongoStore` instance to the session plugin. 12 | 13 | var xp = require('express'), 14 | MongoStore = require('express-session-mongo'); 15 | 16 | var app = xp.createServer(); 17 | 18 | app.configure(function(){ 19 | app.use(xp.cookieDecoder()); 20 | app.use(xp.session({ store: new MongoStore() })); 21 | app.use(app.router); 22 | }); 23 | 24 | You can also pass several options to the constructor to tweak your session store: 25 | 26 | * db - The name of the db to use, defaults to: `express-sessions` 27 | * ip - The IP address of the server to connect to, defaults to: `127.0.0.1` 28 | * port - The Port to connect to, defaults to: `27017` 29 | * collection - The collection to save it's data to, defaults to: `sessions` 30 | * server - A custom mongo Server instance (this overides db, ip & port): 31 | * fsync - Confirm writes after they have been flushed to disk, default: false. 32 | * native_parser - Use BSON native parser, defaults to: true. 33 | * username - The username for the database. 34 | * password - The password which corresponds to the database 35 | * authenciated - An err-first callback that fires once connected and an auth attempt is made. 36 | 37 |
var CustomServer = new Server(123.456.789.1, 12345, { auto_reconnect: true }, {});
38 | app.use(xp.session({ store: new MongoStore({ server: CustomServer }) }));
39 | 40 | ## Removing stale sessions 41 | 42 | MongoDB 2.2 and above supports doing this via an index, see http://docs.mongodb.org/manual/tutorial/expire-data/ 43 | To enable this, run 44 | 45 | db.sessions.ensureIndex( { "lastAccess": 1 }, { expireAfterSeconds: 3600 } ) 46 | 47 | Mongo will now remove all sessions older than an hour (every 60 seconds). 48 | 49 | ## Changes from davglass/express-session-mongo 50 | 51 | 1. Removes connect as a dependency 52 | 2. Adds fsync and native_parser options to constructor 53 | 3. Removes manual session cleanup cleanup code (see Removing stale sessions below) 54 | 55 | 56 | ## License 57 | 58 | Licensed under my standard BSD license. 59 | 60 | ### Based on these classes 61 | 62 | * [Sencha Connect Memory Store](https://github.com/senchalabs/connect/tree/master/lib/connect/middleware/session/memory.js) 63 | * [ciaranj's express-session-mongodb](https://github.com/ciaranj/express-session-mongodb) 64 | -------------------------------------------------------------------------------- /lib/express-session-mongo.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Based on the following classes: 3 | * https://github.com/senchalabs/connect/tree/master/lib/connect/middleware/session/memory.js 4 | * https://github.com/ciaranj/express-session-mongodb 5 | */ 6 | var mongo = require('mongodb'), 7 | util = require(process.binding('natives').util ? 'util' : 'sys'), 8 | Session = require('express-session'), 9 | Db = mongo.Db, 10 | Connection = mongo.Connection, 11 | Server = mongo.Server, 12 | BSON = mongo.BSONNative; 13 | 14 | var MongoStore = function(options) { 15 | options = options || {}; 16 | Session.Store.call(this, options); 17 | 18 | var server, 19 | dbName = (options.db) ? options.db : 'express-sessions', 20 | ip = (options.ip) ? options.ip : '127.0.0.1', 21 | port = (options.port) ? options.port : 27017, 22 | fsync = (typeof options.fsync !== 'undefined') ? options.fsync : false, 23 | nativeParser = (typeof options.native_parser !== 'undefined') ? options.native_parser : true; 24 | 25 | this._collection = (options.collection) ? options.collection : 'sessions'; 26 | 27 | if (options.server) { 28 | server = options.server; 29 | } else { 30 | server= new Server(ip, port, {auto_reconnect: true}, {}); 31 | } 32 | 33 | this._db = new Db(dbName, server, {fsync: fsync, native_parser: nativeParser}); 34 | this._db.open(function(err, db) { 35 | if (options.username && options.password) { 36 | db.authenticate(options.username, options.password, function(err, results) { 37 | (options.authenticated || function() {})(err, results); 38 | }); 39 | } 40 | }); 41 | }; 42 | 43 | util.inherits(MongoStore, Session.Store); 44 | 45 | MongoStore.prototype.set = function(sid, sess, fn) { 46 | this._db.collection(this._collection, function(err, collection) { 47 | collection.findOne({ _sessionid: sid }, function(err, session_data) { 48 | if (err) { 49 | fn && fn(err); 50 | } else { 51 | sess._sessionid = sid; 52 | var method = 'insert'; 53 | if (session_data) { 54 | sess.lastAccess = new Date() 55 | method = 'save'; 56 | } 57 | collection[method](sess, function(err, document) { 58 | if (err) { 59 | } else { 60 | fn && fn(null, sess); 61 | } 62 | }); 63 | } 64 | }); 65 | }); 66 | }; 67 | 68 | MongoStore.prototype.get = function(sid, fn) { 69 | this._db.collection(this._collection, function(err, collection) { 70 | collection.findOne({ _sessionid: sid }, function(err, session_data) { 71 | if (err) { 72 | fn && fn(err); 73 | } else { 74 | if (session_data) { 75 | session_data = cleanSessionData(session_data); 76 | } 77 | fn && fn(null, session_data); 78 | } 79 | }); 80 | }); 81 | }; 82 | 83 | MongoStore.prototype.destroy = function(sid, fn) { 84 | this._db.collection(this._collection, function(err, collection) { 85 | collection.remove({ _sessionid: sid }, function() { 86 | fn && fn(); 87 | }); 88 | }); 89 | }; 90 | 91 | MongoStore.prototype.length = function(fn) { 92 | this._db.collection(this._collection, function(err, collection) { 93 | collection.count(function(count) { 94 | fn && fn(null, count); 95 | }); 96 | }); 97 | }; 98 | 99 | MongoStore.prototype.all = function() { 100 | var arr = []; 101 | this._db.collection(this._collection, function(err, collection) { 102 | collection.find(function(err, cursor) { 103 | cursor.each(function(d) { 104 | d = cleanSessionData(d); 105 | arr.push(d); 106 | }); 107 | fn && fn(null, arr); 108 | }); 109 | }); 110 | }; 111 | 112 | MongoStore.prototype.clear = function(fn) { 113 | this._db.collection(this._collection, function(err, collection) { 114 | collection.remove(function() { 115 | fn && fn(); 116 | }); 117 | }); 118 | }; 119 | 120 | var cleanSessionData = function(json) { 121 | var data = {}; 122 | for (var i in json) { 123 | data[i] = json[i]; 124 | if (data[i] instanceof Object) { 125 | if ('low_' in data[i] || 'high_' in data[i]) { 126 | data[i] = data[i].toNumber(); 127 | } 128 | } 129 | 130 | } 131 | return data; 132 | }; 133 | 134 | module.exports = MongoStore; 135 | --------------------------------------------------------------------------------