UNPKG

express-session-etcd3

Version:

An ETCD v3 store adapter for express-session using etcd3 client.

247 lines (239 loc) 9.93 kB
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