Skip to content

Commit

Permalink
Session file store was added.
Browse files Browse the repository at this point in the history
  • Loading branch information
valery-barysok committed Oct 11, 2014
1 parent 992b564 commit 0ca45f7
Show file tree
Hide file tree
Showing 5 changed files with 312 additions and 0 deletions.
26 changes: 26 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
############################
# npm
############################
node_modules
npm-debug.log


############################
# tmp, editor & OS files
############################
.tmp
*.swo
*.swp
*.swn
*.swm
.DS_STORE
*#
*~
.idea
nbproject


############################
# Other
############################
.node_history
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,16 @@ session-file-store
==================

Session file store is a provision for storing session data in the session file

## Installation

$ npm install session-file-store

## Options

- `path` The directory where the session files will be stored. Defaults to `./sessions`
- `ttl` Time to live in seconds
- `retries` The number of retries to get session data from a session file. Defaults to 5
- `factor` Defaults to 1
- `minTimeout` Defaults to 50
- `maxTimeout` Defaults to 50
3 changes: 3 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module.exports = function(session) {
return require('./lib/session-file-store')(session);
};
237 changes: 237 additions & 0 deletions lib/session-file-store.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
var fs = require('graceful-fs'),
path = require('path'),
retry = require("retry");

/**
* One day in seconds
*/

var ONE_DAY = 24 * 60 * 60;

/**
* https://github.com/expressjs/session#session-store-implementation
*
* @param {object} session express session
* @return {Function} the `FileStore` extending `express`'s session Store
*
* @api public
*/
module.exports = function (session) {
var Store = session.Store;

/**
* Initialize FileStore with the given `options`
*
* @param {Object} options
* @api public
*/
function FileStore(options) {
var self = this;

options = options || {};
Store.call(self, options);

self.path = path.normalize(options.path || './sessions');
self.ttl = options.ttl;
self.retries = options.retries || 5;
self.factor = options.factor || 1;
self.minTimeout = options.minTimeout || 50;
self.maxTimeout = options.maxTimeout || 50;

self.filePattern = /\.json$/;

fs.mkdir(self.path, function (err) {
if (err && err.code != 'EEXIST') throw err;
});
}

/**
* Inherit from Store
*/
FileStore.prototype.__proto__ = Store.prototype;

/**
* Attempts to fetch session from a session file by the given `sessionId`
*
* @param {String} sessionId
* @param {Function} callback
*
* @api public
*/
FileStore.prototype.get = function (sessionId, callback) {
var self = this;

var sessionPath = path.join(this.path, sessionId + '.json'),
json;

var operation = retry.operation({
retries: self.retries,
factor: self.factor,
minTimeout: self.minTimeout,
maxTimeout: self.maxTimeout
});

operation.attempt(function (currentAttempt) {
fs.readFile(sessionPath, 'utf8', function (err, data) {
if (err) return callback(err);

try {
json = JSON.parse(data);
} catch (err) {
if (operation.retry(err)) {
console.log("[retry] will retry, error on last attempt: " + err);
return;
}
return callback(err);
}

callback(null, json);
});
});
};

/**
* Attempts to commit the given session associated with the given `sessionId` to a session file
*
* @param {String} sessionId
* @param {Object} session
* @param {Function} callback optional
*
* @api public
*/
FileStore.prototype.set = function (sessionId, session, callback) {
var self = this;

try {
session.__lastAccess = new Date().getTime();
session = JSON.stringify(session);

fs.writeFile(path.join(self.path, sessionId + '.json'), session, function (err) {
callback && err ? callback(err) : callback(null, JSON.parse(session));
});
} catch (err) {
callback && callback(err);
}
};

/**
* Attempts to unlink a given session by its id
*
* @param {String} sessionId Files are serialized to disk by their
* sessionId
* @param {Function} callback
*
* @api public
*/
FileStore.prototype.destroy = function (sessionId, callback) {
fs.unlink(path.join(this.path, sessionId + '.json'), function (err) {
err && callback(err);
});
};

/**
* Attempts to fetch number of the session files
*
* @param {Function} callback
*
* @api public
*/
FileStore.prototype.length = function (callback) {
var self = this;

fs.readdir(self.path, function (err, files) {
if (err) return callback(err);

var result = 0;
files.forEach(function (file) {
if (self.filePattern.exec(file)) {
++result;
}
});

callback(null, result);
});
};

/**
* Attempts to clear out all of the existing session files
*
* @param {Function} callback
*
* @api public
*/
FileStore.prototype.clear = function (callback) {
var self = this,
filePath;

fs.readdir(self.path, function (err, files) {
if (files.length <= 0) return callback();

var errors = [];
files.forEach(function (file, i) {
filePath = path.join(self.path, file);

if (self.filePattern.exec(file)) {
fs.unlink(filePath, function (err) {
if (err) {
errors.push(err);
}
if (i >= files.length - 1) {
callback(errors.length > 0 ? errors : undefined);
}
});
} else {
if (i >= files.length - 1) {
callback(errors.length > 0 ? errors : undefined);
}
}
});
});
};

/**
* Attempts to find all of the session files
*
* @param {Function} callback
*
* @api public
*/
FileStore.prototype.list = function (callback) {
var self = this;

fs.readdir(self.path, function (err, files) {
if (err) return callback(err);

var sessionFiles = files.filter(function (file) {
return self.filePattern.exec(file);
});

callback(null, sessionFiles);
});
};

/**
* Attempts to detect whether a session file is already expired or not
*
* @param {String} sessionId
* @param {Function} callback
*
* @api public
*/
FileStore.prototype.expired = function (sessionId, callback) {
var self = this,
now = new Date().getTime();
self.get(sessionId, function (err, session) {
if (err) return callback(err);

var maxAge = session.cookie.maxAge,
ttl = self.ttl || ('number' == typeof maxAge
? maxAge / 1000 | 0
: ONE_DAY);

err ? callback(err) : callback(null, session.__lastAccess + ttl < now);
});
};

return FileStore;
};
33 changes: 33 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"name": "session-file-store",
"version": "1.0.0",
"description": "Session file store is a provision for storing session data in the session file",
"main": "index.js",
"private": false,
"repository": {
"type": "git",
"url": "https://github.com/valery-barysok/session-file-store"
},
"keywords": [
"session",
"file",
"store"
],
"author": {
"name": "Valery Barysok"
},
"licenses": [
{
"type": "Apache-2.0",
"url": "http://www.apache.org/licenses/LICENSE-2.0"
}
],
"bugs": {
"url": "https://github.com/valery-barysok/session-file-store/issues"
},
"homepage": "https://github.com/valery-barysok/session-file-store",
"dependencies": {
"graceful-fs": "^3.0.3",
"retry": "^0.6.1"
}
}

0 comments on commit 0ca45f7

Please sign in to comment.