diff --git a/.env-sample b/.env-sample index 2aae5d2..0160f20 100644 --- a/.env-sample +++ b/.env-sample @@ -224,3 +224,9 @@ # CDN base url; if S3 details are given, this will probably be a CloudFront path for assets that are uploaded at launch #BTCEXP_CDN_BASE_URL=xxx + +# Rate limiting +# Window size, in minutes, set to -1 to disable rate limiting +#BTCEXP_RATE_LIMIT_WINDOW_MINUTES=xx +# Window max requests allowed +#BTCEXP_RATE_LIMIT_WINDOW_MAX_REQUESTS=xxx diff --git a/app.js b/app.js index fca47a2..888f9d6 100755 --- a/app.js +++ b/app.js @@ -240,35 +240,42 @@ expressApp.use(config.baseUrl, express.static(path.join(__dirname, 'public'), { // https://www.npmjs.com/package/express-rate-limit -const rateLimitWindowMinutes = 15; -const rateLimitWindowMaxRequests = 200; -const rateLimiter = rateLimit({ - windowMs: rateLimitWindowMinutes * 60 * 1000, // 15 minutes - limit: rateLimitWindowMaxRequests, // Limit each IP to 100 requests per `window` (here, per 15 minutes). - standardHeaders: 'draft-7', // draft-6: `RateLimit-*` headers; draft-7: combined `RateLimit` header - legacyHeaders: false, // Disable the `X-RateLimit-*` headers. - skip: function (req, res) { - if (req.originalUrl.includes("/snippet/")) { - return true; +const rateLimitWindowMinutes = config.rateLimiting.windowMinutes; +const rateLimitWindowMaxRequests = config.rateLimiting.windowMaxRequests; + +if (rateLimitWindowMinutes == -1) { + debugLog("Disabling rate limiting"); + +} else { + debugLog(`Enabling rate limiting: ${rateLimitWindowMaxRequests} requests per ${rateLimitWindowMinutes}min`); + + const rateLimiter = rateLimit({ + windowMs: rateLimitWindowMinutes * 60 * 1000, // 15 minutes + limit: rateLimitWindowMaxRequests, // Limit each IP to 100 requests per `window` (here, per 15 minutes). + standardHeaders: 'draft-7', // draft-6: `RateLimit-*` headers; draft-7: combined `RateLimit` header + legacyHeaders: false, // Disable the `X-RateLimit-*` headers. + skip: function (req, res) { + if (req.originalUrl.includes("/snippet/")) { + return true; + } + + if (req.originalUrl.includes("/api/")) { + return true; + } + + return false; + }, + handler: function (req, res, next) { + debugErrorLog(`Rate-limiting request: ip=${req.ip}, req=${req.originalUrl}`) + res.status(429).json({ + message: "Too many requests, please try again later.", + }); } + }); - if (req.originalUrl.includes("/api/")) { - return true; - } - - return false; - }, - handler: function (req, res, next) { - debugErrorLog(`Rate-limiting request: ip=${req.ip}, req=${req.originalUrl}`) - res.status(429).json({ - message: "Too many requests, please try again later.", - }); - } -}); - -// Apply the rate limiting middleware to all requests. -expressApp.use(rateLimiter); - + // Apply the rate limiting middleware to all requests. + expressApp.use(rateLimiter); +} if (config.baseUrl != '/') { diff --git a/app/config.js b/app/config.js index d5734b3..08f060d 100644 --- a/app/config.js +++ b/app/config.js @@ -124,6 +124,11 @@ module.exports = { baseUrl: cdnBaseUrl }, + rateLimiting: { + windowMinutes: process.env.BTCEXP_RATE_LIMIT_WINDOW_MINUTES || 15, + windowMaxRequests: process.env.BTCEXP_RATE_LIMIT_WINDOW_MAX_REQUESTS || 200 + }, + rpcBlacklist: process.env.BTCEXP_RPC_ALLOWALL.toLowerCase() == "true" ? [] : process.env.BTCEXP_RPC_BLACKLIST ? process.env.BTCEXP_RPC_BLACKLIST.split(',').filter(Boolean)