Skip to content

Commit ec938ac

Browse files
committed
feat: added global rate limiting
1 parent 3868290 commit ec938ac

File tree

6 files changed

+64
-4
lines changed

6 files changed

+64
-4
lines changed

backend/package-lock.json

Lines changed: 28 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

backend/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
"scripts": {
77
"dev": "npx ts-node-dev src/index.ts",
88
"build": "tsc",
9+
"prestart": "npx drizzle-kit migrate",
910
"start": "node dist/index.js",
1011
"format": "prettier --write ."
1112
},
@@ -21,6 +22,7 @@
2122
"dotenv": "^17.0.0",
2223
"drizzle-orm": "^0.44.2",
2324
"express": "^5.1.0",
25+
"express-rate-limit": "^8.0.1",
2426
"express-session": "^1.18.1",
2527
"ioredis": "^5.6.1",
2628
"node-cron": "^4.2.1",

backend/src/index.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,11 @@ import {
2222
fetchSubmissions,
2323
fetchRatingChanges,
2424
} from "./controllers/codeforces";
25+
import { rateLimit } from 'express-rate-limit';
2526
import "./workers/codeforcesWorker";
2627

2728

29+
2830
dotenv.config();
2931
const app = express();
3032

@@ -120,6 +122,17 @@ passport.deserializeUser(async (userId: string, done) => {
120122
}
121123
});
122124

125+
const limiter = rateLimit({
126+
windowMs: 1000,
127+
limit: 25,
128+
standardHeaders: 'draft-8', // draft-6: `RateLimit-*` headers; draft-7 & draft-8: combined `RateLimit` header
129+
legacyHeaders: false, // Disable the `X-RateLimit-*` headers.
130+
ipv6Subnet: 56, // Set to 60 or 64 to be less aggressive, or 52 or 48 to be more aggressive
131+
// store: ... , // Redis, Memcached, etc. See below.
132+
})
133+
134+
app.use(limiter)
135+
123136
app.get("/", (_req, res) => {
124137
res.send("Backend API is working!");
125138
});

backend/src/routes/account.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,25 @@ import {
1616
problems,
1717
} from "../drizzle/schema";
1818
import { and, eq } from "drizzle-orm";
19+
import {rateLimit} from "express-rate-limit";
1920

2021
const router = express.Router();
2122

2223
router.use(requireAuth);
2324

25+
const limiter = rateLimit({
26+
windowMs: 5000,
27+
limit: 1,
28+
standardHeaders: 'draft-8', // draft-6: `RateLimit-*` headers; draft-7 & draft-8: combined `RateLimit` header
29+
legacyHeaders: false, // Disable the `X-RateLimit-*` headers.
30+
ipv6Subnet: 56, // Set to 60 or 64 to be less aggressive, or 52 or 48 to be more aggressive
31+
// store: ... , // Redis, Memcached, etc. See below.
32+
})
33+
34+
//Rate limit these routes to ensure users don't overwhelm the codeforces API
35+
router.use("/start-verification", limiter)
36+
router.use("/check-verification", limiter)
37+
2438
router.post(
2539
"/start-verification",
2640
validateStartVerificationRequest,

backend/src/workers/codeforcesWorker.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { Worker } from "bullmq";
22
import { connection } from "../queues/codeforcesQueue";
33
import { RatingChange, Submission } from "../types/codeforces";
4-
import { client, db } from "../drizzle/db";
4+
import { db } from "../drizzle/db";
55
import { problems } from "../drizzle/schema";
66
import {
77
addCodeforcesProblems,
@@ -31,7 +31,6 @@ async function refreshProblemKeysCache() {
3131
}
3232

3333
async function init() {
34-
await client.connect();
3534
await refreshProblemKeysCache();
3635

3736
console.log("Connected to postgres");

frontend/src/components/CFHandleModal.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,14 +60,18 @@ export default function CFHandleModal({ onSuccess }: CFHandleModalProps) {
6060
body: JSON.stringify({ handle, contestId, index }),
6161
}
6262
);
63-
const data = await res.json();
6463
if (res.ok) {
64+
const data = await res.json();
6565
setVerifiedUser(data.data);
6666
setSuccess(true);
6767
setTimeout(() => {
6868
onSuccess(data.data);
6969
}, 5000);
70-
} else {
70+
} else if (res.status === 429) {
71+
setError("Please wait 5 secs before trying again.")
72+
}
73+
else {
74+
const data = await res.json();
7175
setError(data.message);
7276
}
7377
} catch (err) {

0 commit comments

Comments
 (0)