* feat: Put request rate limit at application server This PR contains implementation for a blanket rate limiter at application server level. Currently the allowed throughput is set high only to be changed later as per the actual data gathered. * Simplify implementation 1. Remove shutdown handler to purge rate limiter keys 2. Have separate keys for default and custom(route-based) rate limiters 3. Do not kill default rate limiter because it is not needed anymore due to (2) above * Set 60s as default for rate limiting window * Fix env types
61 lines
1.6 KiB
TypeScript
61 lines
1.6 KiB
TypeScript
import { Context, Next } from "koa";
|
|
import { defaults } from "lodash";
|
|
import RateLimiter from "@server/RateLimiter";
|
|
import env from "@server/env";
|
|
import { RateLimitExceededError } from "@server/errors";
|
|
import Redis from "@server/redis";
|
|
import { RateLimiterConfig } from "@server/types";
|
|
|
|
export function rateLimiter() {
|
|
return async function rateLimiterMiddleware(ctx: Context, next: Next) {
|
|
if (!env.RATE_LIMITER_ENABLED) {
|
|
return next();
|
|
}
|
|
|
|
const key = RateLimiter.hasRateLimiter(ctx.path)
|
|
? `${ctx.path}:${ctx.ip}`
|
|
: `${ctx.ip}`;
|
|
const limiter = RateLimiter.getRateLimiter(ctx.path);
|
|
|
|
try {
|
|
await limiter.consume(key);
|
|
} catch (rateLimiterRes) {
|
|
ctx.set("Retry-After", `${rateLimiterRes.msBeforeNext / 1000}`);
|
|
ctx.set("RateLimit-Limit", `${limiter.points}`);
|
|
ctx.set("RateLimit-Remaining", `${rateLimiterRes.remainingPoints}`);
|
|
ctx.set(
|
|
"RateLimit-Reset",
|
|
`${new Date(Date.now() + rateLimiterRes.msBeforeNext)}`
|
|
);
|
|
|
|
throw RateLimitExceededError();
|
|
}
|
|
|
|
return next();
|
|
};
|
|
}
|
|
|
|
export function registerRateLimiter(config: RateLimiterConfig) {
|
|
return async function registerRateLimiterMiddleware(
|
|
ctx: Context,
|
|
next: Next
|
|
) {
|
|
if (!env.RATE_LIMITER_ENABLED) {
|
|
return next();
|
|
}
|
|
|
|
if (!RateLimiter.hasRateLimiter(ctx.path)) {
|
|
RateLimiter.setRateLimiter(
|
|
ctx.path,
|
|
defaults(config, {
|
|
duration: env.RATE_LIMITER_DURATION_WINDOW,
|
|
keyPrefix: RateLimiter.RATE_LIMITER_REDIS_KEY_PREFIX,
|
|
storeClient: Redis.defaultClient,
|
|
})
|
|
);
|
|
}
|
|
|
|
return next();
|
|
};
|
|
}
|