const { RateLimiter } = require("limiter"); const { log } = require("../src/util"); class KumaRateLimiter { /** * @param {object} config Rate limiter configuration object */ constructor(config) { this.errorMessage = config.errorMessage; this.tokensPerInterval = config.tokensPerInterval; this.rateLimiter = new RateLimiter(config); } /** * Callback for pass * @callback passCB * @param {object} err Too many requests */ /** * Should the request be passed through * @param {passCB} callback Callback function to call with decision * @param {number} num Number of tokens to remove * @returns {Promise} Should the request be allowed? */ async pass(callback, num = 1) { const remainingRequests = await this.removeTokens(num); if (remainingRequests < this.tokensPerInterval * 0.2) { log.warn( "rate-limit", `${remainingRequests}/${this.tokensPerInterval} remaining requests until rate limiting` ); } else { log.debug( "rate-limit", `${remainingRequests}/${this.tokensPerInterval} remaining requests until rate limiting` ); } if (remainingRequests < 0) { if (callback) { callback({ ok: false, msg: this.errorMessage, }); } return false; } return true; } /** * Remove a given number of tokens * @param {number} num Number of tokens to remove * @returns {Promise} Number of remaining tokens */ async removeTokens(num = 1) { return await this.rateLimiter.removeTokens(num); } } const loginRateLimiter = new KumaRateLimiter({ tokensPerInterval: 20, interval: "minute", fireImmediately: true, errorMessage: "Too frequently, try again later.", }); const apiRateLimiter = new KumaRateLimiter({ tokensPerInterval: 60, interval: "minute", fireImmediately: true, errorMessage: "Too frequently, try again later.", }); module.exports = { loginRateLimiter, apiRateLimiter, };