express-elasticsearch-session
Version:
ElasticSearch session store, using elasticsearch-js for communications with server
183 lines (164 loc) • 4.57 kB
JavaScript
/**
* Session store for Express, backed by elasticsearch
*
* Usage:
* var express = require('express')
* , session = require('express-session')
* , ESStore = require('express-elasticsearch-session')(session);
*
* Then:
* server.use(session({
* secret : 'yoursecret',
* key : 'yoursessionkey',
* cookie : {
* path : '/',
* httpOnly : true,
* maxAge : 365 * 24 * 3600 * 1000 // One year for example
* },
* store : new ESStore()
* }));
*/
"use strict";
var util = require("util"),
elasticsearch = require('elasticsearch');
module.exports = function (session) {
/**
* Constructor
* @param {String} options.host ElasticSearch host (default: "localhost:9200")
* @param {String} options.index ElasticSearch's session index (default: "express")
* @param {String} options.typeName ElasticSearch's session typename (default: "session")
* @param {String} options.ttl (default: 1h)
* @param {String} options.prefix (default: "")
* @param {String} options.logLevel ElasticSearch log-level (default: "trace")
*/
function ESStore(options) {
var defaults = {
host: "localhost:9200",
index: "express",
typeName: "session",
ttl: 1000 * 60 * 60,
prefix: "",
logLevel: "trace"
};
this.options = util._extend(defaults, options || {});
this.client = new elasticsearch.Client({
host: this.options.host,
log: this.options.logLevel
});
this.initialSessionTimeout()
}
util.inherits(ESStore, session.Store);
ESStore.prototype.pSid = function(sid) {
return (this.options.prefix) + sid;
}
/**
* Get session data
*/
ESStore.prototype.get = function (sid, cb) {
this.client.get({
index: this.options.index,
type: this.options.typeName,
id: this.pSid(sid)
}, (e, r) => {
if( typeof cb !== "function" ) {
cb = () => {};
}
if ( e || typeof r === 'undefined' || new Date().getTime() - r._source.timestamp > this.options.ttl ) {
return cb();
}
cb(null, r._source);
})
};
/**
* Set session data
*/
ESStore.prototype.set = function (sid, sess, cb) {
sess.timestamp = new Date().getTime();
this.client.index({
index: this.options.index,
type: this.options.typeName,
id: this.pSid(sid),
body: sess
}, function (e, r) {
if( typeof cb === "function" ) {
cb(e);
}
});
};
/**
* Destroy a session's data
*/
ESStore.prototype.destroy = function (sid, cb) {
this.client.delete({
index: this.options.index,
type: this.options.typeName,
id: this.pSid(sid)
}, function (e, r) {
if( typeof cb === "function" ) {
cb(e, r);
}
});
};
/**
* Set up initial timeout after service restart
*/
ESStore.prototype.initialSessionTimeout = function () {
var self = this
this.timeouts = {};
this.client.search({
index: this.options.index,
type: this.options.typeName,
body: {
query: {
match_all: {}
}
},
_source: false
}, function (e, r) {
if (e) {
console.error(e)
}
var hits = r.hits.hits
if (hits) {
hits.forEach(function (hit) {
self.sessionTimeout(hit._id)
})
}
})
}
/**
* Clear existing timeout for session deletion and refresh
*/
ESStore.prototype.sessionTimeout = function (sid) {
var self = this
if ( this.timeouts[this.pSid(sid)] ) {
clearTimeout(this.timeouts[this.pSid(sid)]);
}
this.timeouts[this.pSid(sid)] = setTimeout(function () {
self.destroy(sid);
}, this.options.ttl);
};
/**
* Refresh a session's expiry
*/
ESStore.prototype.touch = function (sid, sess, cb) {
this.sessionTimeout(sid)
this.client.update({
index: this.options.index,
type: this.options.typeName,
id: this.pSid(sid),
body: {
script: {
inline: "ctx._source.cookie.expires = Instant.ofEpochMilli(params.now).plusMillis(ctx._source.cookie.originalMaxAge).toString();",
lang: "painless",
params: { now: new Date().getTime() }
}
}
}, function (e, r) {
if ( typeof cb === "function" ) {
cb(e, r);
}
});
};
return ESStore;
};