Adds rate limit to reaction endpoint to prevent botting (#1860)

This commit is contained in:
Manuel Emilio Urena
2025-09-26 12:00:09 -04:00
committed by GitHub
parent a585477102
commit f8756fb5dd
2 changed files with 24 additions and 2 deletions
+6 -2
View File
@@ -1,7 +1,11 @@
import { toggleReactionHandler } from './../controllers/reaction.controller';
import { toggleReactionSchema } from './../schema/reaction.schema';
import { toggleReactionSchema, reactionRateLimits } from './../schema/reaction.schema';
import { router, guardedProcedure } from '~/server/trpc';
import { rateLimit } from '~/server/middleware.trpc';
export const reactionRouter = router({
toggle: guardedProcedure.input(toggleReactionSchema).mutation(toggleReactionHandler),
toggle: guardedProcedure
.input(toggleReactionSchema)
.use(rateLimit(reactionRateLimits))
.mutation(toggleReactionHandler),
});
+18
View File
@@ -1,5 +1,23 @@
import { ReviewReactions } from '~/shared/utils/prisma/enums';
import * as z from 'zod';
import { CacheTTL } from '~/server/common/constants';
import type { RateLimit } from '~/server/middleware.trpc';
export const reactionRateLimits: RateLimit[] = [
// 1 minute limit - allow rapid interactions but prevent botting (60 reactions/min = 1 per second)
{ limit: 60, period: CacheTTL.xs },
// 10 minute limit - accommodate active browsing with multiple reaction types
{ limit: 300, period: CacheTTL.md },
// 1 hour limit - normal browsing patterns with reaction changes
{ limit: 1000, period: CacheTTL.hour },
// 24 hour limit - prevent systematic abuse while allowing heavy usage
{ limit: 5000, period: CacheTTL.day },
// Higher limits for users with good reputation scores (≥1000 total score)
{ limit: 100, period: CacheTTL.xs, userReq: (user) => user.meta?.scores?.total >= 1000 },
{ limit: 500, period: CacheTTL.md, userReq: (user) => user.meta?.scores?.total >= 1000 },
{ limit: 1500, period: CacheTTL.hour, userReq: (user) => user.meta?.scores?.total >= 1000 },
{ limit: 8000, period: CacheTTL.day, userReq: (user) => user.meta?.scores?.total >= 1000 },
];
export const reactableEntities: readonly [string, ...string[]] = [
'question',