Oops!
diff --git a/package-lock.json b/package-lock.json
index ed0cbf1f1..701592890 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -25,6 +25,7 @@
"expr-eval": "^2.0.2",
"express": "^4.19.2",
"express-async-handler": "^1.2.0",
+ "express-rate-limit": "^7.4.0",
"express-static-gzip": "2.1.7",
"fs-extra": "11.2.0",
"js-yaml": "^4.1.0",
@@ -6352,6 +6353,21 @@
"integrity": "sha512-rCSVtPXRmQSW8rmik/AIb2P0op6l7r1fMW538yyvTMltCO4xQEWMmobfrIxN2V1/mVrgxB8Az3reYF6yUZw37w==",
"license": "MIT"
},
+ "node_modules/express-rate-limit": {
+ "version": "7.4.0",
+ "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-7.4.0.tgz",
+ "integrity": "sha512-v1204w3cXu5gCDmAvgvzI6qjzZzoMWKnyVDk3ACgfswTQLYiGen+r8w0VnXnGMmzEN/g8fwIQ4JrFFd4ZP6ssg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/express-rate-limit"
+ },
+ "peerDependencies": {
+ "express": "4 || 5 || ^5.0.0-beta.1"
+ }
+ },
"node_modules/express-static-gzip": {
"version": "2.1.7",
"resolved": "https://registry.npmjs.org/express-static-gzip/-/express-static-gzip-2.1.7.tgz",
diff --git a/package.json b/package.json
index 8248f4304..6926097a9 100644
--- a/package.json
+++ b/package.json
@@ -100,6 +100,7 @@
"expr-eval": "^2.0.2",
"express": "^4.19.2",
"express-async-handler": "^1.2.0",
+ "express-rate-limit": "^7.4.0",
"express-static-gzip": "2.1.7",
"fs-extra": "11.2.0",
"js-yaml": "^4.1.0",
diff --git a/server/homebrew.api.js b/server/homebrew.api.js
index 0f2283c2b..1891e2791 100644
--- a/server/homebrew.api.js
+++ b/server/homebrew.api.js
@@ -9,6 +9,7 @@ const yaml = require('js-yaml');
const asyncHandler = require('express-async-handler');
const { nanoid } = require('nanoid');
const { splitTextStyleAndMetadata } = require('../shared/helpers.js');
+const rateLimit = require('express-rate-limit');
const { DEFAULT_BREW, DEFAULT_BREW_LOAD } = require('./brewDefaults.js');
@@ -24,6 +25,16 @@ const isStaticTheme = (renderer, themeName)=>{
// });
// };
+// Define rate limiter options
+const rateLimiter = rateLimit({
+ timeWindow : 5 * 60 * 1000, // 5 minutes window
+ max : 100, // limit each IP to 100 requests per timeWindow
+ handler: (req, res, next) => {
+ console.log(`Rate limiting user ${req.account?.username}`);
+ throw { HBErrorCode: '55', status: 429, message: 'Too many requests from this IP, please try again after 5 minutes'};
+ }
+});
+
const MAX_TITLE_LENGTH = 100;
const api = {
@@ -473,6 +484,7 @@ const api = {
}
};
+router.use('/api', rateLimiter);
router.use('/api', require('./middleware/check-client-version.js'));
router.post('/api', asyncHandler(api.newBrew));
router.put('/api/:id', asyncHandler(api.getBrew('edit', true)), asyncHandler(api.updateBrew));