Skip to content

Commit f94226f

Browse files
authored
refactor: ignore rate limit for plus users and optimize querying (#2976)
Follow up on this: dailydotdev/daily#1236, I decided to ignore rate limit for Plus users. I feel confident that they're not here for spam
1 parent 8b9ec54 commit f94226f

File tree

3 files changed

+44
-3
lines changed

3 files changed

+44
-3
lines changed

__tests__/posts.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ import { generateUUID } from '../src/ids';
8989
import { GQLResponse } from 'mercurius-integration-testing';
9090
import type { GQLPostSmartTitle } from '../src/schema/posts';
9191
import { SubscriptionCycles } from '../src/paddle';
92+
import { remoteConfig } from '../src/remoteConfig';
9293
import {
9394
ContentPreferenceStatus,
9495
ContentPreferenceType,
@@ -2576,6 +2577,17 @@ describe('mutation sharePost', () => {
25762577

25772578
describe('rate limiting', () => {
25782579
const redisKey = `${rateLimiterName}:1:createPost`;
2580+
2581+
beforeEach(() => {
2582+
// Set the post rate limit to 1 for testing
2583+
remoteConfig.vars.postRateLimit = 1;
2584+
});
2585+
2586+
afterEach(() => {
2587+
// Reset to default
2588+
remoteConfig.vars.postRateLimit = undefined;
2589+
});
2590+
25792591
it('store rate limiting state in redis', async () => {
25802592
loggedUser = '1';
25812593

@@ -2611,6 +2623,28 @@ describe('mutation sharePost', () => {
26112623
expect(await getRedisObjectExpiry(redisKey)).toBeGreaterThanOrEqual(20);
26122624
});
26132625

2626+
it('should bypass rate limit for Plus members', async () => {
2627+
loggedUser = '1';
2628+
2629+
// Set user as Plus member
2630+
await con.getRepository(User).update(
2631+
{ id: '1' },
2632+
{
2633+
subscriptionFlags: { cycle: SubscriptionCycles.Yearly },
2634+
reputation: 1,
2635+
},
2636+
);
2637+
2638+
// Create multiple posts without hitting rate limit
2639+
for (let i = 0; i < 3; i++) {
2640+
await deleteKeysByPattern(`${rateLimiterName}:*`);
2641+
const res = await client.mutate(MUTATION, {
2642+
variables: variables,
2643+
});
2644+
expect(res.errors).toBeFalsy();
2645+
}
2646+
});
2647+
26142648
describe('high rate squads', () => {
26152649
beforeEach(async () => {
26162650
await con.getRepository(SquadSource).save({

__tests__/setup.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ jest.mock('../src/remoteConfig', () => ({
5151
de: 'German',
5252
'zh-Hans': 'ChineseSimplified',
5353
},
54+
postRateLimit: 2,
5455
},
5556
}));
5657

src/common/rateLimit.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { Comment, Post, User } from '../entity';
33
import { remoteConfig } from '../remoteConfig';
44
import { subDays } from 'date-fns';
55
import { GraphQLError } from 'graphql/index';
6+
import { isPlusMember } from '../paddle';
67

78
export class RateLimitError extends GraphQLError {
89
extensions = {};
@@ -32,12 +33,17 @@ const ensureReputationBasedRateLimit = async (
3233
errorMessage: string,
3334
): Promise<void> => {
3435
const [user, countValue] = await Promise.all([
35-
con
36-
.getRepository(User)
37-
.findOneOrFail({ select: ['id', 'reputation'], where: { id: userId } }),
36+
con.getRepository(User).findOneOrFail({
37+
select: ['id', 'reputation', 'subscriptionFlags'],
38+
where: { id: userId },
39+
}),
3840
count,
3941
]);
4042

43+
if (isPlusMember(user.subscriptionFlags?.cycle)) {
44+
return;
45+
}
46+
4147
if (
4248
remoteConfig.vars?.rateLimitReputationThreshold &&
4349
user.reputation > remoteConfig.vars.rateLimitReputationThreshold

0 commit comments

Comments
 (0)