redis-sessions
Advanced tools
Comparing version 2.1.0 to 3.0.0
@@ -1,3 +0,10 @@ | ||
# C | ||
# CHANGELOG | ||
## 3.0.0 | ||
* Dropped suppoer for Node 4 and Node 6 | ||
* Added test support for Node 12 | ||
* Updated all dependencies | ||
* Added LICENSE.md file | ||
## 2.1.0 | ||
@@ -4,0 +11,0 @@ |
1113
index.js
@@ -1,198 +0,325 @@ | ||
// Generated by CoffeeScript 1.12.7 | ||
// Generated by CoffeeScript 2.5.1 | ||
var EventEmitter, NodeCache, RedisInst, RedisSessions, _, | ||
boundMethodCheck = function(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new Error('Bound instance method accessed before binding'); } }; | ||
/* | ||
Redis Sessions | ||
_ = require("lodash"); | ||
The MIT License (MIT) | ||
RedisInst = require("redis"); | ||
Copyright © 2013-2018 Patrick Liess, http://www.tcs.de | ||
EventEmitter = require("events").EventEmitter; | ||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | ||
NodeCache = require("node-cache"); | ||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | ||
RedisSessions = (function() { | ||
// # RedisSessions | ||
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
*/ | ||
var EventEmitter, NodeCache, RedisInst, RedisSessions, _, | ||
bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, | ||
extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, | ||
hasProp = {}.hasOwnProperty; | ||
// To create a new instance use: | ||
_ = require("lodash"); | ||
// RedisSessions = require("redis-sessions") | ||
// rs = new RedisSessions() | ||
RedisInst = require("redis"); | ||
// Parameters: | ||
EventEmitter = require("events").EventEmitter; | ||
// `port`: *optional* Default: `6379`. The Redis port. | ||
// `host`, *optional* Default: `127.0.0.1`. The Redis host. | ||
// `options`, *optional* Default: `{}`. Additional options. See [https://github.com/mranney/node_redis#rediscreateclientport-host-options](redis.createClient)) | ||
// `namespace`: *optional* Default: `rs`. The namespace prefix for all Redis keys used by this module. | ||
// `wipe`: *optional* Default: `600`. The interval in second after which the timed out sessions are wiped. No value less than 10 allowed. | ||
// `client`: *optional* An external RedisClient object which will be used for the connection. | ||
// `cachetime` (Number) *optional* Number of seconds to cache sessions in memory. Can only be used if no `client` is supplied. See the "Cache" section. Default: `0`. | ||
NodeCache = require("node-cache"); | ||
class RedisSessions extends EventEmitter { | ||
constructor(o = {}) { | ||
var isclient, redissub, ref, ref1, wipe; | ||
super(o); | ||
// ## Activity | ||
RedisSessions = (function(superClass) { | ||
extend(RedisSessions, superClass); | ||
// Get the number of active unique users (not sessions!) within the last *n* seconds | ||
function RedisSessions(o) { | ||
var isclient, redissub, ref, ref1, wipe; | ||
if (o == null) { | ||
o = {}; | ||
} | ||
this._wipe = bind(this._wipe, this); | ||
this._returnSessions = bind(this._returnSessions, this); | ||
this._initErrors = bind(this._initErrors, this); | ||
this._handleError = bind(this._handleError, this); | ||
this.soid = bind(this.soid, this); | ||
this.soapp = bind(this.soapp, this); | ||
this.set = bind(this.set, this); | ||
this.quit = bind(this.quit, this); | ||
this.ping = bind(this.ping, this); | ||
this.killsoid = bind(this.killsoid, this); | ||
this.killall = bind(this.killall, this); | ||
this._kill = bind(this._kill, this); | ||
this.kill = bind(this.kill, this); | ||
this.get = bind(this.get, this); | ||
this.create = bind(this.create, this); | ||
this.activity = bind(this.activity, this); | ||
RedisSessions.__super__.constructor.call(this, o); | ||
this._initErrors(); | ||
this.redisns = o.namespace || "rs"; | ||
this.redisns = this.redisns + ":"; | ||
this.wiperinterval = null; | ||
this.sessioncache = null; | ||
this.iscache = false; | ||
isclient = false; | ||
if (((ref = o.client) != null ? (ref1 = ref.constructor) != null ? ref1.name : void 0 : void 0) === "RedisClient") { | ||
isclient = true; | ||
this.redis = o.client; | ||
} else if (o.options && o.options.url) { | ||
this.redis = RedisInst.createClient(o.options); | ||
} else { | ||
this.redis = RedisInst.createClient(o.port || 6379, o.host || "127.0.0.1", o.options || {}); | ||
} | ||
this.connected = this.redis.connected || false; | ||
this.redis.on("connect", (function(_this) { | ||
return function() { | ||
_this.connected = true; | ||
_this.emit("connect"); | ||
}; | ||
})(this)); | ||
this.redis.on("error", (function(_this) { | ||
return function(err) { | ||
// **Parameters:** | ||
// * `app` must be [a-zA-Z0-9_-] and 3-20 chars long | ||
// * `dt` Delta time. Amount of seconds to check (e.g. 600 for the last 10 min.) | ||
this.activity = this.activity.bind(this); | ||
// ## Create | ||
// Creates a session for an app and id. | ||
// **Parameters:** | ||
// An object with the following keys: | ||
// * `app` must be [a-zA-Z0-9_-] and 3-20 chars long | ||
// * `id` must be [a-zA-Z0-9_-] and 1-64 chars long | ||
// * `ip` must be a valid IP4 address | ||
// * `ttl` *optional* Default: 7200. Positive integer between 1 and 2592000 (30 days) | ||
// **Example:** | ||
// create({ | ||
// app: "forum", | ||
// id: "user1234", | ||
// ip: "156.78.90.12", | ||
// ttl: 3600 | ||
// }, callback) | ||
// Returns the token when successful. | ||
this.create = this.create.bind(this); | ||
// ## Get | ||
// Get a session for an app and token. | ||
// **Parameters:** | ||
// An object with the following keys: | ||
// * `app` must be [a-zA-Z0-9_-] and 3-20 chars long | ||
// * `token` must be [a-zA-Z0-9] and 64 chars long | ||
this.get = this.get.bind(this); | ||
// ## Kill | ||
// Kill a session for an app and token. | ||
// **Parameters:** | ||
// An object with the following keys: | ||
// * `app` must be [a-zA-Z0-9_-] and 3-20 chars long | ||
// * `token` must be [a-zA-Z0-9] and 64 chars long | ||
this.kill = this.kill.bind(this); | ||
// Helper to _kill a single session | ||
// Used by @kill and @wipe | ||
// Needs options.app, options.token and options.id | ||
this._kill = this._kill.bind(this); | ||
// ## Killall | ||
// Kill all sessions of a single app | ||
// Parameters: | ||
// * `app` must be [a-zA-Z0-9_-] and 3-20 chars long | ||
this.killall = this.killall.bind(this); | ||
// ## Kill all Sessions of Id | ||
// Kill all sessions of a single id within an app | ||
// Parameters: | ||
// * `app` must be [a-zA-Z0-9_-] and 3-20 chars long | ||
// * `id` must be [a-zA-Z0-9_-] and 1-64 chars long | ||
this.killsoid = this.killsoid.bind(this); | ||
// ## Ping | ||
// Ping the Redis server | ||
this.ping = this.ping.bind(this); | ||
// ## Quit | ||
// Quit the Redis connection | ||
// This is needed if Redis-Session is used with AWS Lambda. | ||
this.quit = this.quit.bind(this); | ||
// ## Set | ||
// Set/Update/Delete custom data for a single session. | ||
// All custom data is stored in the `d` object which is a simple hash object structure. | ||
// `d` might contain **one or more** keys with the following types: `string`, `number`, `boolean`, `null`. | ||
// Keys with all values except `null` will be stored. If a key containts `null` the key will be removed. | ||
// Note: If `d` already contains keys that are not supplied in the set request then these keys will be untouched. | ||
// **Parameters:** | ||
// An object with the following keys: | ||
// * `app` must be [a-zA-Z0-9_-] and 3-20 chars long | ||
// * `token` must be [a-zA-Z0-9] and 64 chars long | ||
// * `d` must be an object with keys whose values only consist of strings, numbers, boolean and null. | ||
this.set = this.set.bind(this); | ||
// ## Session of App | ||
// Returns all sessions of a single app that were active within the last *n* seconds | ||
// Note: This might return a lot of data depending on `dt`. Use with care. | ||
// **Parameters:** | ||
// * `app` must be [a-zA-Z0-9_-] and 3-20 chars long | ||
// * `dt` Delta time. Amount of seconds to check (e.g. 600 for the last 10 min.) | ||
this.soapp = this.soapp.bind(this); | ||
// ## Sessions of ID (soid) | ||
// Returns all sessions of a single id | ||
// **Parameters:** | ||
// An object with the following keys: | ||
// * `app` must be [a-zA-Z0-9_-] and 3-20 chars long | ||
// * `id` must be [a-zA-Z0-9_-] and 1-64 chars long | ||
this.soid = this.soid.bind(this); | ||
this._handleError = this._handleError.bind(this); | ||
this._initErrors = this._initErrors.bind(this); | ||
this._returnSessions = this._returnSessions.bind(this); | ||
// Wipe old sessions | ||
// Called by internal housekeeping every `options.wipe` seconds | ||
this._wipe = this._wipe.bind(this); | ||
this._initErrors(); | ||
this.redisns = o.namespace || "rs"; | ||
this.redisns = this.redisns + ":"; | ||
this.wiperinterval = null; | ||
this.sessioncache = null; | ||
this.iscache = false; | ||
isclient = false; | ||
if (((ref = o.client) != null ? (ref1 = ref.constructor) != null ? ref1.name : void 0 : void 0) === "RedisClient") { | ||
isclient = true; | ||
this.redis = o.client; | ||
} else if (o.options && o.options.url) { | ||
this.redis = RedisInst.createClient(o.options); | ||
} else { | ||
this.redis = RedisInst.createClient(o.port || 6379, o.host || "127.0.0.1", o.options || {}); | ||
} | ||
this.connected = this.redis.connected || false; | ||
this.redis.on("connect", () => { | ||
this.connected = true; | ||
this.emit("connect"); | ||
}); | ||
this.redis.on("error", (err) => { | ||
if (err.message.indexOf("ECONNREFUSED")) { | ||
_this.connected = false; | ||
_this.emit("disconnect"); | ||
this.connected = false; | ||
this.emit("disconnect"); | ||
} else { | ||
console.error("Redis ERROR", err); | ||
_this.emit("error"); | ||
this.emit("error"); | ||
} | ||
}; | ||
})(this)); | ||
if (o.cachetime) { | ||
if (isclient) { | ||
console.log("Warning: Caching is disabled. Must not supply `client` option"); | ||
} else { | ||
o.cachetime = parseInt(o.cachetime, 10); | ||
if (o.cachetime > 0) { | ||
this.sessioncache = new NodeCache({ | ||
stdTTL: o.cachetime, | ||
useClones: false | ||
}); | ||
if (o.options && o.options.url) { | ||
redissub = RedisInst.createClient(o.options); | ||
} else { | ||
redissub = RedisInst.createClient(o.port || 6379, o.host || "127.0.0.1", o.options || {}); | ||
}); | ||
if (o.cachetime) { | ||
if (isclient) { | ||
console.log("Warning: Caching is disabled. Must not supply `client` option"); | ||
} else { | ||
o.cachetime = parseInt(o.cachetime, 10); | ||
if (o.cachetime > 0) { | ||
// Setup node-cache | ||
this.sessioncache = new NodeCache({ | ||
stdTTL: o.cachetime, | ||
useClones: false | ||
}); | ||
// Setup the Redis subscriber to listen for changes | ||
if (o.options && o.options.url) { | ||
redissub = RedisInst.createClient(o.options); | ||
} else { | ||
redissub = RedisInst.createClient(o.port || 6379, o.host || "127.0.0.1", o.options || {}); | ||
} | ||
// Setup the listener for change messages | ||
redissub.on("message", (c, m) => { | ||
// Message will only contain a `{app}:{token}` string. Just delete it from node-cache. | ||
this.sessioncache.del(m); | ||
}); | ||
// Setup the subscriber | ||
this.iscache = true; | ||
redissub.subscribe(`${this.redisns}cache`); | ||
} | ||
redissub.on("message", (function(_this) { | ||
return function(c, m) { | ||
_this.sessioncache.del(m); | ||
}; | ||
})(this)); | ||
this.iscache = true; | ||
redissub.subscribe(this.redisns + "cache"); | ||
} | ||
} | ||
} | ||
if (o.wipe !== 0) { | ||
wipe = o.wipe || 600; | ||
if (wipe < 10) { | ||
wipe = 10; | ||
if (o.wipe !== 0) { | ||
wipe = o.wipe || 600; | ||
if (wipe < 10) { | ||
wipe = 10; | ||
} | ||
this.wiperinterval = setInterval(this._wipe, wipe * 1000); | ||
} | ||
this.wiperinterval = setInterval(this._wipe, wipe * 1000); | ||
} | ||
} | ||
RedisSessions.prototype.activity = function(options, cb) { | ||
if (this._validate(options, ["app", "dt"], cb) === false) { | ||
return; | ||
} | ||
this.redis.zcount("" + this.redisns + options.app + ":_users", this._now() - options.dt, "+inf", function(err, resp) { | ||
if (err) { | ||
cb(err); | ||
activity(options, cb) { | ||
boundMethodCheck(this, RedisSessions); | ||
if (this._validate(options, ["app", "dt"], cb) === false) { | ||
return; | ||
} | ||
cb(null, { | ||
activity: resp | ||
this.redis.zcount(`${this.redisns}${options.app}:_users`, this._now() - options.dt, "+inf", function(err, resp) { | ||
if (err) { | ||
cb(err); | ||
return; | ||
} | ||
cb(null, { | ||
activity: resp | ||
}); | ||
}); | ||
}); | ||
}; | ||
} | ||
RedisSessions.prototype.create = function(options, cb) { | ||
var e, mc, nullkeys, thesession, token; | ||
options.d = options.d || { | ||
___duMmYkEy: null | ||
}; | ||
options = this._validate(options, ["app", "id", "ip", "ttl", "d", "no_resave"], cb); | ||
if (options === false) { | ||
return; | ||
} | ||
token = this._createToken(); | ||
mc = this._createMultiStatement(options.app, token, options.id, options.ttl, false); | ||
mc.push(["sadd", "" + this.redisns + options.app + ":us:" + options.id, token]); | ||
thesession = ["hmset", "" + this.redisns + options.app + ":" + token, "id", options.id, "r", 1, "w", 1, "ip", options.ip, "la", this._now(), "ttl", parseInt(options.ttl)]; | ||
if (options.d) { | ||
nullkeys = []; | ||
for (e in options.d) { | ||
if (options.d[e] === null) { | ||
nullkeys.push(e); | ||
create(options, cb) { | ||
var e, mc, nullkeys, thesession, token; | ||
boundMethodCheck(this, RedisSessions); | ||
options.d = options.d || { | ||
___duMmYkEy: null | ||
}; | ||
options = this._validate(options, ["app", "id", "ip", "ttl", "d", "no_resave"], cb); | ||
if (options === false) { | ||
return; | ||
} | ||
token = this._createToken(); | ||
// Prepopulate the multi statement | ||
mc = this._createMultiStatement(options.app, token, options.id, options.ttl, false); | ||
mc.push(["sadd", `${this.redisns}${options.app}:us:${options.id}`, token]); | ||
// Create the default session hash | ||
thesession = ["hmset", `${this.redisns}${options.app}:${token}`, "id", options.id, "r", 1, "w", 1, "ip", options.ip, "la", this._now(), "ttl", parseInt(options.ttl)]; | ||
if (options.d) { | ||
// Remove null values | ||
nullkeys = []; | ||
for (e in options.d) { | ||
if (options.d[e] === null) { | ||
nullkeys.push(e); | ||
} | ||
} | ||
options.d = _.omit(options.d, nullkeys); | ||
if (_.keys(options.d).length) { | ||
thesession = thesession.concat(["d", JSON.stringify(options.d)]); | ||
} | ||
} | ||
options.d = _.omit(options.d, nullkeys); | ||
if (_.keys(options.d).length) { | ||
thesession = thesession.concat(["d", JSON.stringify(options.d)]); | ||
// Check for `no_resave` #36 | ||
if (options.no_resave) { | ||
thesession.push("no_resave"); | ||
thesession.push(1); | ||
} | ||
mc.push(thesession); | ||
// Run the redis statement | ||
this.redis.multi(mc).exec(function(err, resp) { | ||
if (err) { | ||
cb(err); | ||
return; | ||
} | ||
if (resp[4] !== "OK") { | ||
cb("Unknow error"); | ||
return; | ||
} | ||
cb(null, { | ||
token: token | ||
}); | ||
}); | ||
} | ||
if (options.no_resave) { | ||
thesession.push("no_resave"); | ||
thesession.push(1); | ||
} | ||
mc.push(thesession); | ||
this.redis.multi(mc).exec(function(err, resp) { | ||
if (err) { | ||
cb(err); | ||
get(options, cb) { | ||
var cache, cachekey, thekey; | ||
boundMethodCheck(this, RedisSessions); | ||
options = this._validate(options, ["app", "token"], cb); | ||
if (options === false) { | ||
return; | ||
} | ||
if (resp[4] !== "OK") { | ||
cb("Unknow error"); | ||
return; | ||
cachekey = `${options.app}:${options.token}`; | ||
if (this.iscache && !options._nocache) { | ||
// Try to find the session in cache | ||
cache = this.sessioncache.get(cachekey); | ||
if (cache !== void 0) { | ||
cb(null, cache); | ||
return; | ||
} | ||
} | ||
cb(null, { | ||
token: token | ||
}); | ||
}); | ||
}; | ||
RedisSessions.prototype.get = function(options, cb) { | ||
var cache, cachekey, thekey; | ||
options = this._validate(options, ["app", "token"], cb); | ||
if (options === false) { | ||
return; | ||
} | ||
cachekey = options.app + ":" + options.token; | ||
if (this.iscache && !options._nocache) { | ||
cache = this.sessioncache.get(cachekey); | ||
if (cache !== void 0) { | ||
cb(null, cache); | ||
return; | ||
} | ||
} | ||
thekey = "" + this.redisns + cachekey; | ||
this.redis.hmget(thekey, "id", "r", "w", "ttl", "d", "la", "ip", "no_resave", (function(_this) { | ||
return function(err, resp) { | ||
thekey = `${this.redisns}${cachekey}`; | ||
this.redis.hmget(thekey, "id", "r", "w", "ttl", "d", "la", "ip", "no_resave", (err, resp) => { | ||
var mc, o; | ||
@@ -203,3 +330,4 @@ if (err) { | ||
} | ||
o = _this._prepareSession(resp); | ||
// Prepare the data | ||
o = this._prepareSession(resp); | ||
if (o === null) { | ||
@@ -209,5 +337,6 @@ cb(null, {}); | ||
} | ||
if (_this.iscache) { | ||
_this.sessioncache.set(cachekey, o); | ||
if (this.iscache) { | ||
this.sessioncache.set(cachekey, o); | ||
} | ||
// Secret switch to disable updating the stats - we don't need this when we kill a session | ||
if (options._noupdate) { | ||
@@ -217,8 +346,9 @@ cb(null, o); | ||
} | ||
mc = _this._createMultiStatement(options.app, options.token, o.id, o.ttl, o.no_resave); | ||
// Update the counters | ||
mc = this._createMultiStatement(options.app, options.token, o.id, o.ttl, o.no_resave); | ||
mc.push(["hincrby", thekey, "r", 1]); | ||
if (o.idle > 1) { | ||
mc.push(["hset", thekey, "la", _this._now()]); | ||
mc.push(["hset", thekey, "la", this._now()]); | ||
} | ||
_this.redis.multi(mc).exec(function(err, resp) { | ||
this.redis.multi(mc).exec(function(err, resp) { | ||
if (err) { | ||
@@ -230,13 +360,12 @@ cb(err); | ||
}); | ||
}; | ||
})(this)); | ||
}; | ||
}); | ||
} | ||
RedisSessions.prototype._no_resave_check = function(session, options, cb, done) { | ||
if (!session.no_resave) { | ||
done(); | ||
return; | ||
} | ||
this.redis.zscore(this.redisns + "SESSIONS", options.app + ":" + options.token + ":" + session.id, (function(_this) { | ||
return function(err, resp) { | ||
_no_resave_check(session, options, cb, done) { | ||
if (!session.no_resave) { | ||
done(); | ||
return; | ||
} | ||
// Check if the session has run out | ||
this.redis.zscore(`${this.redisns}SESSIONS`, `${options.app}:${options.token}:${session.id}`, (err, resp) => { | ||
if (err) { | ||
@@ -246,3 +375,4 @@ cb(err); | ||
} | ||
if (resp === null || resp < _this._now()) { | ||
if (resp === null || resp < this._now()) { | ||
// Session has run out. | ||
cb(null, {}); | ||
@@ -252,14 +382,13 @@ return; | ||
done(); | ||
}; | ||
})(this)); | ||
}; | ||
}); | ||
} | ||
RedisSessions.prototype.kill = function(options, cb) { | ||
options = this._validate(options, ["app", "token"], cb); | ||
if (options === false) { | ||
return; | ||
} | ||
options._noupdate = true; | ||
this.get(options, (function(_this) { | ||
return function(err, resp) { | ||
kill(options, cb) { | ||
boundMethodCheck(this, RedisSessions); | ||
options = this._validate(options, ["app", "token"], cb); | ||
if (options === false) { | ||
return; | ||
} | ||
options._noupdate = true; | ||
this.get(options, (err, resp) => { | ||
if (err) { | ||
@@ -276,15 +405,14 @@ cb(err); | ||
options.id = resp.id; | ||
_this._kill(options, cb); | ||
}; | ||
})(this)); | ||
}; | ||
this._kill(options, cb); | ||
}); | ||
} | ||
RedisSessions.prototype._kill = function(options, cb) { | ||
var mc; | ||
mc = [["zrem", "" + this.redisns + options.app + ":_sessions", options.token + ":" + options.id], ["srem", "" + this.redisns + options.app + ":us:" + options.id, options.token], ["zrem", this.redisns + "SESSIONS", options.app + ":" + options.token + ":" + options.id], ["del", "" + this.redisns + options.app + ":" + options.token], ["exists", "" + this.redisns + options.app + ":us:" + options.id]]; | ||
if (this.iscache) { | ||
mc.push(["publish", this.redisns + "cache", options.app + ":" + options.token]); | ||
} | ||
this.redis.multi(mc).exec((function(_this) { | ||
return function(err, resp) { | ||
_kill(options, cb) { | ||
var mc; | ||
boundMethodCheck(this, RedisSessions); | ||
mc = [["zrem", `${this.redisns}${options.app}:_sessions`, `${options.token}:${options.id}`], ["srem", `${this.redisns}${options.app}:us:${options.id}`, options.token], ["zrem", `${this.redisns}SESSIONS`, `${options.app}:${options.token}:${options.id}`], ["del", `${this.redisns}${options.app}:${options.token}`], ["exists", `${this.redisns}${options.app}:us:${options.id}`]]; | ||
if (this.iscache) { | ||
mc.push(["publish", `${this.redisns}cache`, `${options.app}:${options.token}`]); | ||
} | ||
this.redis.multi(mc).exec((err, resp) => { | ||
if (err) { | ||
@@ -294,4 +422,6 @@ cb(err); | ||
} | ||
// NOW. If the last reply of the multi statement is 0 then this was the last session. | ||
// We need to remove the ZSET for this user also: | ||
if (resp[4] === 0) { | ||
_this.redis.zrem("" + _this.redisns + options.app + ":_users", options.id, function() { | ||
this.redis.zrem(`${this.redisns}${options.app}:_users`, options.id, function() { | ||
if (err) { | ||
@@ -310,16 +440,16 @@ cb(err); | ||
} | ||
}; | ||
})(this)); | ||
}; | ||
}); | ||
} | ||
RedisSessions.prototype.killall = function(options, cb) { | ||
var appsessionkey, appuserkey; | ||
options = this._validate(options, ["app"], cb); | ||
if (options === false) { | ||
return; | ||
} | ||
appsessionkey = "" + this.redisns + options.app + ":_sessions"; | ||
appuserkey = "" + this.redisns + options.app + ":_users"; | ||
this.redis.zrange(appsessionkey, 0, -1, (function(_this) { | ||
return function(err, resp) { | ||
killall(options, cb) { | ||
var appsessionkey, appuserkey; | ||
boundMethodCheck(this, RedisSessions); | ||
options = this._validate(options, ["app"], cb); | ||
if (options === false) { | ||
return; | ||
} | ||
// First we need to get all sessions of the app | ||
appsessionkey = `${this.redisns}${options.app}:_sessions`; | ||
appuserkey = `${this.redisns}${options.app}:_users`; | ||
this.redis.zrange(appsessionkey, 0, -1, (err, resp) => { | ||
var e, globalkeys, j, k, len, len1, mc, thekey, tokenkeys, userkeys, ussets; | ||
@@ -342,4 +472,4 @@ if (err) { | ||
thekey = e.split(":"); | ||
globalkeys.push(options.app + ":" + e); | ||
tokenkeys.push("" + _this.redisns + options.app + ":" + thekey[0]); | ||
globalkeys.push(`${options.app}:${e}`); | ||
tokenkeys.push(`${this.redisns}${options.app}:${thekey[0]}`); | ||
userkeys.push(thekey[1]); | ||
@@ -353,14 +483,14 @@ } | ||
e = userkeys[k]; | ||
results.push("" + this.redisns + options.app + ":us:" + e); | ||
results.push(`${this.redisns}${options.app}:us:${e}`); | ||
} | ||
return results; | ||
}).call(_this); | ||
mc = [["zrem", appsessionkey].concat(resp), ["zrem", appuserkey].concat(userkeys), ["zrem", _this.redisns + "SESSIONS"].concat(globalkeys), ["del"].concat(ussets), ["del"].concat(tokenkeys)]; | ||
if (_this.iscache) { | ||
}).call(this); | ||
mc = [["zrem", appsessionkey].concat(resp), ["zrem", appuserkey].concat(userkeys), ["zrem", `${this.redisns}SESSIONS`].concat(globalkeys), ["del"].concat(ussets), ["del"].concat(tokenkeys)]; | ||
if (this.iscache) { | ||
for (k = 0, len1 = resp.length; k < len1; k++) { | ||
e = resp[k]; | ||
mc.push(["publish", _this.redisns + "cache", options.app + ":" + (e.split(":")[0])]); | ||
mc.push(["publish", `${this.redisns}cache`, `${options.app}:${e.split(":")[0]}`]); | ||
} | ||
} | ||
_this.redis.multi(mc).exec(function(err, resp) { | ||
this.redis.multi(mc).exec(function(err, resp) { | ||
if (err) { | ||
@@ -374,13 +504,12 @@ cb(err); | ||
}); | ||
}; | ||
})(this)); | ||
}; | ||
}); | ||
} | ||
RedisSessions.prototype.killsoid = function(options, cb) { | ||
options = this._validate(options, ["app", "id"], cb); | ||
if (options === false) { | ||
return; | ||
} | ||
this.redis.smembers("" + this.redisns + options.app + ":us:" + options.id, (function(_this) { | ||
return function(err, resp) { | ||
killsoid(options, cb) { | ||
boundMethodCheck(this, RedisSessions); | ||
options = this._validate(options, ["app", "id"], cb); | ||
if (options === false) { | ||
return; | ||
} | ||
this.redis.smembers(`${this.redisns}${options.app}:us:${options.id}`, (err, resp) => { | ||
var j, len, mc, token; | ||
@@ -398,14 +527,16 @@ if (err) { | ||
mc = []; | ||
// Grab all sessions we need to get | ||
for (j = 0, len = resp.length; j < len; j++) { | ||
token = resp[j]; | ||
mc.push(["zrem", "" + _this.redisns + options.app + ":_sessions", token + ":" + options.id]); | ||
mc.push(["srem", "" + _this.redisns + options.app + ":us:" + options.id, token]); | ||
mc.push(["zrem", _this.redisns + "SESSIONS", options.app + ":" + token + ":" + options.id]); | ||
mc.push(["del", "" + _this.redisns + options.app + ":" + token]); | ||
if (_this.iscache) { | ||
mc.push(["publish", _this.redisns + "cache", options.app + ":" + token]); | ||
// Add to the multi commands array | ||
mc.push(["zrem", `${this.redisns}${options.app}:_sessions`, `${token}:${options.id}`]); | ||
mc.push(["srem", `${this.redisns}${options.app}:us:${options.id}`, token]); | ||
mc.push(["zrem", `${this.redisns}SESSIONS`, `${options.app}:${token}:${options.id}`]); | ||
mc.push(["del", `${this.redisns}${options.app}:${token}`]); | ||
if (this.iscache) { | ||
mc.push(["publish", `${this.redisns}cache`, `${options.app}:${token}`]); | ||
} | ||
} | ||
mc.push(["exists", "" + _this.redisns + options.app + ":us:" + options.id]); | ||
_this.redis.multi(mc).exec(function(err, resp) { | ||
mc.push(["exists", `${this.redisns}${options.app}:us:${options.id}`]); | ||
this.redis.multi(mc).exec((err, resp) => { | ||
var e, k, len1, ref, total; | ||
@@ -416,2 +547,3 @@ if (err) { | ||
} | ||
// get the amount of deleted sessions | ||
total = 0; | ||
@@ -423,4 +555,6 @@ ref = resp.slice(3); | ||
} | ||
// NOW. If the last reply of the multi statement is 0 then this was the last session. | ||
// We need to remove the ZSET for this user also: | ||
if (_.last(resp) === 0) { | ||
_this.redis.zrem("" + _this.redisns + options.app + ":_users", options.id, function() { | ||
this.redis.zrem(`${this.redisns}${options.app}:_users`, options.id, function() { | ||
cb(null, { | ||
@@ -436,26 +570,28 @@ kill: total | ||
}); | ||
}; | ||
})(this)); | ||
}; | ||
}); | ||
} | ||
RedisSessions.prototype.ping = function(cb) { | ||
this.redis.ping(cb); | ||
}; | ||
ping(cb) { | ||
boundMethodCheck(this, RedisSessions); | ||
this.redis.ping(cb); | ||
} | ||
RedisSessions.prototype.quit = function() { | ||
if (this.wiperinterval !== null) { | ||
clearInterval(this.wiperinterval); | ||
quit() { | ||
boundMethodCheck(this, RedisSessions); | ||
if (this.wiperinterval !== null) { | ||
clearInterval(this.wiperinterval); | ||
} | ||
this.redis.quit(); | ||
} | ||
this.redis.quit(); | ||
}; | ||
RedisSessions.prototype.set = function(options, cb) { | ||
options = this._validate(options, ["app", "token", "d", "no_resave"], cb); | ||
if (options === false) { | ||
return; | ||
} | ||
options._noupdate = true; | ||
options._nocache = true; | ||
this.get(options, (function(_this) { | ||
return function(err, resp) { | ||
set(options, cb) { | ||
boundMethodCheck(this, RedisSessions); | ||
options = this._validate(options, ["app", "token", "d", "no_resave"], cb); | ||
if (options === false) { | ||
return; | ||
} | ||
options._noupdate = true; | ||
options._nocache = true; | ||
// Get the session | ||
this.get(options, (err, resp) => { | ||
var e, mc, nullkeys, thekey; | ||
@@ -470,2 +606,3 @@ if (err) { | ||
} | ||
// Cleanup `d` | ||
nullkeys = []; | ||
@@ -477,2 +614,3 @@ for (e in options.d) { | ||
} | ||
// OK ready to set some data | ||
if (resp.d) { | ||
@@ -483,7 +621,10 @@ resp.d = _.extend(_.omit(resp.d, nullkeys), _.omit(options.d, nullkeys)); | ||
} | ||
thekey = "" + _this.redisns + options.app + ":" + options.token; | ||
mc = _this._createMultiStatement(options.app, options.token, resp.id, resp.ttl, resp.no_resave); | ||
// We now have a cleaned version of resp.d ready to save back to Redis. | ||
// If resp.d contains no keys we want to delete the `d` key within the hash though. | ||
thekey = `${this.redisns}${options.app}:${options.token}`; | ||
mc = this._createMultiStatement(options.app, options.token, resp.id, resp.ttl, resp.no_resave); | ||
mc.push(["hincrby", thekey, "w", 1]); | ||
// Only update the `la` (last access) value if more than 1 second idle | ||
if (resp.idle > 1) { | ||
mc.push(["hset", thekey, "la", _this._now()]); | ||
mc.push(["hset", thekey, "la", this._now()]); | ||
} | ||
@@ -496,6 +637,6 @@ if (_.keys(resp.d).length) { | ||
} | ||
if (_this.iscache) { | ||
mc.push(["publish", _this.redisns + "cache", options.app + ":" + options.token]); | ||
if (this.iscache) { | ||
mc.push(["publish", `${this.redisns}cache`, `${options.app}:${options.token}`]); | ||
} | ||
_this.redis.multi(mc).exec(function(err, reply) { | ||
this.redis.multi(mc).exec(function(err, reply) { | ||
if (err) { | ||
@@ -505,15 +646,15 @@ cb(err); | ||
} | ||
// Set `w` to the actual counter value | ||
resp.w = reply[3]; | ||
cb(null, resp); | ||
}); | ||
}; | ||
})(this)); | ||
}; | ||
}); | ||
} | ||
RedisSessions.prototype.soapp = function(options, cb) { | ||
if (this._validate(options, ["app", "dt"], cb) === false) { | ||
return; | ||
} | ||
this.redis.zrevrangebyscore("" + this.redisns + options.app + ":_sessions", "+inf", this._now() - options.dt, (function(_this) { | ||
return function(err, resp) { | ||
soapp(options, cb) { | ||
boundMethodCheck(this, RedisSessions); | ||
if (this._validate(options, ["app", "dt"], cb) === false) { | ||
return; | ||
} | ||
this.redis.zrevrangebyscore(`${this.redisns}${options.app}:_sessions`, "+inf", this._now() - options.dt, (err, resp) => { | ||
var e; | ||
@@ -533,14 +674,13 @@ if (err) { | ||
})(); | ||
_this._returnSessions(options, resp, cb); | ||
}; | ||
})(this)); | ||
}; | ||
this._returnSessions(options, resp, cb); | ||
}); | ||
} | ||
RedisSessions.prototype.soid = function(options, cb) { | ||
options = this._validate(options, ["app", "id"], cb); | ||
if (options === false) { | ||
return; | ||
} | ||
this.redis.smembers("" + this.redisns + options.app + ":us:" + options.id, (function(_this) { | ||
return function(err, resp) { | ||
soid(options, cb) { | ||
boundMethodCheck(this, RedisSessions); | ||
options = this._validate(options, ["app", "id"], cb); | ||
if (options === false) { | ||
return; | ||
} | ||
this.redis.smembers(`${this.redisns}${options.app}:us:${options.id}`, (err, resp) => { | ||
if (err) { | ||
@@ -550,102 +690,109 @@ cb(err); | ||
} | ||
_this._returnSessions(options, resp, cb); | ||
}; | ||
})(this)); | ||
}; | ||
this._returnSessions(options, resp, cb); | ||
}); | ||
} | ||
RedisSessions.prototype._createMultiStatement = function(app, token, id, ttl, no_resave) { | ||
var now, o; | ||
now = this._now(); | ||
o = [["zadd", "" + this.redisns + app + ":_sessions", now, token + ":" + id], ["zadd", "" + this.redisns + app + ":_users", now, id], ["zadd", this.redisns + "SESSIONS", now + ttl, app + ":" + token + ":" + id]]; | ||
if (no_resave) { | ||
o.push(["hset", "" + this.redisns + app + ":" + token, "ttl", ttl]); | ||
// Helpers | ||
_createMultiStatement(app, token, id, ttl, no_resave) { | ||
var now, o; | ||
now = this._now(); | ||
o = [["zadd", `${this.redisns}${app}:_sessions`, now, `${token}:${id}`], ["zadd", `${this.redisns}${app}:_users`, now, id], ["zadd", `${this.redisns}SESSIONS`, now + ttl, `${app}:${token}:${id}`]]; | ||
if (no_resave) { | ||
o.push(["hset", `${this.redisns}${app}:${token}`, "ttl", ttl]); | ||
} | ||
return o; | ||
} | ||
return o; | ||
}; | ||
RedisSessions.prototype._createToken = function() { | ||
var i, j, possible, t; | ||
t = ""; | ||
possible = "ABCDEFGHIJKLMNOPQRSTUVWXYabcdefghijklmnopqrstuvwxyz0123456789"; | ||
for (i = j = 0; j < 55; i = ++j) { | ||
t += possible.charAt(Math.floor(Math.random() * possible.length)); | ||
_createToken() { | ||
var i, j, possible, t; | ||
t = ""; | ||
// Note we don't use Z as a valid character here | ||
possible = "ABCDEFGHIJKLMNOPQRSTUVWXYabcdefghijklmnopqrstuvwxyz0123456789"; | ||
for (i = j = 0; j < 55; i = ++j) { | ||
t += possible.charAt(Math.floor(Math.random() * possible.length)); | ||
} | ||
// add the current time in ms to the very end seperated by a Z | ||
return t + 'Z' + new Date().getTime().toString(36); | ||
} | ||
return t + 'Z' + new Date().getTime().toString(36); | ||
}; | ||
RedisSessions.prototype._handleError = function(cb, err, data) { | ||
var _err, ref; | ||
if (data == null) { | ||
data = {}; | ||
_handleError(cb, err, data = {}) { | ||
var _err, ref; | ||
boundMethodCheck(this, RedisSessions); | ||
// try to create a error Object with humanized message | ||
if (_.isString(err)) { | ||
_err = new Error(); | ||
_err.name = err; | ||
_err.message = ((ref = this._ERRORS) != null ? typeof ref[err] === "function" ? ref[err](data) : void 0 : void 0) || "unkown"; | ||
} else { | ||
_err = err; | ||
} | ||
cb(_err); | ||
} | ||
if (_.isString(err)) { | ||
_err = new Error(); | ||
_err.name = err; | ||
_err.message = ((ref = this._ERRORS) != null ? typeof ref[err] === "function" ? ref[err](data) : void 0 : void 0) || "unkown"; | ||
} else { | ||
_err = err; | ||
_initErrors() { | ||
var key, msg, ref; | ||
boundMethodCheck(this, RedisSessions); | ||
this._ERRORS = {}; | ||
ref = this.ERRORS; | ||
for (key in ref) { | ||
msg = ref[key]; | ||
this._ERRORS[key] = _.template(msg); | ||
} | ||
} | ||
cb(_err); | ||
}; | ||
RedisSessions.prototype._initErrors = function() { | ||
var key, msg, ref; | ||
this._ERRORS = {}; | ||
ref = this.ERRORS; | ||
for (key in ref) { | ||
msg = ref[key]; | ||
this._ERRORS[key] = _.template(msg); | ||
_now() { | ||
return parseInt((new Date()).getTime() / 1000); | ||
} | ||
}; | ||
RedisSessions.prototype._now = function() { | ||
return parseInt((new Date()).getTime() / 1000); | ||
}; | ||
RedisSessions.prototype._prepareSession = function(session) { | ||
var now, o; | ||
now = this._now(); | ||
if (session[0] === null) { | ||
return null; | ||
_prepareSession(session) { | ||
var now, o; | ||
now = this._now(); | ||
if (session[0] === null) { | ||
return null; | ||
} | ||
// Create the return object | ||
o = { | ||
id: session[0], | ||
r: Number(session[1]), | ||
w: Number(session[2]), | ||
ttl: Number(session[3]), | ||
idle: now - session[5], | ||
ip: session[6] | ||
}; | ||
// Oh wait. If o.ttl < o.idle we need to bail out. | ||
if (o.ttl < o.idle) { | ||
// We return an empty session object | ||
return null; | ||
} | ||
// Support for `no_resave` #36 | ||
if (session[7] === "1") { | ||
o.no_resave = true; | ||
o.ttl = o.ttl - o.idle; | ||
} | ||
// Parse the content of `d` | ||
if (session[4]) { | ||
o.d = JSON.parse(session[4]); | ||
} | ||
return o; | ||
} | ||
o = { | ||
id: session[0], | ||
r: Number(session[1]), | ||
w: Number(session[2]), | ||
ttl: Number(session[3]), | ||
idle: now - session[5], | ||
ip: session[6] | ||
}; | ||
if (o.ttl < o.idle) { | ||
return null; | ||
} | ||
if (session[7] === "1") { | ||
o.no_resave = true; | ||
o.ttl = o.ttl - o.idle; | ||
} | ||
if (session[4]) { | ||
o.d = JSON.parse(session[4]); | ||
} | ||
return o; | ||
}; | ||
RedisSessions.prototype._returnSessions = function(options, sessions, cb) { | ||
var e, mc; | ||
if (!sessions.length) { | ||
cb(null, { | ||
sessions: [] | ||
}); | ||
return; | ||
} | ||
mc = (function() { | ||
var j, len, results; | ||
results = []; | ||
for (j = 0, len = sessions.length; j < len; j++) { | ||
e = sessions[j]; | ||
results.push(["hmget", "" + this.redisns + options.app + ":" + e, "id", "r", "w", "ttl", "d", "la", "ip", "no_resave"]); | ||
_returnSessions(options, sessions, cb) { | ||
var e, mc; | ||
boundMethodCheck(this, RedisSessions); | ||
if (!sessions.length) { | ||
cb(null, { | ||
sessions: [] | ||
}); | ||
return; | ||
} | ||
return results; | ||
}).call(this); | ||
this.redis.multi(mc).exec((function(_this) { | ||
return function(err, resp) { | ||
mc = (function() { | ||
var j, len, results; | ||
results = []; | ||
for (j = 0, len = sessions.length; j < len; j++) { | ||
e = sessions[j]; | ||
results.push(["hmget", `${this.redisns}${options.app}:${e}`, "id", "r", "w", "ttl", "d", "la", "ip", "no_resave"]); | ||
} | ||
return results; | ||
}).call(this); | ||
this.redis.multi(mc).exec((err, resp) => { | ||
var j, len, o, session; | ||
@@ -659,3 +806,3 @@ if (err) { | ||
e = resp[j]; | ||
session = _this._prepareSession(e); | ||
session = this._prepareSession(e); | ||
if (session !== null) { | ||
@@ -668,112 +815,116 @@ o.push(session); | ||
}); | ||
}; | ||
})(this)); | ||
}; | ||
}); | ||
} | ||
RedisSessions.prototype._VALID = { | ||
app: /^([a-zA-Z0-9_-]){3,20}$/, | ||
id: /^(.*?){1,128}$/, | ||
ip: /^.{1,39}$/, | ||
token: /^([a-zA-Z0-9]){64}$/ | ||
}; | ||
RedisSessions.prototype._validate = function(o, items, cb) { | ||
var e, item, j, keys, len; | ||
for (j = 0, len = items.length; j < len; j++) { | ||
item = items[j]; | ||
switch (item) { | ||
case "app": | ||
case "id": | ||
case "ip": | ||
case "token": | ||
if (!o[item]) { | ||
this._handleError(cb, "missingParameter", { | ||
item: item | ||
}); | ||
return false; | ||
} | ||
o[item] = o[item].toString(); | ||
if (!this._VALID[item].test(o[item])) { | ||
this._handleError(cb, "invalidFormat", { | ||
item: item | ||
}); | ||
return false; | ||
} | ||
break; | ||
case "ttl": | ||
o.ttl = parseInt(o.ttl || 7200, 10); | ||
if (_.isNaN(o.ttl) || !_.isNumber(o.ttl) || o.ttl < 10) { | ||
this._handleError(cb, "invalidValue", { | ||
msg: "ttl must be a positive integer >= 10" | ||
}); | ||
return false; | ||
} | ||
break; | ||
case "no_resave": | ||
if (o.no_resave === true) { | ||
o.no_resave = true; | ||
} else { | ||
o.no_resave = false; | ||
} | ||
break; | ||
case "dt": | ||
o[item] = parseInt(o[item], 10); | ||
if (_.isNaN(o[item]) || !_.isNumber(o[item]) || o[item] < 10) { | ||
this._handleError(cb, "invalidValue", { | ||
msg: "ttl must be a positive integer >= 10" | ||
}); | ||
return false; | ||
} | ||
break; | ||
case "d": | ||
if (!o[item]) { | ||
this._handleError(cb, "missingParameter", { | ||
item: item | ||
}); | ||
return false; | ||
} | ||
if (!_.isObject(o.d) || _.isArray(o.d)) { | ||
this._handleError(cb, "invalidValue", { | ||
msg: "d must be an object" | ||
}); | ||
return false; | ||
} | ||
keys = _.keys(o.d); | ||
if (!keys.length) { | ||
this._handleError(cb, "invalidValue", { | ||
msg: "d must containt at least one key." | ||
}); | ||
return false; | ||
} | ||
for (e in o.d) { | ||
if (!_.isString(o.d[e]) && !_.isNumber(o.d[e]) && !_.isBoolean(o.d[e]) && !_.isNull(o.d[e])) { | ||
_validate(o, items, cb) { | ||
var e, item, j, keys, len; | ||
for (j = 0, len = items.length; j < len; j++) { | ||
item = items[j]; | ||
switch (item) { | ||
case "app": | ||
case "id": | ||
case "ip": | ||
case "token": | ||
if (!o[item]) { | ||
this._handleError(cb, "missingParameter", { | ||
item: item | ||
}); | ||
return false; | ||
} | ||
o[item] = o[item].toString(); | ||
if (!this._VALID[item].test(o[item])) { | ||
this._handleError(cb, "invalidFormat", { | ||
item: item | ||
}); | ||
return false; | ||
} | ||
break; | ||
case "ttl": | ||
o.ttl = parseInt(o.ttl || 7200, 10); | ||
if (_.isNaN(o.ttl) || !_.isNumber(o.ttl) || o.ttl < 10) { | ||
this._handleError(cb, "invalidValue", { | ||
msg: "d." + e + " has a forbidden type. Only strings, numbers, boolean and null are allowed." | ||
msg: "ttl must be a positive integer >= 10" | ||
}); | ||
return false; | ||
} | ||
} | ||
break; | ||
case "no_resave": | ||
if (o.no_resave === true) { | ||
o.no_resave = true; | ||
} else { | ||
o.no_resave = false; | ||
} | ||
break; | ||
case "dt": | ||
o[item] = parseInt(o[item], 10); | ||
if (_.isNaN(o[item]) || !_.isNumber(o[item]) || o[item] < 10) { | ||
this._handleError(cb, "invalidValue", { | ||
msg: "ttl must be a positive integer >= 10" | ||
}); | ||
return false; | ||
} | ||
break; | ||
case "d": | ||
if (!o[item]) { | ||
this._handleError(cb, "missingParameter", { | ||
item: item | ||
}); | ||
return false; | ||
} | ||
if (!_.isObject(o.d) || _.isArray(o.d)) { | ||
this._handleError(cb, "invalidValue", { | ||
msg: "d must be an object" | ||
}); | ||
return false; | ||
} | ||
keys = _.keys(o.d); | ||
if (!keys.length) { | ||
this._handleError(cb, "invalidValue", { | ||
msg: "d must containt at least one key." | ||
}); | ||
return false; | ||
} | ||
// Check if every key is either a boolean, string or a number | ||
for (e in o.d) { | ||
if (!_.isString(o.d[e]) && !_.isNumber(o.d[e]) && !_.isBoolean(o.d[e]) && !_.isNull(o.d[e])) { | ||
this._handleError(cb, "invalidValue", { | ||
msg: `d.${e} has a forbidden type. Only strings, numbers, boolean and null are allowed.` | ||
}); | ||
return false; | ||
} | ||
} | ||
} | ||
} | ||
return o; | ||
} | ||
return o; | ||
_wipe() { | ||
var that; | ||
boundMethodCheck(this, RedisSessions); | ||
that = this; | ||
this.redis.zrangebyscore(`${this.redisns}SESSIONS`, "-inf", this._now(), function(err, resp) { | ||
if (!err && resp.length) { | ||
_.each(resp, function(e) { | ||
var options; | ||
e = e.split(':'); | ||
options = { | ||
app: e[0], | ||
token: e[1], | ||
id: e[2] | ||
}; | ||
that._kill(options, function() {}); | ||
}); | ||
return; | ||
} | ||
}); | ||
} | ||
}; | ||
RedisSessions.prototype._wipe = function() { | ||
var that; | ||
that = this; | ||
this.redis.zrangebyscore(this.redisns + "SESSIONS", "-inf", this._now(), function(err, resp) { | ||
if (!err && resp.length) { | ||
_.each(resp, function(e) { | ||
var options; | ||
e = e.split(':'); | ||
options = { | ||
app: e[0], | ||
token: e[1], | ||
id: e[2] | ||
}; | ||
that._kill(options, function() {}); | ||
}); | ||
return; | ||
} | ||
}); | ||
// Validation regex used by _validate | ||
RedisSessions.prototype._VALID = { | ||
app: /^([a-zA-Z0-9_-]){3,20}$/, | ||
id: /^(.*?){1,128}$/, | ||
ip: /^.{1,39}$/, | ||
token: /^([a-zA-Z0-9]){64}$/ | ||
}; | ||
@@ -789,4 +940,4 @@ | ||
})(EventEmitter); | ||
}).call(this); | ||
module.exports = RedisSessions; |
{ | ||
"name": "redis-sessions", | ||
"description": "An advanced session store for Redis", | ||
"version": "2.1.0", | ||
"version": "3.0.0", | ||
"license": "MIT", | ||
"author": "P. Liess <smrchy+npm@gmail.com>", | ||
"engines": { | ||
"node": "> 0.10.20" | ||
"node": "> 8" | ||
}, | ||
@@ -15,11 +15,11 @@ "scripts": { | ||
"dependencies": { | ||
"lodash": "^4.17.11", | ||
"node-cache": "^4.2.0", | ||
"redis": "^2.x" | ||
"lodash": "^4.17.15", | ||
"node-cache": "^5.1.1", | ||
"redis": "^3.0.2" | ||
}, | ||
"devDependencies": { | ||
"coffeescript": "^1.12.7", | ||
"async": "^2.x", | ||
"mocha": "^5.2.0", | ||
"should": "11.1.0" | ||
"coffeescript": "^2.5.1", | ||
"async": "^3.2.0", | ||
"mocha": "^8.0.1", | ||
"should": "13.2.3" | ||
}, | ||
@@ -36,3 +36,9 @@ "keywords": [ | ||
"url": "http://github.com/smrchy/redis-sessions.git" | ||
}, | ||
"mocha": { | ||
"bail": true, | ||
"slow": 5, | ||
"timeout": 8000, | ||
"reporter": "spec" | ||
} | ||
} |
@@ -1,2 +0,2 @@ | ||
// Generated by CoffeeScript 1.12.7 | ||
// Generated by CoffeeScript 2.5.1 | ||
var RedisSessions, _, async, should; | ||
@@ -919,2 +919,3 @@ | ||
resp.d.e.should.equal(20.5); | ||
// Delay the reply just a tiny bit in case Travis works slower | ||
setTimeout(done, 20); | ||
@@ -921,0 +922,0 @@ }); |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
120888
1892
+ Addeddenque@1.5.1(transitive)
+ Addednode-cache@5.1.2(transitive)
+ Addedredis@3.1.2(transitive)
+ Addedredis-errors@1.2.0(transitive)
+ Addedredis-parser@3.0.0(transitive)
- Removeddouble-ended-queue@2.1.0-0(transitive)
- Removednode-cache@4.2.1(transitive)
- Removedredis@2.8.0(transitive)
- Removedredis-parser@2.6.0(transitive)
Updatedlodash@^4.17.15
Updatednode-cache@^5.1.1
Updatedredis@^3.0.2