omnivore/packages/api/src/utils/rate_limit.ts

59 lines
1.7 KiB
TypeScript

import rateLimit, { MemoryStore, Options } from 'express-rate-limit'
import { RedisStore } from 'rate-limit-redis'
import { env } from '../env'
import { redisDataSource } from '../redis_data_source'
import { getClaimsByToken, getTokenByRequest, isSystemRequest } from './auth'
// use the redis store if we have a redis connection
const redisClient = redisDataSource.redisClient
const getStore = (prefix?: string) =>
redisClient
? new RedisStore({
sendCommand: (command: string, ...args: string[]) =>
redisClient.call(command, ...args) as never,
prefix,
})
: new MemoryStore()
const configs: Partial<Options> = {
windowMs: 60 * 1000, // 1 minute
max: 5,
// skip preflight requests and test requests and system requests
skip: (req) =>
req.method === 'OPTIONS' || env.dev.isLocal || isSystemRequest(req),
store: getStore('rate-limit'),
}
export const apiLimiter = rateLimit({
...configs,
max: async (req) => {
// 100 RPM for an authenticated request, 15 for a non-authenticated request
const token = getTokenByRequest(req)
try {
const claims = await getClaimsByToken(token)
return claims ? 60 : 15
} catch (e) {
console.log('non-authenticated request')
return 15
}
},
keyGenerator: (req) => {
return getTokenByRequest(req) || req.ip
},
store: getStore('api-rate-limit'),
})
// 5 RPM for auth requests
export const authLimiter = rateLimit({
...configs,
store: getStore('auth-rate-limit'),
})
// The hourly limiter is used on the create account,
// and reset password endpoints
// this limits users to five operations per an hour
export const hourlyLimiter = rateLimit({
...configs,
windowMs: 60 * 60 * 1000,
store: getStore('hourly-rate-limit'),
})