diff --git a/backend/src/db/migrations/20240611151327_custom-rate-limits-for-self-hosting.ts b/backend/src/db/migrations/20240611151327_custom-rate-limits-for-self-hosting.ts index 32bb55a61..cb8007d9f 100644 --- a/backend/src/db/migrations/20240611151327_custom-rate-limits-for-self-hosting.ts +++ b/backend/src/db/migrations/20240611151327_custom-rate-limits-for-self-hosting.ts @@ -19,6 +19,9 @@ export async function up(knex: Knex): Promise { }); await createOnUpdateTrigger(knex, TableName.RateLimit); + + // create rate limit entry + await knex(TableName.RateLimit).insert({}); } } diff --git a/backend/src/server/config/rateLimiter.ts b/backend/src/server/config/rateLimiter.ts index cc62fd08c..e5bec1c6c 100644 --- a/backend/src/server/config/rateLimiter.ts +++ b/backend/src/server/config/rateLimiter.ts @@ -61,20 +61,22 @@ export const publicEndpointLimit: RateLimitOptions = { keyGenerator: (req) => req.realIp }; -export const globalRateLimiterCfg = async (rateLimits: TRateLimit): Promise => { +export const globalRateLimiterCfg = async (rateLimits?: TRateLimit): Promise => { const appCfg = getConfig(); const redis = appCfg.isRedisConfigured ? new Redis(appCfg.REDIS_URL, { connectTimeout: 500, maxRetriesPerRequest: 1 }) : null; - readLimit.max = rateLimits.readRateLimit; - publicEndpointLimit.max = rateLimits.publicEndpointLimit; - writeLimit.max = rateLimits.writeRateLimit; - secretsLimit.max = rateLimits.secretsRateLimit; - authRateLimit.max = rateLimits.authRateLimit; - inviteUserRateLimit.max = rateLimits.inviteUserRateLimit; - mfaRateLimit.max = rateLimits.mfaRateLimit; - creationLimit.max = rateLimits.creationLimit; + if (rateLimits) { + readLimit.max = rateLimits.readRateLimit; + publicEndpointLimit.max = rateLimits.publicEndpointLimit; + writeLimit.max = rateLimits.writeRateLimit; + secretsLimit.max = rateLimits.secretsRateLimit; + authRateLimit.max = rateLimits.authRateLimit; + inviteUserRateLimit.max = rateLimits.inviteUserRateLimit; + mfaRateLimit.max = rateLimits.mfaRateLimit; + creationLimit.max = rateLimits.creationLimit; + } return { timeWindow: 60 * 1000, diff --git a/backend/src/server/routes/v1/rate-limit-router.ts b/backend/src/server/routes/v1/rate-limit-router.ts index be38afb73..f2f200228 100644 --- a/backend/src/server/routes/v1/rate-limit-router.ts +++ b/backend/src/server/routes/v1/rate-limit-router.ts @@ -1,6 +1,7 @@ import { z } from "zod"; import { RateLimitSchema } from "@app/db/schemas"; +import { BadRequestError } from "@app/lib/errors"; import { readLimit } from "@app/server/config/rateLimiter"; import { verifySuperAdmin } from "@app/server/plugins/auth/superAdmin"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; @@ -22,12 +23,18 @@ export const registerRateLimitRouter = async (server: FastifyZodProvider) => { }, handler: async () => { const rateLimit = await server.services.rateLimit.getRateLimits(); + if (!rateLimit) { + throw new BadRequestError({ + name: "Get Rate Limit Error", + message: "Rate limit configuration does not exist." + }); + } return { rateLimit }; } }); server.route({ - method: "PATCH", + method: "PUT", url: "/", config: { rateLimit: readLimit @@ -40,14 +47,14 @@ export const registerRateLimitRouter = async (server: FastifyZodProvider) => { schema: { body: z.object({ - readRateLimit: z.number().optional(), - writeRateLimit: z.number().optional(), - secretsRateLimit: z.number().optional(), - authRateLimit: z.number().optional(), - inviteUserRateLimit: z.number().optional(), - mfaRateLimit: z.number().optional(), - creationLimit: z.number().optional(), - publicEndpointLimit: z.number().optional() + readRateLimit: z.number(), + writeRateLimit: z.number(), + secretsRateLimit: z.number(), + authRateLimit: z.number(), + inviteUserRateLimit: z.number(), + mfaRateLimit: z.number(), + creationLimit: z.number(), + publicEndpointLimit: z.number() }), response: { 200: z.object({ diff --git a/backend/src/services/rate-limit/rate-limit-service.ts b/backend/src/services/rate-limit/rate-limit-service.ts index 4d4511127..5f1e75b95 100644 --- a/backend/src/services/rate-limit/rate-limit-service.ts +++ b/backend/src/services/rate-limit/rate-limit-service.ts @@ -1,5 +1,3 @@ -import { BadRequestError } from "@app/lib/errors"; - import { TRateLimitDALFactory } from "./rate-limit-dal"; import { TRateLimit, TRateLimitUpdateDTO } from "./rate-limit-types"; @@ -10,20 +8,16 @@ type TRateLimitServiceFactoryDep = { export type TRateLimitServiceFactory = ReturnType; export const rateLimitServiceFactory = ({ rateLimitDAL }: TRateLimitServiceFactoryDep) => { - const getRateLimits = async (): Promise => { - return rateLimitDAL.findOne({ id: "00000000-0000-0000-0000-000000000000" }); + const getRateLimits = async (): Promise => { + try { + return await rateLimitDAL.findOne({ id: "00000000-0000-0000-0000-000000000000" }); + } catch (error) { + return undefined; + } }; const updateRateLimit = async (updates: TRateLimitUpdateDTO): Promise => { - const rateLimit = await rateLimitDAL.findOne({ - id: "00000000-0000-0000-0000-000000000000" - }); - - if (!rateLimit) { - throw new BadRequestError({ name: "Rate Limit Update", message: "Rate Limit does not exist yet" }); - } - - return rateLimitDAL.updateById(rateLimit.id, updates); + return rateLimitDAL.updateById("00000000-0000-0000-0000-000000000000", updates); }; return { diff --git a/frontend/src/hooks/api/rateLimit/mutation.ts b/frontend/src/hooks/api/rateLimit/mutation.ts index e2cccf78f..22a7f9898 100644 --- a/frontend/src/hooks/api/rateLimit/mutation.ts +++ b/frontend/src/hooks/api/rateLimit/mutation.ts @@ -8,9 +8,9 @@ import { TRateLimit } from "./types"; export const useUpdateRateLimit = () => { const queryClient = useQueryClient(); - return useMutation>({ + return useMutation({ mutationFn: async (opt) => { - const { data } = await apiRequest.patch<{ rateLimit: TRateLimit }>("/api/v1/rate-limit", opt); + const { data } = await apiRequest.put<{ rateLimit: TRateLimit }>("/api/v1/rate-limit", opt); return data.rateLimit; }, onSuccess: (data) => {