You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
76 lines
2.0 KiB
76 lines
2.0 KiB
// limiter.js
|
|
// Copyright (C) 2022 DTP Technologies, LLC
|
|
// License: Apache-2.0
|
|
|
|
'use strict';
|
|
|
|
const path = require('path');
|
|
|
|
const { RateLimiterRedis } = require('rate-limiter-flexible');
|
|
const expressLimiter = require('express-limiter');
|
|
|
|
const { SiteService, SiteError } = require('../../lib/site-lib');
|
|
|
|
class LimiterService extends SiteService {
|
|
|
|
constructor (dtp) {
|
|
super(dtp, module.exports);
|
|
this.config = require(path.resolve(dtp.config.root, 'config', 'limiter.js'));
|
|
|
|
this.limiter = expressLimiter(this.dtp.app, this.dtp.redis);
|
|
this.handlers = {
|
|
lookup: this.limiterLookup.bind(this),
|
|
whitelist: this.limiterWhitelist.bind(this),
|
|
};
|
|
|
|
this.rateLimiters = { };
|
|
}
|
|
|
|
createMiddleware (config) {
|
|
const options = {
|
|
total: config.total,
|
|
expire: config.expire,
|
|
lookup: this.handlers.lookup,
|
|
whitelist: this.handlers.whitelist,
|
|
onRateLimited: async (req, res, next) => {
|
|
this.emit('limiter:block', req);
|
|
next(new SiteError(config.status || 429, config.message || 'Rate limit exceeded'));
|
|
},
|
|
};
|
|
const middleware = this.limiter(options);
|
|
return async (req, res, next) => {
|
|
return middleware(req, res, next);
|
|
};
|
|
}
|
|
|
|
limiterLookup (req, res, options, next) {
|
|
if (req.user) {
|
|
options.lookup = 'user._id'; // req.user._id, populated by PassportJS session
|
|
} else {
|
|
options.lookup = 'ip'; // req.ip, populated by ExpressJS with trust_proxy=1
|
|
}
|
|
return next();
|
|
}
|
|
|
|
limiterWhitelist (req) {
|
|
return req.user && req.user.flags.isAdmin;
|
|
}
|
|
|
|
createRateLimiter (id, config) {
|
|
if (this.rateLimiters[id]) {
|
|
return this.rateLimiters[id];
|
|
}
|
|
config = Object.assign({
|
|
storeClient: this.dtp.redis,
|
|
}, config);
|
|
this.rateLimiters[id] = new RateLimiterRedis(config);
|
|
return this.rateLimiters[id];
|
|
}
|
|
}
|
|
|
|
module.exports = {
|
|
slug: 'limiter',
|
|
name: 'limiter',
|
|
className: 'LimiterService',
|
|
create: (dtp) => { return new LimiterService(dtp); },
|
|
};
|