Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

redis-sessions

Package Overview
Dependencies
Maintainers
1
Versions
49
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

redis-sessions - npm Package Compare versions

Comparing version 0.5.3 to 1.0.0

3

CHANGELOG.md
# CHANGELOG
## 1.0.0
* Use lodash 4.0.0
## 0.5.3

@@ -5,0 +8,0 @@

609

index.js

@@ -1,2 +0,3 @@

// Generated by CoffeeScript 1.6.3
// Generated by CoffeeScript 1.9.3
/*

@@ -14,10 +15,9 @@ Redis Sessions

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.
*/
*/
(function() {
var EventEmitter, RedisInst, RedisSessions, _,
__bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
__hasProp = {}.hasOwnProperty,
__extends = 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; };
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;

@@ -30,29 +30,28 @@ _ = require("lodash");

RedisSessions = (function(_super) {
__extends(RedisSessions, _super);
RedisSessions = (function(superClass) {
extend(RedisSessions, superClass);
function RedisSessions(o) {
var wipe, _ref, _ref1,
_this = this;
var 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.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);
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.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);
this._initErrors();
this.redisns = o.namespace || "rs";
this.redisns = this.redisns + ":";
if (((_ref = o.client) != null ? (_ref1 = _ref.constructor) != null ? _ref1.name : void 0 : void 0) === "RedisClient") {
if (((ref = o.client) != null ? (ref1 = ref.constructor) != null ? ref1.name : void 0 : void 0) === "RedisClient") {
this.redis = o.client;

@@ -63,15 +62,19 @@ } else {

this.connected = this.redis.connected || false;
this.redis.on("connect", function() {
_this.connected = true;
_this.emit("connect");
});
this.redis.on("error", function(err) {
if (err.message.indexOf("ECONNREFUSED")) {
_this.connected = false;
_this.emit("disconnect");
} else {
console.error("Redis ERROR", err);
_this.emit("error");
}
});
this.redis.on("connect", (function(_this) {
return function() {
_this.connected = true;
_this.emit("connect");
};
})(this));
this.redis.on("error", (function(_this) {
return function(err) {
if (err.message.indexOf("ECONNREFUSED")) {
_this.connected = false;
_this.emit("disconnect");
} else {
console.error("Redis ERROR", err);
_this.emit("error");
}
};
})(this));
wipe = o.wipe || 600;

@@ -141,4 +144,3 @@ if (wipe < 10) {

RedisSessions.prototype.get = function(options, cb) {
var now, thekey,
_this = this;
var now, thekey;
options = this._validate(options, ["app", "token"], cb);

@@ -150,23 +152,5 @@ if (options === false) {

thekey = "" + this.redisns + options.app + ":" + options.token;
this.redis.hmget(thekey, "id", "r", "w", "ttl", "d", "la", "ip", function(err, resp) {
var mc, o;
if (err) {
cb(err);
return;
}
o = _this._prepareSession(resp);
if (o === null) {
cb(null, {});
return;
}
if (options._noupdate) {
cb(null, o);
return;
}
mc = _this._createMultiStatement(options.app, options.token, o.id, o.ttl);
mc.push(["hincrby", thekey, "r", 1]);
if (o.idle > 1) {
mc.push(["hset", thekey, "la", now]);
}
_this.redis.multi(mc).exec(function(err, resp) {
this.redis.hmget(thekey, "id", "r", "w", "ttl", "d", "la", "ip", (function(_this) {
return function(err, resp) {
var mc, o;
if (err) {

@@ -176,9 +160,28 @@ cb(err);

}
cb(null, o);
});
});
o = _this._prepareSession(resp);
if (o === null) {
cb(null, {});
return;
}
if (options._noupdate) {
cb(null, o);
return;
}
mc = _this._createMultiStatement(options.app, options.token, o.id, o.ttl);
mc.push(["hincrby", thekey, "r", 1]);
if (o.idle > 1) {
mc.push(["hset", thekey, "la", now]);
}
_this.redis.multi(mc).exec(function(err, resp) {
if (err) {
cb(err);
return;
}
cb(null, o);
});
};
})(this));
};
RedisSessions.prototype.kill = function(options, cb) {
var _this = this;
options = this._validate(options, ["app", "token"], cb);

@@ -189,48 +192,50 @@ if (options === false) {

options._noupdate = true;
this.get(options, function(err, resp) {
if (err) {
cb(err);
return;
}
if (!resp.id) {
cb(null, {
kill: 0
});
return;
}
options.id = resp.id;
_this._kill(options, cb);
});
this.get(options, (function(_this) {
return function(err, resp) {
if (err) {
cb(err);
return;
}
if (!resp.id) {
cb(null, {
kill: 0
});
return;
}
options.id = resp.id;
_this._kill(options, cb);
};
})(this));
};
RedisSessions.prototype._kill = function(options, cb) {
var mc,
_this = this;
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]];
this.redis.multi(mc).exec(function(err, resp) {
if (err) {
cb(err);
return;
}
if (resp[4] === 0) {
_this.redis.zrem("" + _this.redisns + options.app + ":_users", options.id, function() {
if (err) {
cb(err);
return;
}
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]];
this.redis.multi(mc).exec((function(_this) {
return function(err, resp) {
if (err) {
cb(err);
return;
}
if (resp[4] === 0) {
_this.redis.zrem("" + _this.redisns + options.app + ":_users", options.id, function() {
if (err) {
cb(err);
return;
}
cb(null, {
kill: resp[3]
});
});
} else {
cb(null, {
kill: resp[3]
});
});
} else {
cb(null, {
kill: resp[3]
});
}
});
}
};
})(this));
};
RedisSessions.prototype.killall = function(options, cb) {
var appsessionkey, appuserkey,
_this = this;
var appsessionkey, appuserkey;
options = this._validate(options, ["app"], cb);

@@ -242,36 +247,5 @@ if (options === false) {

appuserkey = "" + this.redisns + options.app + ":_users";
this.redis.zrange(appsessionkey, 0, -1, function(err, resp) {
var e, globalkeys, mc, thekey, tokenkeys, userkeys, ussets, _i, _len;
if (err) {
cb(err);
return;
}
if (!resp.length) {
cb(null, {
kill: 0
});
return;
}
globalkeys = [];
tokenkeys = [];
userkeys = [];
for (_i = 0, _len = resp.length; _i < _len; _i++) {
e = resp[_i];
thekey = e.split(":");
globalkeys.push("" + options.app + ":" + e);
tokenkeys.push("" + _this.redisns + options.app + ":" + thekey[0]);
userkeys.push(thekey[1]);
}
userkeys = _.uniq(userkeys);
ussets = (function() {
var _j, _len1, _results;
_results = [];
for (_j = 0, _len1 = userkeys.length; _j < _len1; _j++) {
e = userkeys[_j];
_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)];
_this.redis.multi(mc).exec(function(err, resp) {
this.redis.zrange(appsessionkey, 0, -1, (function(_this) {
return function(err, resp) {
var e, globalkeys, j, len, mc, thekey, tokenkeys, userkeys, ussets;
if (err) {

@@ -281,11 +255,43 @@ cb(err);

}
cb(null, {
kill: resp[0]
if (!resp.length) {
cb(null, {
kill: 0
});
return;
}
globalkeys = [];
tokenkeys = [];
userkeys = [];
for (j = 0, len = resp.length; j < len; j++) {
e = resp[j];
thekey = e.split(":");
globalkeys.push(options.app + ":" + e);
tokenkeys.push("" + _this.redisns + options.app + ":" + thekey[0]);
userkeys.push(thekey[1]);
}
userkeys = _.uniq(userkeys);
ussets = (function() {
var k, len1, results;
results = [];
for (k = 0, len1 = userkeys.length; k < len1; k++) {
e = userkeys[k];
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)];
_this.redis.multi(mc).exec(function(err, resp) {
if (err) {
cb(err);
return;
}
cb(null, {
kill: resp[0]
});
});
});
});
};
})(this));
};
RedisSessions.prototype.killsoid = function(options, cb) {
var _this = this;
options = this._validate(options, ["app", "id"], cb);

@@ -295,25 +301,5 @@ if (options === false) {

}
this.redis.smembers("" + this.redisns + options.app + ":us:" + options.id, function(err, resp) {
var mc, token, _i, _len;
if (err) {
cb(err);
return;
}
if (!resp.length) {
cb(null, {
kill: 0
});
return;
}
mc = [];
for (_i = 0, _len = resp.length; _i < _len; _i++) {
token = resp[_i];
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]);
}
mc.push(["exists", "" + _this.redisns + options.app + ":us:" + options.id]);
_this.redis.multi(mc).exec(function(err, resp) {
var e, total, _j, _len1, _ref;
this.redis.smembers("" + this.redisns + options.app + ":us:" + options.id, (function(_this) {
return function(err, resp) {
var j, len, mc, token;
if (err) {

@@ -323,25 +309,46 @@ cb(err);

}
total = 0;
_ref = resp.slice(3);
for (_j = 0, _len1 = _ref.length; _j < _len1; _j += 4) {
e = _ref[_j];
total = total + e;
if (!resp.length) {
cb(null, {
kill: 0
});
return;
}
if (_.last(resp) === 0) {
_this.redis.zrem("" + _this.redisns + options.app + ":_users", options.id, function() {
mc = [];
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]);
}
mc.push(["exists", "" + _this.redisns + options.app + ":us:" + options.id]);
_this.redis.multi(mc).exec(function(err, resp) {
var e, k, len1, ref, total;
if (err) {
cb(err);
return;
}
total = 0;
ref = resp.slice(3);
for (k = 0, len1 = ref.length; k < len1; k += 4) {
e = ref[k];
total = total + e;
}
if (_.last(resp) === 0) {
_this.redis.zrem("" + _this.redisns + options.app + ":_users", options.id, function() {
cb(null, {
kill: total
});
});
} else {
cb(null, {
kill: total
});
});
} else {
cb(null, {
kill: total
});
}
});
});
}
});
};
})(this));
};
RedisSessions.prototype.set = function(options, cb) {
var _this = this;
options = this._validate(options, ["app", "token", "d"], cb);

@@ -352,36 +359,5 @@ if (options === false) {

options._noupdate = true;
this.get(options, function(err, resp) {
var e, mc, nullkeys, thekey;
if (err) {
cb(err);
return;
}
if (!resp.id) {
cb(null, {});
return;
}
nullkeys = [];
for (e in options.d) {
if (options.d[e] === null) {
nullkeys.push(e);
}
}
if (resp.d) {
resp.d = _.extend(_.omit(resp.d, nullkeys), _.omit(options.d, nullkeys));
} else {
resp.d = _.omit(options.d, nullkeys);
}
thekey = "" + _this.redisns + options.app + ":" + options.token;
mc = _this._createMultiStatement(options.app, options.token, resp.id, resp.ttl);
mc.push(["hincrby", thekey, "w", 1]);
if (resp.idle > 1) {
mc.push(["hset", thekey, "la", _this._now()]);
}
if (_.keys(resp.d).length) {
mc.push(["hset", thekey, "d", JSON.stringify(resp.d)]);
} else {
mc.push(["hdel", thekey, "d"]);
resp = _.omit(resp, "d");
}
_this.redis.multi(mc).exec(function(err, reply) {
this.get(options, (function(_this) {
return function(err, resp) {
var e, mc, nullkeys, thekey;
if (err) {

@@ -391,34 +367,67 @@ cb(err);

}
resp.w = reply[3];
cb(null, resp);
});
});
if (!resp.id) {
cb(null, {});
return;
}
nullkeys = [];
for (e in options.d) {
if (options.d[e] === null) {
nullkeys.push(e);
}
}
if (resp.d) {
resp.d = _.extend(_.omit(resp.d, nullkeys), _.omit(options.d, nullkeys));
} else {
resp.d = _.omit(options.d, nullkeys);
}
thekey = "" + _this.redisns + options.app + ":" + options.token;
mc = _this._createMultiStatement(options.app, options.token, resp.id, resp.ttl);
mc.push(["hincrby", thekey, "w", 1]);
if (resp.idle > 1) {
mc.push(["hset", thekey, "la", _this._now()]);
}
if (_.keys(resp.d).length) {
mc.push(["hset", thekey, "d", JSON.stringify(resp.d)]);
} else {
mc.push(["hdel", thekey, "d"]);
resp = _.omit(resp, "d");
}
_this.redis.multi(mc).exec(function(err, reply) {
if (err) {
cb(err);
return;
}
resp.w = reply[3];
cb(null, resp);
});
};
})(this));
};
RedisSessions.prototype.soapp = function(options, cb) {
var _this = this;
if (this._validate(options, ["app", "dt"], cb) === false) {
return;
}
this.redis.zrevrangebyscore("" + this.redisns + options.app + ":_sessions", "+inf", this._now() - options.dt, function(err, resp) {
var e;
if (err) {
cb(err);
return;
}
resp = (function() {
var _i, _len, _results;
_results = [];
for (_i = 0, _len = resp.length; _i < _len; _i++) {
e = resp[_i];
_results.push(e.split(':')[0]);
this.redis.zrevrangebyscore("" + this.redisns + options.app + ":_sessions", "+inf", this._now() - options.dt, (function(_this) {
return function(err, resp) {
var e;
if (err) {
cb(err);
return;
}
return _results;
})();
_this._returnSessions(options, resp, cb);
});
resp = (function() {
var j, len, results;
results = [];
for (j = 0, len = resp.length; j < len; j++) {
e = resp[j];
results.push(e.split(':')[0]);
}
return results;
})();
_this._returnSessions(options, resp, cb);
};
})(this));
};
RedisSessions.prototype.soid = function(options, cb) {
var _this = this;
options = this._validate(options, ["app", "id"], cb);

@@ -428,9 +437,11 @@ if (options === false) {

}
this.redis.smembers("" + this.redisns + options.app + ":us:" + options.id, function(err, resp) {
if (err) {
cb(err);
return;
}
_this._returnSessions(options, resp, cb);
});
this.redis.smembers("" + this.redisns + options.app + ":us:" + options.id, (function(_this) {
return function(err, resp) {
if (err) {
cb(err);
return;
}
_this._returnSessions(options, resp, cb);
};
})(this));
};

@@ -441,10 +452,10 @@

now = this._now();
return [["zadd", "" + this.redisns + app + ":_sessions", now, "" + token + ":" + id], ["zadd", "" + this.redisns + app + ":_users", now, id], ["zadd", "" + this.redisns + "SESSIONS", now + ttl, "" + app + ":" + token + ":" + id]];
return [["zadd", "" + this.redisns + app + ":_sessions", now, token + ":" + id], ["zadd", "" + this.redisns + app + ":_users", now, id], ["zadd", this.redisns + "SESSIONS", now + ttl, app + ":" + token + ":" + id]];
};
RedisSessions.prototype._createToken = function() {
var i, possible, t, _i;
var i, j, possible, t;
t = "";
possible = "ABCDEFGHIJKLMNOPQRSTUVWXYabcdefghijklmnopqrstuvwxyz0123456789";
for (i = _i = 0; _i < 55; i = ++_i) {
for (i = j = 0; j < 55; i = ++j) {
t += possible.charAt(Math.floor(Math.random() * possible.length));

@@ -456,3 +467,3 @@ }

RedisSessions.prototype._handleError = function(cb, err, data) {
var _err, _ref;
var _err, ref;
if (data == null) {

@@ -464,3 +475,3 @@ data = {};

_err.name = err;
_err.message = ((_ref = this._ERRORS) != null ? typeof _ref[err] === "function" ? _ref[err](data) : void 0 : void 0) || "unkown";
_err.message = ((ref = this._ERRORS) != null ? typeof ref[err] === "function" ? ref[err](data) : void 0 : void 0) || "unkown";
} else {

@@ -473,7 +484,7 @@ _err = err;

RedisSessions.prototype._initErrors = function() {
var key, msg, _ref;
var key, msg, ref;
this._ERRORS = {};
_ref = this.ERRORS;
for (key in _ref) {
msg = _ref[key];
ref = this.ERRORS;
for (key in ref) {
msg = ref[key];
this._ERRORS[key] = _.template(msg);

@@ -511,4 +522,3 @@ }

RedisSessions.prototype._returnSessions = function(options, sessions, cb) {
var e, mc,
_this = this;
var e, mc;
if (!sessions.length) {

@@ -521,28 +531,30 @@ cb(null, {

mc = (function() {
var _i, _len, _results;
_results = [];
for (_i = 0, _len = sessions.length; _i < _len; _i++) {
e = sessions[_i];
_results.push(["hmget", "" + this.redisns + options.app + ":" + e, "id", "r", "w", "ttl", "d", "la", "ip"]);
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"]);
}
return _results;
return results;
}).call(this);
this.redis.multi(mc).exec(function(err, resp) {
var o, session, _i, _len;
if (err) {
cb(err);
return;
}
o = [];
for (_i = 0, _len = resp.length; _i < _len; _i++) {
e = resp[_i];
session = _this._prepareSession(e);
if (session !== null) {
o.push(session);
this.redis.multi(mc).exec((function(_this) {
return function(err, resp) {
var j, len, o, session;
if (err) {
cb(err);
return;
}
}
cb(null, {
sessions: o
});
});
o = [];
for (j = 0, len = resp.length; j < len; j++) {
e = resp[j];
session = _this._prepareSession(e);
if (session !== null) {
o.push(session);
}
}
cb(null, {
sessions: o
});
};
})(this));
};

@@ -558,5 +570,5 @@

RedisSessions.prototype._validate = function(o, items, cb) {
var e, item, keys, _i, _len;
for (_i = 0, _len = items.length; _i < _len; _i++) {
item = items[_i];
var e, item, j, keys, len;
for (j = 0, len = items.length; j < len; j++) {
item = items[j];
switch (item) {

@@ -633,20 +645,21 @@ case "app":

RedisSessions.prototype._wipe = function() {
var _this = this;
this.redis.zrangebyscore("" + this.redisns + "SESSIONS", "-inf", this._now(), function(err, resp) {
if (err) {
return;
}
if (resp.length) {
_.each(resp, function(e) {
var options;
e = e.split(':');
options = {
app: e[0],
token: e[1],
id: e[2]
};
_this._kill(options, function() {});
});
}
});
this.redis.zrangebyscore(this.redisns + "SESSIONS", "-inf", this._now(), (function(_this) {
return function(err, resp) {
if (err) {
return;
}
if (resp.length) {
_.each(resp, function(e) {
var options;
e = e.split(':');
options = {
app: e[0],
token: e[1],
id: e[2]
};
_this._kill(options, function() {});
});
}
};
})(this));
};

@@ -653,0 +666,0 @@

{
"name": "redis-sessions",
"description": "An advanced session store for Redis",
"version": "0.5.3",
"version": "1.0.0",
"author": "P. Liess <smrchy+npm@gmail.com>",

@@ -13,4 +13,4 @@ "engines": {

"dependencies": {
"redis": "2.3.0",
"lodash": "3.10.1"
"redis": "2.4.2",
"lodash": "4.x"
},

@@ -21,4 +21,4 @@ "optionalDependencies": {

"devDependencies": {
"mocha": "*",
"should": "*",
"mocha": "2.3.4",
"should": "8.2.0",
"async": "*"

@@ -25,0 +25,0 @@ },

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc