// limiter.js // Copyright (C) 2021 Digital Telepresence, LLC // License: Apache-2.0 'use strict'; const path = require('path'); 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), }; } create (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; } } module.exports = { slug: 'limiter', name: 'limiter', create: (dtp) => { return new LimiterService(dtp); }, };