sessionstore
Version:
Sessionstore is a node.js module for multiple databases. It can be very useful if you work with express or connect.
221 lines (192 loc) • 6.09 kB
JavaScript
var util = require('util'),
Session = require('../sessionInterface'),
use = require('../use'),
_ = require('lodash'),
async = require('async'),
jsondate = require('jsondate'),
elasticsearch = use('elasticsearch');
var ElasticSearchSessionStore = function (options) {
options = options || {};
Session.Store.call(this, options);
var defaults = {
index: 'express',
typeName: 'session',
prefix: '',
ttl: 60 * 60 * 24 * 14, // 14 days
pingInterval: 1000
};
_.defaults(options, defaults);
if (!options.hosts && !options.host) {
options.host = 'localhost:9200';
}
this.options = options;
this.index = this.options.index;
this.typeName = this.options.typeName;
};
util.inherits(ElasticSearchSessionStore, Session.Store);
_.extend(ElasticSearchSessionStore.prototype, {
connect: function (callback) {
var self = this;
this.isConnected = false;
this.client = new elasticsearch.Client(this.options);
var callbacked = false;
this.closeCalled = false;
var interval = setInterval(function () {
if (self.closeCalled) {
clearInterval(interval);
}
self.client.ping(function (err) {
if (err) {
if (self.isConnected) {
self.isConnected = false;
self.emit('disconnect');
}
if (callback && !callbacked) {
callbacked = true;
callback(err, self);
}
return;
}
if (!self.isConnected) {
// Github issue #39 - recover after temp ping error.
if (callbacked) {
// Already callbacked, so only restore isConnected state.
self.isConnected = true;
self.emit('connect');
} else {
// Not callbacked yet, so perform init logic and handle isConnected state.
self.client.indices.create({
index: self.index,
type: self.typeName
}, function(err) {
if (err && err.message.toLowerCase().indexOf('already') >= 0) {
err = null;
}
if (err) {
if (callback && !callbacked) {
callbacked = true;
callback(err, self);
}
return;
}
self.client.indices.putMapping({
index: self.index,
type: self.typeName,
body: {
session: {
_ttl: { enabled: true, default: '14d' }
}
}
}, function(err) {
if (err) {
if (callback && !callbacked) {
callbacked = true;
callback(err, self);
}
return;
}
self.isConnected = true;
self.emit('connect');
if (callback && !callbacked) {
callbacked = true;
callback(err, self);
}
});
});
}
}
});
}, this.options.pingInterval);
},
disconnect: function (callback) {
this.closeCalled = true;
if (this.client) this.client.close();
if (callback) callback(null);
},
set: function (sid, sess, callback) {
var ttl = this.options.ttl * 1000;
if (sess && sess.cookie && sess.cookie.expires) {
ttl = (new Date(sess.cookie.expires)).getTime() - Date.now();
sess.expiresAt = new Date(sess.cookie.expires);
} else {
sess.expiresAt = new Date(Date.now() + this.options.ttl * 1000);
}
// sess._version = sess._version || 1;
// sess._version++;
var self = this;
this.client.exists({
index: this.index,
type: this.typeName,
id: this.options.prefix + sid
}, function (err, exists) {
var methodName = undefined;
if (!exists) {
methodName = 'create';
}
self.client.index({
index: self.index,
type: self.typeName,
id: self.options.prefix + sid,
opType: methodName,
// version: sess._version > 2 ? sess._version - 1 : undefined,
//ttl: ttl + 'ms',
body: sess,
refresh: true
}, function (err, res) {
if (err && (err.message.toLowerCase().indexOf('version') >= 0)) {
return callback(new Error('ConcurrencyError: Session was updated by someone else!'));
}
callback(err, res);
});
});
},
//touch: function (sid, sess, callback) {
// this.set(sid, sess, callback);
//},
get: function (sid, callback) {
this.client.get({
index: this.index,
type: this.typeName,
id: this.options.prefix + sid
}, function (err, res) {
if (err && (err.message.toLowerCase().indexOf('not found') >= 0 || err.message.toLowerCase().indexOf('no such index') >= 0)) {
err = null;
}
if (err) return callback(err);
if (typeof res == 'undefined') return callback(null, null);
if (res._source) {
var sess = jsondate.parse(JSON.stringify(res._source));
if (sess.expiresAt && sess.expiresAt.getTime() > Date.now()) {
delete sess.expiresAt;
return callback(null, sess);
}
}
callback(null, null);
});
},
destroy: function (sid, callback) {
this.client.delete({
index: this.index,
type: this.typeName,
id: this.options.prefix + sid
}, function (err, res) {
if (err && err.message.toLowerCase().indexOf('not found') >= 0) {
err = null;
}
if (callback) callback(err);
});
},
clear: function (callback) {
var self = this;
this.client.indices.exists({index: this.index}, function (err, result) {
if (result){
self.client.indices.delete({index: self.index}, function (err) {
if (callback) callback(err);
});
} else {
if (callback) callback(err);
}
});
}
});
module.exports = ElasticSearchSessionStore;