Skip to content

Repository files navigation

RankForge

A full-stack competitive programming platform with temporal leaderboards, real-time code judging, and deep analytics.

License: MIT Next.js NestJS PRs welcome

Live Demo

Backend is on Render free tier — first request may take ~30s to wake up.

Highlights

  • 95+ problems across 4 difficulty tiers (Easy, Medium, Hard, Expert) with real test cases
  • Online judge — compile and run code in 10 languages via Piston API, with per-test-case verdicts (AC/WA/TLE/RE/CE)
  • LeetCode-style editor — Run code against custom input, see compilation errors and output instantly, then submit to judge all test cases
  • Temporal leaderboard — scrub through time to see how rankings evolved, powered by segment trees
  • Contest replay mode — watch a contest unfold like a movie with animated rank graphs
  • Codeforces-style rating system — Elo-based algorithm with per-contest rating changes
  • 10 language support — C, C++, Python, Java, JavaScript, TypeScript, Go, Rust, Kotlin, Ruby
  • Plagiarism detection — token-based n-gram similarity analysis across contest submissions
  • LeetCode-style profiles — solve donut, submission heatmap, language breakdown, skill radar
  • Real-time updates — live verdict delivery, leaderboard changes, and timer sync via WebSockets
  • Monochrome brutalist UI — editorial black-and-white design with a light/dark toggle

Tech Stack

Layer Technology
Frontend Next.js 16, TypeScript, Tailwind CSS v4, shadcn/ui, Monaco Editor, Recharts
Backend NestJS 11, TypeScript, Prisma ORM, REST API, WebSockets (ws)
Database PostgreSQL (Neon)
Queue Redis (Upstash), BullMQ
Code Execution Piston API (10 languages, free, open-source)
Auth JWT (access + refresh tokens), bcrypt, Passport.js
Monorepo Turborepo, pnpm workspaces
Deployment Vercel (frontend), Render (backend)

Architecture

┌──────────────┐     ┌──────────────┐     ┌──────────────┐
│   Next.js    │────>│   NestJS     │────>│  PostgreSQL   │
│   Frontend   │     │   API        │     │  (Prisma)     │
│  (Vercel)    │<────│  (Render)    │     │  (Neon)       │
└──────────────┘  WS └──────┬───────┘     └──────────────┘
                            │
                   ┌────────┴────────┐
                   │  Redis + BullMQ │
                   │   (Upstash)     │
                   └────────┬────────┘
                            │
              ┌─────────────┴─────────────┐
              │  Judge Worker (BullMQ)     │
              │  Code execution via Piston │
              │  10 languages supported    │
              └───────────────────────────┘

How Judging Works

1. User writes code in Monaco Editor
2. "Run" → executes against custom input via Piston API, shows output instantly
3. "Submit" → enqueues to BullMQ judge queue
4. Judge worker runs code against ALL test cases via Piston API
5. Per-test-case verdict (AC/WA/TLE/RE/CE) saved to DB
6. Real-time verdict delivered via WebSocket
7. Leaderboard updates if contest submission

Supported verdicts: Accepted, Wrong Answer, Time Limit Exceeded, Memory Limit Exceeded, Runtime Error, Compilation Error

Project Structure

rankforge/
├── apps/
│   ├── web/                  # Next.js frontend (20 pages)
│   ├── api/                  # NestJS backend (15 modules, ~50 endpoints)
│   └── judge-worker/         # Standalone judge service
├── packages/
│   ├── shared/               # Types, constants, Zod validation schemas
│   └── segment-tree/         # Temporal leaderboard data structure
├── docker-compose.yml        # Local PostgreSQL + Redis
├── render.yaml               # Render deployment blueprint
└── turbo.json                # Monorepo task runner

Features

Code Editor & Judge

  • Monaco Editor — VS Code-powered editor with syntax highlighting for all 10 languages
  • Run — test your code against custom input, see output or errors instantly
  • Submit — judge against all test cases, see per-test verdicts in real-time
  • Compilation errors — shown inline with error messages from the compiler
  • Runtime errors — stack traces and error output displayed
  • Custom test run — edit input, run, compare output with expected

Core Platform

  • Auth — Register, login, logout, JWT refresh, role-based access (User, Problem Setter, Organizer, Admin)
  • Problems — CRUD with Markdown statements, difficulty tags, sample/hidden test cases
  • Contests — Full lifecycle (Draft -> Published -> Registration -> Live -> Frozen -> Ended -> Results), ICPC-style scoring
  • Contest creation — organizers can create contests, add problems, manage lifecycle

Differentiating Features

  • Temporal Leaderboard — Segment tree queries over contest timeline: standings at any minute T, score progressions, peak activity intervals
  • Contest Replay — Animated playback of the entire contest showing rank movements over time
  • Rating System — Codeforces-style algorithm: expected rank vs actual rank, performance rating, inflation control
  • Post-Contest Analytics — Per-problem breakdown, solve time vs average comparison, rating impact
  • Growth Analytics — Topic mastery radar chart, 365-day submission heatmap, rating over time, solve streaks

Profile & Social

  • LeetCode-style Profile — Solved donut chart, difficulty breakdown, submission heatmap, language stats, skill tags, badges
  • Achievements — 10 badges (First Blood, Centurion, Expert, Streak 30, etc.)
  • Discussions — Threaded comments on problems
  • Editorials — Post-solve explanations by problem setters

Admin & Security

  • Admin Dashboard — System stats, user management, verdict distribution charts
  • Plagiarism Detection — Token-based code similarity (trigram Jaccard), flags pairs >80% similar
  • Virtual Contests — Take past contests with personal timer
  • Rejudge — Re-evaluate submissions when test cases change

Pages

Route Description
/ Landing page
/login, /register Authentication
/dashboard Personal dashboard with stats, streak, upcoming contests
/problems Problem list with difficulty/tag filters
/problems/[slug] Split-pane editor: problem + Monaco + run/submit + test output
/submissions All submissions with verdict badges
/submissions/[id] Submission detail with per-test results
/contests Contest list with status badges
/contests/create Contest creation form
/contests/[slug] Contest room: timer, problems, registration, announcements
/contests/[slug]/leaderboard ICPC-style live leaderboard with first blood
/contests/[slug]/leaderboard/temporal Time-scrubber leaderboard (segment tree)
/contests/[slug]/leaderboard/replay Animated contest replay
/contests/[slug]/ratings Post-contest rating changes
/contests/[slug]/analytics Personal performance breakdown
/users/[username] LeetCode-style public profile
/analytics Personal growth: topic radar, heatmap, rating graph
/admin Admin dashboard with system stats

Design

Monochrome brutalist / editorial UI — oversized display type, hairline rules, sharp corners, and big negative space. Fully light / dark themed via a single toggle (top-right corner); the entire app flips through CSS variables.

  • Light brutalist showcase pages (landing, login, register) — black-on-white, magazine-cover typography.
  • Dark editorial application shell — numbered mono sidebar, glass-like surfaces.
  • No color: status and verdicts read by fill + glyph (AC = solid chip, failures outlined), so the design stays strictly grayscale.

Theming is driven by semantic CSS variables (--c-bg, --c-fg, --c-surface, --c-border, …) defined for both themes in apps/web/src/app/globals.css, so every component flips automatically.

Getting Started

Prerequisites

  • Node.js 22+
  • pnpm (npm install -g pnpm)
  • Docker (for local PostgreSQL + Redis)

Local Development

git clone https://github.com/dobbydobap/RankForge.git
cd RankForge

pnpm install

# Start PostgreSQL + Redis
docker compose up -d

# Copy environment variables
cp .env.example .env

# Push database schema and seed
pnpm db:push
pnpm db:seed

# Start development servers
pnpm dev

Open:

Demo Accounts

Available after seeding (pnpm db:seed locally, or POST /api/seed?key=...&force=true in production):

User Email Password Role Rating
demo demo@rankforge.dev Demo1234 User 1240 — pre-loaded with solved problems, submissions & a daily streak
admin admin@rankforge.dev Admin123 Admin 2100
alice alice@rankforge.dev Password1 User 1650
bob bob@rankforge.dev Password1 User 1420
charlie charlie@rankforge.dev Password1 User 1850
diana diana@rankforge.dev Password1 User 1300
eve eve@rankforge.dev Password1 User 1550

Note: On the live demo, you can register a new account to try the platform. Demo accounts are available if the database has been seeded.

Deployment

Deployed on the free tier:

Service Platform Cost
Frontend Vercel Free
Backend Render Free
PostgreSQL Neon Free (0.5 GB)
Redis Upstash Free (10K cmd/day)

Deploy Your Own

  1. Neon — Create project at neon.tech, copy connection string
  2. Upstash — Create Redis at upstash.com, copy REDIS_URL (use rediss://)
  3. Render — Connect GitHub repo, set:
    • Build: chmod +x apps/api/render-build.sh && bash apps/api/render-build.sh
    • Start: cd apps/api && node dist/main.js
    • Env: DATABASE_URL, REDIS_URL, JWT_ACCESS_SECRET, JWT_REFRESH_SECRET, CORS_ORIGIN, NODE_ENV=production
  4. Vercel — Import repo, root directory apps/web, set:
    • NEXT_PUBLIC_API_URL = Render URL + /api
    • NEXT_PUBLIC_WS_URL = Render URL with wss://
  5. Seed production DB: POST /api/seed?key=YOUR_JWT_ACCESS_SECRET&force=true

Key Data Structures

Segment Tree (Temporal Leaderboard)

The @rankforge/segment-tree package provides O(log n) queries over the contest timeline:

const tree = new ContestSegmentTree(120); // 120-minute contest

tree.update(10, 100, true);  // +100 points at minute 10
tree.update(30, 200, true);  // +200 points at minute 30

const data = tree.query(5, 35);
// { totalScore: 300, submissionCount: 2, acceptedCount: 2, maxScoreGain: 200 }

Powers: leaderboard at any time T, score progression graphs, peak activity detection, and contest replay.

Scripts

pnpm dev              # Start all services
pnpm build            # Build all packages
pnpm test             # Run all tests
pnpm db:push          # Push Prisma schema
pnpm db:seed          # Seed database (95+ problems, 6 users, demo contest)
pnpm db:studio        # Open Prisma Studio
pnpm dev:web          # Frontend only
pnpm dev:api          # Backend only

Testing

pnpm test:segment-tree    # Segment tree unit tests (9 tests)
pnpm test:shared          # Zod validation tests (8 tests)

Load Testing

The submission pipeline was load-tested to 500 concurrent users. The full harness lives in loadtest/ and is reproducible end to end.

Test setup

  • Host: Windows 11, Intel i7-1355U (10C / 12T), 16 GB RAM. Postgres 16 and Redis 7 in Docker (WSL2); API run locally with node dist/main.js. The k6 load generator shares the host with the system under test — an acknowledged limitation; runs where total host CPU saturated were discarded.
  • Tooling: k6 v2.2.0, Node v24.18.0.
  • Code execution is mocked (JUDGE_EXECUTOR=mock) at a fixed 200 ms/test, so the numbers measure RankForge's own orchestration — API, BullMQ queue, Postgres, WebSocket — and not the third-party Wandbox compile service. The 200 ms figure is calibrated against the median of 31 real Wandbox test-case executions (2657 ms) measured on this stack; the real executor is never load-tested (it is a free public service).
  • Identity & rate limits: 500 pre-seeded users, each with a pre-signed JWT and a unique X-Forwarded-For, so per-client rate limits (3 submissions / 10 s, 10 req/s) behave as they would in production rather than collapsing onto one IP.
  • Scenarios: S1 — open-loop ramping-arrival-rate, 25 → 150 submissions/s (throughput / breaking point). S2 — closed-loop ramping-vus, 0 → 500 VUs over 2 min then held 5 min, each user looping submit → poll verdict → think 5–10 s (the "500 concurrent users" headline). Time-to-verdict is read from server-side Submission.createdAt → judgedAt; the first 60 s of each run is excluded as warmup.

The bottleneck. The BullMQ judge worker ran at the default concurrency of 1, so verdicts drained at a hard ceiling of ~1.4/s regardless of load, while the Prisma connection pool (default 21) exhausted under the enqueue burst (P2024 timeouts → HTTP 5xx). Evidence: queue depth grew linearly while active stayed pinned at 1 and API CPU sat idle. Fix: configurable worker concurrency (JUDGE_CONCURRENCY=32) plus connection_limit=40 on the database URL.

S2 — 500 concurrent users (2 min ramp + 5 min hold)

Metric Before (concurrency 1) After (concurrency 32, pool 40)
Submissions judged within window 4.5% 100%
Verdict throughput (steady state) 1.1 / s 23.8 / s
Time-to-verdict p50 / p95 / p99 307 s / 352 s / 355 s 6.3 s / 47 s / 61 s
Users timed out (gave up after 120 s) 1155 0
HTTP error rate (5xx) 0% 0%

S1 — open-loop, arrival rate ramped to 150 submissions/s

Metric Before (concurrency 1) After (concurrency 32, pool 40)
Verdict throughput (mean / peak) 1.4 / 2 per s 34 / 63 per s
Submissions judged within window 0% 58–79%
First failure mode under load pool exhaustion → 5xx rate-limit 429s (load shed cleanly)

How to reproduce

winget install k6 && docker compose up -d
node loadtest/seed-users.mjs && node loadtest/gen-tokens.mjs   # 500 users + tokens
bash loadtest/run-one.sh s2 1  s2-before        # baseline (concurrency 1)
bash loadtest/run-one.sh s2 32 s2-after 40      # fixed (concurrency 32, pool 40)

Honest caveats. Numbers are single-host and shared-generator, so absolute latencies are conservative; the before/after comparison is what matters and both sides ran identically. Reported figures are the mean of 2 clean runs per configuration (S2 "after" is 1 clean run corroborated by a second); runs interrupted by the laptop's Modern Standby were detected via the Windows event log and discarded. S1 enqueue latency varied run-to-run with host contention; the verdict-throughput ceiling and completion rate — the actual bottleneck signature — were consistent across every run.

Contributing

RankForge is open source and contributions are welcome. See CONTRIBUTING.md for setup and guidelines, and check the Issues tab — issues labeled good first issue and help wanted are a great place to start.

License

MIT © 2026 dobbydobap

About

Competitive programming platform - 10-language async code judge, real-time WebSocket verdicts, Elo ratings, temporal segment-tree leaderboards

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages