mirror of
https://github.com/directus/directus.git
synced 2026-04-25 03:00:53 -04:00
Adding choice for caching rate limiting
This can be set in the config to 'redis' or 'memory'
This commit is contained in:
22
api/package-lock.json
generated
22
api/package-lock.json
generated
@@ -2317,7 +2317,8 @@
|
||||
"denque": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/denque/-/denque-1.4.1.tgz",
|
||||
"integrity": "sha512-OfzPuSZKGcgr96rf1oODnfjqBFmr1DVoc/TrItj3Ohe0Ah1C5WX5Baquw/9U9KovnQ88EqmJbD66rKYUQYN1tQ=="
|
||||
"integrity": "sha512-OfzPuSZKGcgr96rf1oODnfjqBFmr1DVoc/TrItj3Ohe0Ah1C5WX5Baquw/9U9KovnQ88EqmJbD66rKYUQYN1tQ==",
|
||||
"optional": true
|
||||
},
|
||||
"depd": {
|
||||
"version": "1.1.2",
|
||||
@@ -5293,6 +5294,14 @@
|
||||
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-2.0.2.tgz",
|
||||
"integrity": "sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA=="
|
||||
},
|
||||
"node-cache": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/node-cache/-/node-cache-5.1.2.tgz",
|
||||
"integrity": "sha512-t1QzWwnk4sjLWaQAS8CHgOJ+RAfmHpxFWmc36IWTiWHQfs0w5JDMBS1b1ZxQteo0vVVuWJvIUKHDkkeK7vIGCg==",
|
||||
"requires": {
|
||||
"clone": "2.x"
|
||||
}
|
||||
},
|
||||
"node-exceptions": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/node-exceptions/-/node-exceptions-4.0.1.tgz",
|
||||
@@ -6294,7 +6303,8 @@
|
||||
"rate-limiter-flexible": {
|
||||
"version": "2.1.10",
|
||||
"resolved": "https://registry.npmjs.org/rate-limiter-flexible/-/rate-limiter-flexible-2.1.10.tgz",
|
||||
"integrity": "sha512-Pa+8TPD4xYaiCUB5K4a/+j2FHDUe4HP1g49JmKEmkOkhqPaeVqxJsZuuVaza/svSCOT+V73vtsyBiSFK/e1yXw=="
|
||||
"integrity": "sha512-Pa+8TPD4xYaiCUB5K4a/+j2FHDUe4HP1g49JmKEmkOkhqPaeVqxJsZuuVaza/svSCOT+V73vtsyBiSFK/e1yXw==",
|
||||
"optional": true
|
||||
},
|
||||
"raw-body": {
|
||||
"version": "2.4.0",
|
||||
@@ -6433,6 +6443,7 @@
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/redis/-/redis-3.0.2.tgz",
|
||||
"integrity": "sha512-PNhLCrjU6vKVuMOyFu7oSP296mwBkcE6lrAjruBYG5LgdSqtRBoVQIylrMyVZD/lkF24RSNNatzvYag6HRBHjQ==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"denque": "^1.4.1",
|
||||
"redis-commands": "^1.5.0",
|
||||
@@ -6443,17 +6454,20 @@
|
||||
"redis-commands": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.6.0.tgz",
|
||||
"integrity": "sha512-2jnZ0IkjZxvguITjFTrGiLyzQZcTvaw8DAaCXxZq/dsHXz7KfMQ3OUJy7Tz9vnRtZRVz6VRCPDvruvU8Ts44wQ=="
|
||||
"integrity": "sha512-2jnZ0IkjZxvguITjFTrGiLyzQZcTvaw8DAaCXxZq/dsHXz7KfMQ3OUJy7Tz9vnRtZRVz6VRCPDvruvU8Ts44wQ==",
|
||||
"optional": true
|
||||
},
|
||||
"redis-errors": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz",
|
||||
"integrity": "sha1-62LSrbFeTq9GEMBK/hUpOEJQq60="
|
||||
"integrity": "sha1-62LSrbFeTq9GEMBK/hUpOEJQq60=",
|
||||
"optional": true
|
||||
},
|
||||
"redis-parser": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz",
|
||||
"integrity": "sha1-tm2CjNyv5rS4pCin3vTGvKwxyLQ=",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"redis-errors": "^1.0.0"
|
||||
}
|
||||
|
||||
@@ -56,9 +56,9 @@
|
||||
"dev": "cross-env NODE_ENV=development LOG_LEVEL=trace ts-node-dev --files src/server.ts --respawn --watch \"src/**/*.ts\" --transpile-only",
|
||||
"cli": "cross-env NODE_ENV=development ts-node --script-mode --transpile-only src/cli/index.ts",
|
||||
"prepublishOnly": "npm run build",
|
||||
"build-windows":"rd /s /q dist 2>nul && tsc -b && copyfiles \"src\\**\\*.*\" -e \"src\\**\\*.ts\" -u 1 dist",
|
||||
"dev-windows":"cross-env NODE_ENV=development LOG_LEVEL=trace ts-node-dev --files src\\server.ts --respawn --watch \"src\\**\\*.ts\" --transpile-only",
|
||||
"cli-windows":"cross-env NODE_ENV=development ts-node --script-mode --transpile-only src\\cli\\index.ts",
|
||||
"build-windows": "rd /s /q dist 2>nul && tsc -b && copyfiles \"src\\**\\*.*\" -e \"src\\**\\*.ts\" -u 1 dist",
|
||||
"dev-windows": "cross-env NODE_ENV=development LOG_LEVEL=trace ts-node-dev --files src\\server.ts --respawn --watch \"src\\**\\*.ts\" --transpile-only",
|
||||
"cli-windows": "cross-env NODE_ENV=development ts-node --script-mode --transpile-only src\\cli\\index.ts",
|
||||
"prepublishOnly-windows": "npm run build-windows"
|
||||
},
|
||||
"files": [
|
||||
@@ -103,6 +103,7 @@
|
||||
"lodash": "^4.17.19",
|
||||
"ms": "^2.1.2",
|
||||
"nanoid": "^3.1.12",
|
||||
"node-cache": "^5.1.2",
|
||||
"nodemailer": "^6.4.11",
|
||||
"ora": "^4.1.1",
|
||||
"otplib": "^12.0.1",
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
{{ redisServer }}
|
||||
|
||||
####################################################################################################
|
||||
# Rate Limiting
|
||||
# Rate Limiting and caching
|
||||
|
||||
{{ rateLimits}}
|
||||
####################################################################################################
|
||||
|
||||
@@ -20,33 +20,38 @@ const redisClient = redis.createClient({
|
||||
});
|
||||
|
||||
const rateLimiter: RequestHandler = asyncHandler(async (req, res, next) => {
|
||||
// first need to check that redis is running!
|
||||
if (!redisClient) {
|
||||
throw new RedisNotFoundException('Redis client does not exist');
|
||||
}
|
||||
// options for the rate limiter are set below. Opts can be found
|
||||
// at https://github.com/animir/node-rate-limiter-flexible/wiki/Options
|
||||
// more basic for memory store
|
||||
const opts = {
|
||||
storeClient: redisClient,
|
||||
points: env.CONSUMED_POINTS_LIMIT, // Number of points
|
||||
duration: env.CONSUMED_RESET_DURATION, // Number of seconds before consumed points are reset.
|
||||
|
||||
// Custom
|
||||
execEvenly: env.EXEC_EVENLY, // delay actions after first action - this may need adjusting (leaky bucket)
|
||||
blockDuration: env.BLOCK_POINT_DURATION, // Do not block if consumed more than points
|
||||
inmemoryBlockOnConsumed: env.INMEMORY_BLOCK_CONSUMED, // eg if 200 points consumed
|
||||
inmemoryBlockDuration: env.INMEMEMORY_BLOCK_DURATION, // block for certain amount of seconds
|
||||
keyPrefix: 'rlflx', // must be unique for limiters with different purpose
|
||||
insuranceLimiter: new RateLimiterMemory({
|
||||
points: 20, // 20 is fair if you have 5 workers and 1 cluster
|
||||
duration: 1,
|
||||
}),
|
||||
};
|
||||
|
||||
const rateLimiterRedis = new RateLimiterRedis(opts);
|
||||
let rateLimiterSet = new RateLimiterMemory(opts);
|
||||
|
||||
if (env.RATE_LIMIT_TYPE === 'redis') {
|
||||
const redisOpts = {
|
||||
...opts,
|
||||
storeClient: redisClient,
|
||||
// Custom
|
||||
execEvenly: env.EXEC_EVENLY, // delay actions after first action - this may need adjusting (leaky bucket)
|
||||
blockDuration: env.BLOCK_POINT_DURATION, // Do not block if consumed more than points
|
||||
inmemoryBlockOnConsumed: env.INMEMORY_BLOCK_CONSUMED, // eg if 200 points consumed
|
||||
inmemoryBlockDuration: env.INMEMEMORY_BLOCK_DURATION, // block for certain amount of seconds
|
||||
};
|
||||
|
||||
rateLimiterSet = new RateLimiterRedis(redisOpts);
|
||||
|
||||
// first need to check that redis is running!
|
||||
if (!redisClient) {
|
||||
throw new RedisNotFoundException('Redis client does not exist');
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
await rateLimiterRedis.consume(req.ip);
|
||||
await rateLimiterSet.consume(req.ip);
|
||||
} catch (rejRes) {
|
||||
// If there is no error, rateLimiterRedis promise rejected with number of ms before next request allowed
|
||||
const secs = Math.round(rejRes.msBeforeNext / 1000) || 1;
|
||||
|
||||
Reference in New Issue
Block a user