Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Initial Checkin

  • Loading branch information...
commit 39c079608ac44420449aef835506db4292b97a5c 1 parent e547af6
@odogono authored
View
41 README.markdown
@@ -0,0 +1,41 @@
+# connect-session-file
+
+
+## Installation
+
+
+## Options
+
+ - `path` storage path (optional, default: process.env.TMPDIR)
+ - `prefix` filename prefix (optional, default: `file-store-`)
+ - `useAsync` use asynchronous file operations (optional, default: false)
+ - `printDebug` prints debug output (optional, default: false)
+ - `reapInterval` interval between removing stale sessions (optional, default: 600000 (10 mins), disable: -1 )
+ - `maxAge` maximum age of sessions before removal (optional, default: 600000*3 (30 mins) )
+
+## Example
+
+See example/app.js
+
+With express:
+
+ var FileStore = require('connect-session-file');
+
+ app.use(express.session({
+ secret: settings.cookie_secret,
+ store: new FileSessionStore({
+ db: settings.db
+ })
+ }));
+
+
+## Tests
+
+
+
+
+## See Also
+
+https://github.com/kcbanner/connect-mongo
+
+https://github.com/bartt/connect-session-mongo
View
32 example/app.js
@@ -0,0 +1,32 @@
+#!/usr/bin/env node
+
+var connect = require('connect'),
+ FileSessionStore = require('../lib/connect-session-file');
+
+connect(
+ connect.cookieParser()
+ , connect.session({
+ secret:'session file'
+ , store: new FileSessionStore({path:'.', printDebug:true, useAsync:true})
+ })
+ , connect.favicon()
+ , function(req,res,next) {
+ var sess = req.session;
+ console.log('+ begin ' + req.url );
+ if (sess.views) {
+ sess.views++;
+ res.setHeader('Content-Type', 'text/html');
+ res.write('<p>views: ' + sess.views + '</p>');
+ res.write('<p>expires in: ' + (sess.cookie.maxAge / 1000) + 's</p>');
+ res.write('<img src="blank.png"/>');
+ res.write('<img src="blank2.png"/>');
+ res.write('<img src="blank3.png"/>');
+ res.write('<img src="blank4.png"/>');
+ res.write('<img src="blank5.png"/>');
+ res.end();
+ } else {
+ sess.views = 1;
+ res.end('welcome to the file session demo. refresh!');
+ }
+ }
+).listen(8080);
View
280 lib/connect-session-file.js
@@ -0,0 +1,280 @@
+
+/**
+ Module dependencies.
+ */
+var sys = require('sys'),
+ path = require('path'),
+ util = require('util'),
+ fs = require('fs'),
+ crypto = require('crypto'),
+ Store = require('connect').middleware.session.Store;
+
+/**
+ Initialize FileSessionStore with given `opts`.
+
+ @param {Object} opts Options.
+ @api public
+ */
+var FileSessionStore = module.exports = function FileSessionStore(opts) {
+ opts = opts || {};
+ Store.call(this, opts);
+
+ // define default session store directory
+ this.prefix = opts.prefix || 'file-store-';
+ this.path = opts.path || process.env.TMPDIR;
+ this.useAsync = opts.useAsync;
+ this.printDebug = opts.printDebug;
+
+ this.filePattern = new RegExp( '^' + this.prefix + '.*' );
+ this.count = 0;
+
+ // set default reapInterval to 10 minutes
+ this.reapInterval = opts.reapInterval || 600000;
+ this.maxAge = opts.maxAge || 600000 * 3;
+
+ // interval for reaping stale sessions
+ if (this.reapInterval !== -1) {
+ setInterval(function (self) {
+ self.reap(self.maxAge);
+ }, this.reapInterval, this);
+ }
+};
+
+sys.inherits(FileSessionStore, Store);
+
+/**
+ Reap sessions older than `ms` milliseconds.
+
+ @param {Number} ms Milliseconds threshold.
+ @api private
+ */
+FileSessionStore.prototype.reap = function (ms) {
+ var threshold = + new Date() - ms;
+ var self = this; // store 'this' object
+ var val, filePath;
+
+ // TODO AV : check the files we are reading match the prefix and are not directories
+
+ fs.readdir( self.path, function(err, files){
+ if( files.length <= 0 ){
+ fn(null,result);
+ return;
+ }
+ files.forEach(function(f,i){
+ if( !self.filePattern.exec(f) )
+ return;
+ filePath = path.join(self.path,f);
+ fs.readFile( filePath, function (err, data) {
+ if( err == null ){
+ val = JSON.parse(data);
+ if (val.lastAccess < threshold) {
+ fs.unlink( filePath );
+ }
+ }
+ });
+ });
+ });
+};
+
+/**
+ Attemp to fetch sessin by the given `sid`.
+
+ @param {String} sid Session ID.
+ @param {Function} fn Function, that called after get.
+ @api public
+ */
+FileSessionStore.prototype.get = function(sid, fn) {
+ var serial = this.count++; //Math.round(Math.random()*1000);
+ var fileName = this.prefix + crypto.createHash('md5').update(sid).digest('hex');
+ var filePath = path.join( this.path, fileName );
+ var printDebug = this.printDebug;
+ fn = fn || function () {};
+
+ if( !this.useAsync ){
+ if( path.existsSync(filePath) ){
+ var data = fs.readFileSync( filePath );
+ if( printDebug )
+ console.log(serial + ' get sync OK [' + fileName + ']' + data + '.' );
+ fn( null, JSON.parse(data) );
+ }else{
+ if( printDebug )
+ console.log(serial + ' get sync FAIL [' + fileName + '] - no data found');
+ fn();
+ }
+ return;
+ }
+
+ if( printDebug )
+ console.log(serial + ' get [' + fileName + ']');
+
+ path.exists( filePath, function (exists) {
+ if( exists ){
+ fs.readFile( filePath, function (err, data) {
+ if(err || data.length <= 0){
+ if( printDebug )
+ console.log(serial + ' get FAIL [' + fileName + '] - no data found');
+ fn();
+ }else{
+ if( printDebug )
+ console.log(serial + ' get OK [' + fileName + ']' + data + '.' );
+ fn( null, JSON.parse(data) );
+ }
+ });
+ } else{
+ if( printDebug )
+ console.log(serial + ' get FAIL [' + fileName + '] not exists');
+ fn();
+ }
+ });
+};
+
+/**
+ Commit the given `sess` object associated with the given `sid`.
+
+ @param {String} sid Session ID.
+ @param {Session} sess Session values.
+ @param {Function} fn Function, that called after set.
+ @api public
+ */
+FileSessionStore.prototype.set = function (sid, sess, fn) {
+ var serial = this.count++; //Math.round(Math.random()*1000);
+ var fileName = this.prefix + crypto.createHash('md5').update(sid).digest('hex');
+ var printDebug = this.printDebug;
+ if( printDebug )
+ console.log(serial + ' set [' + fileName + '] = ' + JSON.stringify(sess));
+
+ var content = JSON.stringify(sess);
+ var filePath = path.join( this.path, fileName );
+
+ if( !this.useAsync ){
+ fs.writeFileSync( filePath, content );
+ if( printDebug )
+ console.log(serial + ' set sync OK [' + filePath + '] = ' + JSON.stringify(sess));
+ fn && fn();
+ return;
+ }
+
+ fs.writeFile( filePath, content, function(err){
+ if( err && printDebug )
+ console.log(serial + 'set err ' + err );
+ if( printDebug )
+ console.log(serial + ' set OK [' + filePath + '] = ' + JSON.stringify(sess));
+ fn && fn();
+ });
+};
+
+/**
+ Destroy the session associated with the given `sid`.
+
+ @param {String} sid Session ID.
+ @param {Function} fn Function, that called after value delete.
+ @api public
+ */
+FileSessionStore.prototype.destroy = function (sid, fn) {
+ var fileName = this.prefix + crypto.createHash('md5').update(sid).digest('hex');
+ var filePath = path.join( this.path, fileName );
+ fn = fn || function () {};
+
+ path.exists( filePath, function (exists) {
+ if( exists ) {
+ fs.unlink( filePath, function (err, data) {
+ fn();
+ });
+ } else{
+ fn();
+ }
+ });
+};
+
+/**
+ Invoke the given callback `fn` with all active sessions.
+ Method wasn't tested!
+
+ @param {Function} fn Function that applyed to all active sessions.
+ @api public
+ */
+FileSessionStore.prototype.all = function (fn) {
+ var self = this;
+ var result = [];
+ fn = fn || function () {};
+
+
+ fs.readdir( self.path, function(err, files){
+ if( files.length <= 0 ){
+ fn(null,result);
+ return;
+ }
+ files.forEach(function(f,i){
+
+ if( self.filePattern.exec(f) ){
+ fs.readFile( path.join(self.path,f), function (err, data) {
+ if( err == null && data ){
+ result.push( JSON.parse(data) );
+ }
+ if( i >= files.length-1 )
+ fn(null, result);
+ });
+ }else{
+ if( i >= files.length-1 )
+ fn(null, result);
+ }
+ });
+ });
+};
+
+/**
+ Clear all sessions.
+
+ @param {Function} fn Function, that calls after removing all sessions.
+ @api public
+ */
+FileSessionStore.prototype.clear = function (fn) {
+
+ var self = this; // store 'this' object
+ var filePath;
+ fn = fn || function () {};
+
+ fs.readdir( self.path, function(err, files){
+ if( files.length <= 0 ){
+ fn(null,result);
+ return;
+ }
+ files.forEach(function(f,i){
+ filePath = path.join(self.path,f);
+
+ if( self.filePattern.exec(f) ){
+ // log('deleting ' + filePath );
+ fs.unlink( filePath, function (err) {
+ if( i >= files.length-1 )
+ fn();
+ });
+ }else{
+ if( i >= files.length-1 )
+ fn();
+ }
+ });
+ });
+
+};
+
+/**
+ Fetch number of sessions.
+
+ @param {Function} fn Function, that accepts number of sessions.
+ @api public
+ */
+FileSessionStore.prototype.length = function (fn) {
+ var self = this;
+ var result = [];
+ var result = 0;
+ fn = fn || function () {};
+
+ fs.readdir( self.path, function(err, files){
+ files.forEach( function(f){
+ if( self.filePattern.exec(f) ){
+ result++;
+ }
+ })
+ fn( null, result );
+ });
+};
View
22 package.json
@@ -0,0 +1,22 @@
+{
+ "name": "connect-session-file",
+ "version": "1.0.0",
+ "description": "File Session Store for Connect Middleware",
+ "author": "Alexander Veenendaal <alex@odogono.com>",
+ "bugs": { "web" : "http://github.com/odogono/connect-session-file/issues" },
+ "os": ["darwin", "linux"],
+ "engines": {
+ "node" : ">=0.2.0"
+ },
+ "directories": {
+ "lib" : "lib"
+ },
+ "main": "./lib/connect-session-file",
+ "dependencies": {
+ "connect": ">=0.2.4"
+ },
+ "repository": {
+ "type":"git",
+ "url":"http://github.com/odogono/connect-session-file.git"
+ }
+}
Please sign in to comment.
Something went wrong with that request. Please try again.