rate-limiter-flexible
Version:
Flexible API rate limiter backed by Redis for distributed node.js applications
119 lines (108 loc) • 2.89 kB
JavaScript
const RateLimiterStoreAbstract = require('./RateLimiterStoreAbstract');
const RateLimiterRes = require('./RateLimiterRes');
class RateLimiterRedis extends RateLimiterStoreAbstract {
/**
*
* @param {Object} opts
* Defaults {
* ... see other in RateLimiterStoreAbstract
*
* redis: RedisClient
* }
*/
constructor(opts) {
super(opts);
if (opts.redis) {
this.client = opts.redis;
} else {
this.client = opts.storeClient;
}
}
_getRateLimiterRes(rlKey, changedPoints, result) {
let [resSet, consumed, resTtlMs] = result;
// Support ioredis results format
if (Array.isArray(resSet)) {
[, resSet] = resSet;
[, consumed] = consumed;
[, resTtlMs] = resTtlMs;
}
const res = new RateLimiterRes();
res.consumedPoints = parseInt(consumed);
res.isFirstInDuration = resSet === 'OK';
res.remainingPoints = Math.max(this.points - res.consumedPoints, 0);
if (resTtlMs === -1) {
// If rlKey created by incrby() not by set()
res.isFirstInDuration = true;
res.msBeforeNext = this.duration;
this.client.expire(rlKey, this.duration);
} else {
res.msBeforeNext = resTtlMs;
}
return res;
}
_upsert(rlKey, points, msDuration, forceExpire = false) {
return new Promise((resolve, reject) => {
const secDuration = Math.floor(msDuration / 1000);
if (forceExpire) {
this.client
.multi()
.set(rlKey, points, 'EX', secDuration)
.pttl(rlKey)
.exec((err, res) => {
if (err) {
return reject(err);
}
return resolve(['FORCE', points, res[1]]);
});
} else {
this.client
.multi()
.set(rlKey, 0, 'EX', secDuration, 'NX')
.incrby(rlKey, points)
.pttl(rlKey)
.exec((err, res) => {
if (err) {
return reject(err);
}
return resolve(res);
});
}
});
}
_get(rlKey) {
return new Promise((resolve, reject) => {
this.client
.multi()
.get(rlKey)
.pttl(rlKey)
.exec((err, res) => {
if (err) {
reject(err);
} else {
const [points] = res;
if (points === null) {
res = null;
} else if (Array.isArray(points)) {
// Support ioredis format
res.unshift([null, 'GET']);
} else {
res.unshift('GET');
}
resolve(res);
}
});
});
}
_delete(rlKey) {
return new Promise((resolve, reject) => {
this.client.del(rlKey, (err, res) => {
if (err) {
reject(err);
} else {
resolve(res > 0);
}
});
});
}
}
module.exports = RateLimiterRedis;