overleaf/services/web/app/src/infrastructure/RateLimiter.js

47 lines
1.3 KiB
JavaScript
Raw Normal View History

const settings = require('settings-sharelatex')
const Metrics = require('metrics-sharelatex')
const RedisWrapper = require('./RedisWrapper')
const rclient = RedisWrapper.client('ratelimiter')
const RollingRateLimiter = require('rolling-rate-limiter')
const { promisifyAll } = require('../util/promises')
const RateLimiter = {
addCount(opts, callback) {
if (settings.disableRateLimits) {
return callback(null, true)
}
if (callback == null) {
callback = function() {}
}
const namespace = `RateLimit:${opts.endpointName}:`
const k = `{${opts.subjectName}}`
const limiter = RollingRateLimiter({
redis: rclient,
namespace,
interval: opts.timeInterval * 1000,
maxInInterval: opts.throttle
})
limiter(k, function(err, timeLeft, actionsLeft) {
if (err) {
return callback(err)
}
const allowed = timeLeft === 0
if (!allowed) {
Metrics.inc('rate-limit-hit', 1, {
path: opts.endpointName
})
}
return callback(null, allowed)
})
},
clearRateLimit(endpointName, subject, callback) {
// same as the key which will be built by RollingRateLimiter (namespace+k)
const keyName = `RateLimit:${endpointName}:{${subject}}`
rclient.del(keyName, callback)
}
}
RateLimiter.promises = promisifyAll(RateLimiter)
module.exports = RateLimiter