Skip to content

Commit 92fc698

Browse files
authored
Merge pull request #69 from crux-bphc/feat/rate-limits
Added Global Rate Limits
2 parents a5603d4 + ec938ac commit 92fc698

File tree

7 files changed

+66
-24
lines changed

7 files changed

+66
-24
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: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ import {
2222
fetchSubmissions,
2323
fetchRatingChanges,
2424
} from "./controllers/codeforces";
25+
import { rateLimit } from 'express-rate-limit';
26+
import "./workers/codeforcesWorker";
27+
28+
2529

2630
dotenv.config();
2731
const app = express();
@@ -118,6 +122,17 @@ passport.deserializeUser(async (userId: string, done) => {
118122
}
119123
});
120124

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+
121136
app.get("/", (_req, res) => {
122137
res.send("Backend API is working!");
123138
});

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");

docker-compose.yml

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -103,26 +103,6 @@ services:
103103
profiles:
104104
- "dev"
105105

106-
cf-worker:
107-
build:
108-
context: ./
109-
dockerfile: ./backend/docker/dev/Dockerfile
110-
env_file:
111-
- .env
112-
environment:
113-
DATABASE_URL: postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres/${POSTGRES_DB}
114-
REDIS_URL: redis://redis:6379
115-
volumes:
116-
- ./backend/src:/usr/local/app/backend/src
117-
- ./backend/tsconfig.json:/usr/local/app/backend/tsconfig.json
118-
command: npx ts-node src/workers/codeforcesWorker.ts
119-
depends_on:
120-
- postgres
121-
- redis
122-
profiles:
123-
- "dev"
124-
- "prod"
125-
126106
volumes:
127107
pgdata:
128108
redisdata:

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)