Solve Problems Β· Fight in 1v1 Battles Β· Join Contests Β· Climb the Leaderboard
CodeForge is a competitive programming platform built from scratch β similar to LeetCode and Codeforces. Users can solve algorithmic problems, fight real-time 1v1 coding battles, participate in timed contests, and track their ELO rating progress over time.
The project is fully deployed and live:
- Frontend: Vercel β code-forge-iota-ten.vercel.app
- Backend: Render (Dockerized) β codeforge-backend-10w2.onrender.com
β οΈ Backend is on Render's free tier β it may take ~20β30s to wake up on first request.
- Browse problems filtered by difficulty: Easy, Medium, Hard
- Built-in C++ code editor with line numbers and tab support
- Run Code β test against visible sample cases instantly (no DB save)
- Submit β runs code against all hidden test cases via a background queue
- Verdicts:
Accepted,Wrong Answer,Compile Error,Runtime Error,Time Limit Exceeded - Full submission history with runtime and memory stats per submission
- Enter a matchmaking lobby β get auto-paired with another online user
- Both players receive the same problem at the same time
- First to get
Acceptedon all test cases wins the battle - Live opponent progress updates via WebSockets (tests passed, verdict, etc.)
- Battle ELO rating updates automatically after every match
Player 1 ββ[ Matchmaking ]βββΊ CodeForge Server βββ[ Matchmaking ]ββ Player 2
β
Creates 1v1 Battle Room
β
Both solve simultaneously βββΊ First to AC wins!
- Join scheduled timed programming contests
- Multiple problems per contest
- Live leaderboard β ICPC-style scoring (problems solved + time penalty)
- Rating gets automatically calculated by a background cron job once the contest ends
- Two separate ratings: Contest Rating and Battle Rating
- Both start at 1200 and change based on performance
- Full rating history stored for each user β tracked over time
- JWT-based login/logout with Redis token blacklisting (tokens are immediately invalidated on logout)
- Passwords hashed using bcryptjs
- API rate limiting to prevent abuse
- Code execution with timeout limits and memory caps
- Create, edit, and delete problems
- Add/remove test cases (visible or hidden)
- Create and schedule contests
ββββββββββββββββββββββββββββββββββββ
β Browser (React + Vite) β
β Hosted on Vercel (Global CDN) β
ββββββββββββββββ¬ββββββββββββββββββββ
β HTTP REST + WebSocket
ββββββββββββββββΌββββββββββββββββββββββββββββββββββββββββββββ
β Express.js Backend (Node.js 20) β
β Running in Docker on Render Cloud β
β β
β βββββββββββββββ ββββββββββββββββ βββββββββββββββββ β
β β REST APIs β β Socket.io β β BullMQ β β
β β (Auth, Sub, β β (Battles, β β (Job Queue β β
β β Contest..) β β Matchmaking)β β for judging) β β
β ββββββββ¬βββββββ ββββββββββββββββ βββββββββ¬ββββββββ β
β β β β
β ββββββββΌββββββββββββββββββββββββββββββββββββββΌββββββββ β
β β Judge Service β β
β β 1. Wrap user code in a complete C++ program β β
β β 2. Compile with g++ β β
β β 3. Run against each test case, capture output β β
β β 4. Compare output vs expected β give verdict β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
ββββββββββββββββββββββββ¬βββββββββββββββββββββ¬βββββββββββββββ
β β
βββββββββββββββΌβββββββ βββββββββββΌβββββββββββββββ
β MongoDB Atlas β β Upstash Redis β
β - Users β β - BullMQ job queue β
β - Problems β β - Blacklisted tokens β
β - Submissions β β - Rate limit counters β
β - Contests β ββββββββββββββββββββββββββ
β - Battles β
ββββββββββββββββββββββ
This is the core of the platform. When a user submits C++ code, it can't just be run directly β it needs a proper main() with input reading and output printing. Here's how it works:
Step 1 β Code Wrapping
User writes a Solution class β system wraps it in a complete C++ program
with input parsing (int, vector<int>, string, etc.) and a main() function
Step 2 β Compilation
g++ -O1 main.cpp -o main
If compilation fails β returns Compile Error with the g++ output
Step 3 β Test Case Execution
For each test case: ./main < input.txt
5 second timeout per test case
Captures stdout, stderr, and execution time
Step 4 β Verdict
Compare actual output vs expected output (after trimming whitespace)
β Accepted / Wrong Answer / TLE / Runtime Error
Two execution modes:
- Docker mode (local/self-hosted): Runs in isolated containers with
--network none, memory limit 128MB, CPU limit 0.5 cores - Direct mode (Render cloud): Runs
g++natively inside a temp directory, cleaned up automatically
erDiagram
User ||--o{ Submission : submits
User ||--o{ Battle : competes_in
User ||--o{ Contest : registers_for
Problem ||--o{ TestCase : contains
Problem ||--o{ Submission : evaluated_on
Contest ||--o{ Problem : includes
Battle ||--|| Problem : plays_on
Battle ||--o| User : winner
| Field | Type | Description |
|---|---|---|
_id |
ObjectId | Unique User ID |
name |
String | Display name |
email |
String | Unique email address |
password |
String | Hashed password (bcryptjs) |
role |
String | 'user' or 'admin' |
contestRating |
Number | Contest rating (starts at 1200) |
battleRating |
Number | 1v1 Battle rating (starts at 1200) |
ratingHistory |
Array | Audit log of rating changes with timestamps |
| Field | Type | Description |
|---|---|---|
_id |
ObjectId | Unique Problem ID |
problemNumber |
Number | Unique incremental problem number |
title |
String | Problem title |
description |
String | Problem statement, input/output formats & constraints |
difficulty |
String | 'Easy' | 'Medium' | 'Hard' |
functionName |
String | Target function name inside Solution class |
starterCode |
String | Boilerplate code template shown in editor |
judgeConfig |
Object | { returnType: String, parameters: [{ name, type }] } |
| Field | Type | Description |
|---|---|---|
_id |
ObjectId | Unique Test Case ID |
problem |
ObjectId | Ref problems
|
input |
String | Raw input fed via stdin
|
expectedOutput |
String | Expected output checked against stdout
|
isHidden |
Boolean |
false = visible sample test case, true = hidden judge case |
| Field | Type | Description |
|---|---|---|
_id |
ObjectId | Unique Submission ID |
user |
ObjectId | Ref users
|
problem |
ObjectId | Ref problems
|
code |
String | Submitted C++ source code |
language |
String | 'cpp' |
status |
String |
'pending' | 'accepted' | 'wrong_answer' | 'compile_error' | 'runtime_error' | 'time_limit_exceeded'
|
passedTests |
Number | Number of test cases passed |
totalTests |
Number | Total test cases evaluated |
runtime |
Number | Execution time in milliseconds |
memory |
Number | Peak memory usage in KB |
error |
String | Compiler or runtime error message (if any) |
battle |
ObjectId | Ref battles (null if practice submission) |
| Field | Type | Description |
|---|---|---|
_id |
ObjectId | Unique Contest ID |
title |
String | Contest title & theme |
description |
String | Rules and guidelines |
startTime |
Date | Contest start timestamp |
endTime |
Date | Contest end timestamp |
problems |
[ObjectId] | Array of Problem references |
participants |
[ObjectId] | Array of registered User references |
finalLeaderboard |
Array | Final ranks, scores, and penalty times |
| Field | Type | Description |
|---|---|---|
_id |
ObjectId | Unique Battle ID |
player1 |
Object | { user: Ref, rating: Number, currentCode: String } |
player2 |
Object | { user: Ref, rating: Number, currentCode: String } |
problem |
ObjectId | Ref problems assigned to both players |
winner |
ObjectId | Ref users (null if tied or in progress) |
status |
String |
'waiting' | 'in_progress' | 'finished'
|
duration |
Number | Match duration limit (default: 15 minutes) |
endReason |
String |
'accepted' | 'timeout' | 'surrender'
|
All endpoints are prefixed with /api/v1.
| Method | Endpoint | Auth | Description |
|---|---|---|---|
POST |
/register |
Public | Create a new account |
POST |
/login |
Public | Login and get JWT token |
POST |
/logout |
User | Invalidate current token |
| Method | Endpoint | Auth | Description |
|---|---|---|---|
GET |
/ |
Public | Get all problems (filter, paginate, search) |
GET |
/:id |
Public | Get a single problem |
POST |
/ |
Admin | Create a problem |
PUT |
/:id |
Admin | Update a problem |
DELETE |
/:id |
Admin | Delete a problem |
| Method | Endpoint | Auth | Description |
|---|---|---|---|
POST |
/run |
User | Run code on sample test cases (no DB save) |
POST |
/ |
User | Submit code for full judging (queued async) |
GET |
/:id |
User | Check submission result |
GET |
/my |
User | My submission history |
GET |
/problem/:id |
User | My submissions for a specific problem |
GET |
/my/stats |
User | My acceptance rate and stats |
| Method | Endpoint | Auth | Description |
|---|---|---|---|
POST |
/matchmake |
User | Join matchmaking pool |
GET |
/:id |
User | Get current battle info |
| Method | Endpoint | Auth | Description |
|---|---|---|---|
GET |
/ |
Public | Get all contests |
GET |
/:id |
Public | Get contest details |
POST |
/ |
Admin | Create a contest |
POST |
/:id/register |
User | Register for a contest |
GET |
/:id/leaderboard |
Public | Live leaderboard |
| Method | Endpoint | Auth | Description |
|---|---|---|---|
GET |
/profile |
User | My profile and rating history |
GET |
/leaderboard |
Public | Global rankings |
Socket connects with JWT: io(SERVER_URL, { query: { token } })
Events you send (Client β Server):
| Event | Payload | What it does |
|---|---|---|
join_lobby |
β | Enter matchmaking |
leave_lobby |
β | Cancel matchmaking |
join_battle |
{ battleId } |
Join battle room |
battle_code_update |
{ battleId, code } |
Sync code state |
surrender_battle |
{ battleId } |
Forfeit the match |
Events you receive (Server β Client):
| Event | Payload | What it means |
|---|---|---|
match_found |
{ battleId, problem, opponent } |
Opponent found, battle starting |
opponent_status |
{ testsPassed, totalTests, status } |
Opponent progress update |
battle_submission_result |
{ status, passedTests } |
Your submission judged |
battle_ended |
{ winner, reason, ratingChanges } |
Match is over |
CodeForge/
βββ backend/
β βββ Dockerfile # Node.js 20 + g++ for Render deployment
β βββ server.js # App entry β HTTP server + Socket.io setup
β βββ src/
β βββ app.js # Express app, middleware, routes
β βββ config/
β β βββ database.js # MongoDB connection
β β βββ redis.js # Redis client
β β βββ queue.config.js # BullMQ Redis connection (with TLS)
β βββ controllers/ # Request handlers
β βββ middlewares/
β β βββ auth.middleware.js # JWT verification + blacklist check
β β βββ rateLimiter.middleware.js
β βββ models/ # Mongoose schemas
β βββ queues/
β β βββ submission.queue.js # BullMQ queue setup
β βββ repositories/ # DB query functions
β βββ routes/ # API route definitions
β βββ services/
β β βββ judge.service.js # Core judging logic
β β βββ matchmaking.service.js
β β βββ battleResult.service.js
β β βββ rating.service.js # ELO calculations
β β βββ contestRatingScheduler.service.js # Cron job for auto-rating
β β βββ executor/
β β βββ code-generator.js # Wraps user code into a full C++ program
β β βββ cpp.executor.js # Compiles and runs code (Docker or direct)
β β βββ executor.factory.js
β βββ sockets/
β β βββ socket.js # Socket.io connection setup
β β βββ socketManager.js # Global io() instance
β β βββ battle.socket.js # Battle room event handlers
β βββ utils/
β β βββ cache.js # Redis helper functions
β βββ workers/
β βββ submission.worker.js # BullMQ worker β processes judge jobs
β
βββ frontend/
β βββ vercel.json # SPA routing fix for Vercel
β βββ src/
β βββ App.jsx # Routes
β βββ components/ # Navbar, Cards, Modals
β βββ context/ # Auth context (global user state)
β βββ pages/
β β βββ ProblemList.jsx # Problem browser
β β βββ ProblemDetail.jsx # Editor + Run/Submit + Submission history
β β βββ BattleLobby.jsx # Matchmaking UI
β β βββ BattleArena.jsx # Live 1v1 battle screen
β β βββ Contests.jsx # Contest list
β β βββ ContestDetail.jsx # Contest problems + leaderboard
β β βββ Profile.jsx # User stats + rating chart
β β βββ Submissions.jsx # All submissions view
β β βββ CreateProblem.jsx # Admin: add problem
β β βββ CreateContest.jsx # Admin: add contest
β βββ services/
β βββ api.js # Axios instance with auth interceptor
β βββ socket.js # Shared socket.io client
βββ README.md
- Node.js v18+
- MongoDB (local or Atlas URI)
- Redis (local or Upstash URL)
g++compiler installed (sudo apt install g++on Linux / MinGW on Windows)
git clone https://github.com/Lovejindal1/CodeForge.git
cd CodeForgecd backend
npm install
cp .env.example .env # Fill in your values
npm startBackend runs at http://localhost:3000
cd frontend
npm install
npm run devFrontend runs at http://localhost:5173
| Variable | Required | Description |
|---|---|---|
PORT |
No | Port to run on (default: 3000) |
NODE_ENV |
Yes | development or production |
MONGO_URI |
Yes | MongoDB connection string |
REDIS_URL |
Yes | Redis URL (use rediss:// for TLS) |
JWT_SECRET |
Yes | Secret key for signing tokens |
JWT_EXPIRES_IN |
Yes | Token expiry e.g. 7d |
FRONTEND_URL |
Yes | Allowed CORS origin |
| Variable | Required | Description |
|---|---|---|
VITE_API_URL |
Yes | Backend REST API base URL |
VITE_SOCKET_URL |
Yes | Backend WebSocket URL |
CodeForge is built on a modern, decoupled cloud architecture using 5 free-tier services:
[Vercel] βββΊ Frontend (React + Vite SPA)
[Render] βββΊ Backend API & Judge (Docker with g++)
[MongoDB Atlas] βββΊ Primary Database (M0 Cloud Cluster)
[Upstash Redis] βββΊ Task Queue & Cache (Serverless Redis TLS)
[UptimeRobot] βββΊ 24/7 Keep-Alive Heartbeat (/health pinger)
- Sign up at MongoDB Atlas and create a free M0 Cluster.
-
Network Access: Add IP
0.0.0.0/0(Allow access from anywhere, required for cloud hosting). - Database Access: Create a database user with Read/Write privileges.
-
Connection String: Click Connect
$\rightarrow$ Drivers$\rightarrow$ Copy the URI:mongodb+srv://<username>:<password>@cluster0.xxxxx.mongodb.net/codeforge?retryWrites=true&w=majority
- Sign up at Upstash Redis and create a free database.
- Select the cloud region closest to your Render server region.
- In the database dashboard, copy the
rediss://connection string (TLS enabled). - Usage in CodeForge:
- BullMQ: Powers the asynchronous submission judging worker queue.
- Token Blacklist: Instantly invalidates JWTs on logout.
- Rate Limiting: Throttles brute-force API requests.
- Sign up at Render and click New +
$\rightarrow$ Web Service. - Connect your GitHub repository:
https://github.com/Lovejindal1/CodeForge. - Configure the service:
-
Name:
codeforge-backend -
Runtime / Environment:
Docker -
Root Directory:
backend -
Dockerfile Path:
./Dockerfile -
Instance Type:
Free
-
Name:
- Add Environment Variables:
NODE_ENV=production PORT=3000 MONGO_URI=mongodb+srv://<username>:<password>@cluster0.xxxxx.mongodb.net/codeforge REDIS_URL=rediss://default:xxxxxx@xxxxxx.upstash.io:6379 JWT_SECRET=your_super_secret_jwt_key JWT_EXPIRES_IN=7d FRONTEND_URL=https://code-forge-iota-ten.vercel.app
- Click Create Web Service. Render builds the Docker image (
node:20-bullseye-slimwithg++) and starts the server.
- Sign up at Vercel and click Add New
$\rightarrow$ Project. - Import the
CodeForgerepository. - Configure project settings:
-
Root Directory:
frontend -
Framework Preset:
Vite
-
Root Directory:
- Add Environment Variables:
VITE_API_URL=https://<your-render-backend-url>/api/v1 VITE_SOCKET_URL=https://<your-render-backend-url>
- Click Deploy.
Note: Single Page Application (SPA) routing is pre-configured via
frontend/vercel.jsonto prevent 404 errors on page refreshes.
Render's free tier automatically spins down web services after 15 minutes of inactivity, resulting in a ~30β40s cold start when a user visits. To prevent this:
- Sign up for free at UptimeRobot.
- Click Add New Monitor:
- Monitor Type:
HTTP(s) - Friendly Name:
CodeForge Backend Keep-Alive - URL (or IP):
https://<your-render-backend-url>/health - Monitoring Interval:
Every 5 minutes
- Monitor Type:
- Save monitor. UptimeRobot now pings the
/healthendpoint every 5 minutes, keeping the backend warm 24/7 with zero cold start delays.
Love Jindal
- GitHub: @Lovejindal1
- Live Project: code-forge-iota-ten.vercel.app
β If you like this project, please give it a star!
Made with β€οΈ by Love Jindal