express-session-etcd3
Version:
An ETCD v3 store adapter for express-session using etcd3 client.
247 lines (239 loc) • 9.93 kB
JavaScript
import { Store } from 'express-session';
import { Etcd3 } from 'etcd3';
import debug from 'debug';
/*! *****************************************************************************
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
this file except in compliance with the License. You may obtain a copy of the
License at http://www.apache.org/licenses/LICENSE-2.0
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
MERCHANTABLITY OR NON-INFRINGEMENT.
See the Apache Version 2.0 License for specific language governing permissions
and limitations under the License.
***************************************************************************** */
/* global Reflect, Promise */
var extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
function __extends(d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
}
/**
* One day in seconds.
*/
var oneDay = 86400;
/**
* Max TTL time to leasing on ETCD3 Client in seconds.
*/
var maxTTL = 6442450;
/**
* Default configuration values for the etcd v3 options
*/
var defaultOptions = Object.freeze({
prefix: 'sess',
hosts: '127.0.0.1:2379',
skipTouch: false
});
/**
* An etcd v3 store adapter for Express session using [etcd3](https://github.com/mixer/etcd3) client.
*
* ```
* var session = require('express-session');
* var Etcd3Store = require('express-session-etcd3');
*
* app.use(session({
* store: new Etcd3Store(options),
* secret: 'keyboard cat',
* resave: false
* }));
* ```
*/
var Etcd3Store = /** @class */ (function (_super) {
__extends(Etcd3Store, _super);
function Etcd3Store(config, client) {
if (config === void 0) { config = defaultOptions; }
if (client === void 0) { client = new Etcd3(config); }
var _this = _super.call(this, config) || this;
_this.config = config;
_this.client = client;
_this.debug = debug('express-session:etcd3');
/**
* This method is used to get a session from the store given a session
* ID (`sid`). The `callback` should be called as `callback(error, session)`.
*
* The `session` argument should be a session if found, otherwise `null` or
* `undefined` if the session was not found (and there was no error). A special
* case is made when `error.code === 'ENOENT'` to act like `callback(null, null)`.
*/
_this.get = function (sid, callback) {
_this.debug('GET "%s"', sid);
try {
_this.client
.get(_this.key(sid))
.json()
.then(function (val) { return _this.callbackWithLog(callback, null, val); }, function (err) { return _this.callbackWithLog(callback, err); });
}
catch (err) {
_this.callbackWithLog(callback, err);
}
};
/**
* This required method is used to upsert a session into the store given a
* session ID (`sid`) and session (`session`) object. The callback should be
* called as `callback(error)` once the session has been set in the store.
*/
_this.set = function (sid, session, callback) {
var ttl = _this.getTTL(session, sid);
_this.debug('SET "%s" ttl:%s %O', sid, ttl, session);
try {
var leasing_1 = _this.client.lease(ttl);
leasing_1
.put(_this.key(sid))
.value(JSON.stringify(session))
.then(function () {
leasing_1.release();
_this.callbackWithLog(callback);
}, function (err) { return _this.callbackWithLog(callback, err); });
}
catch (err) {
callback(err);
}
};
/**
* This method is used to "touch" a given session given a
* session ID (`sid`) and session (`session`) object. The `callback` should be
* called as `callback(error)` once the session has been touched.
*
* This is primarily used when the store will automatically delete idle sessions
* and this method is used to signal to the store the given session is active,
* potentially resetting the idle timer.
*/
_this.touch = function (sid, session, callback) {
if (_this.config.skipTouch) {
_this.debug('SKIP TOUCH "%s"', sid);
callback(null);
return;
}
_this.debug('TOUCH "%s" %O', sid, session);
_this.set(sid, session, callback);
};
/**
* This method is used to get all sessions in the store as an array. The
* `callback` should be called as `callback(error, sessions)`.
*/
_this.all = function (callback) {
_this.debug('ALL');
try {
_this.client
.getAll()
.prefix(_this.key())
.json()
.then(function (json) { return Object.values(json); })
.then(function (val) { return _this.callbackWithLog(callback, null, val); }, function (err) { return _this.callbackWithLog(callback, err); });
}
catch (err) {
_this.callbackWithLog(callback, err);
}
};
/**
* This method is used to get the count of all sessions in the store.
* The `callback` should be called as `callback(error, len)`.
*/
_this.length = function (callback) {
_this.debug('LENGTH');
try {
_this.client
.getAll()
.prefix(_this.key())
.count()
.then(function (val) { return _this.callbackWithLog(callback, null, val); }, function (err) { return _this.callbackWithLog(callback, err); });
}
catch (err) {
_this.callbackWithLog(callback, err);
}
};
/**
* This method is used to destroy/delete a session from the store given
* a session ID (`sid`). The `callback` should be called as `callback(error)`
* once the session is destroyed.
*/
_this.destroy = function (sid, callback) {
_this.debug('DESTROY');
try {
_this.client
.delete()
.prefix(_this.key(sid))
.then(function () { return _this.callbackWithLog(callback); }, function (err) { return _this.callbackWithLog(callback, err); });
}
catch (err) {
callback(err);
}
};
/**
* This method is used to delete all sessions from the store. The `callback`
* should be called as `callback(error)` once the store is cleared.
*/
_this.clear = function (callback) {
_this.debug('CLEAR');
try {
_this.client
.delete()
.prefix(_this.key())
.then(function () { return _this.callbackWithLog(callback); }, function (err) { return _this.callbackWithLog(callback, err); });
}
catch (err) {
_this.callbackWithLog(callback, err);
}
};
_this.debug('init config: %O', config);
return _this;
}
/**
* Build the etcd key with the right prefix and the givin session ID (`sid`)
*/
Etcd3Store.prototype.key = function (sid) {
if (sid === void 0) { sid = ''; }
return (this.config.prefix || defaultOptions.prefix) + '/' + sid;
};
/**
* Get the Time to Live (`ttl`) of the session
*/
Etcd3Store.prototype.getTTL = function (sess, sid) {
var rawTTL = this.getRawTTL(sess, sid);
return rawTTL > maxTTL ? maxTTL : rawTTL;
};
/**
* Get the raw Time to Live (`ttl`) of the session from the data sources
*/
Etcd3Store.prototype.getRawTTL = function (sess, sid) {
var storeTtl = this['ttl'];
if (typeof storeTtl === 'number')
return storeTtl;
if (typeof storeTtl === 'string')
return Number(storeTtl);
if (typeof storeTtl === 'function')
return storeTtl(this, sess, sid);
if (storeTtl)
throw new TypeError('`store.ttl` must be a number or function.');
var maxAge = sess.cookie.maxAge;
return typeof maxAge === 'number' ? Math.floor(maxAge / 1000) : oneDay;
};
/**
* Logging callback result
*/
Etcd3Store.prototype.callbackWithLog = function (cb, err, value) {
if (err === void 0) { err = null; }
if (value === void 0) { value = null; }
var log = err ? ['ERR %O', err] : value ? ['DONE, data: %O', value] : ['DONE'];
this.debug.apply(this, [log.shift()].concat(log));
cb(err, value);
};
return Etcd3Store;
}(Store));
export default Etcd3Store;
export { oneDay, maxTTL, defaultOptions };
//# sourceMappingURL=express-session-etcd3.es5.js.map