Skip to content
ayush03devPublic

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

46 Commits

Folders and files

Repository files navigation

CodeBox

CodeBox is a full-stack online coding judge. Users can browse programming problems, write solutions in Python, Java, or C++, run code against sample or custom inputs, submit solutions against hidden test cases, and review their submission history. Administrators can create problems, define function signatures, and manage test cases from the web UI.

The project is a microservice monorepo built with Spring Boot, React, PostgreSQL, Kafka, and Docker-based execution sandboxes.

Features

  • Email/password signup and login with role-based JWT authentication
  • Public problem catalog with generated starter code
  • Admin-only problem and test-case management
  • Monaco-based browser code editor
  • Python 3.12, Java 21, and C++17 judging
  • Synchronous runs against sample or custom inputs
  • Asynchronous submissions against all test cases
  • Per-user submission history and verdict polling
  • Docker Compose development environment
  • Kubernetes manifests and a Jenkins-to-ArgoCD GitOps deployment flow

Tech Stack

Layer Technologies
Frontend React 19, Vite, React Router, Zustand, Axios, Monaco Editor, Tailwind CSS
Backend Java 21, Spring Boot 3.5, Spring Web, Spring Data JPA
Data and messaging PostgreSQL 16, Kafka 3.7
Authentication JWT (HS256), BCrypt password hashing
Judge sandbox Docker containers with network, memory, CPU, and time limits
Delivery Docker Compose, Kubernetes/Kustomize, Jenkins, ArgoCD

System Design

High-Level Architecture

flowchart LR
    User[Browser] --> Frontend[React frontend<br/>Nginx or Vite]
    Frontend -->|/api/auth/*| Auth[Auth Service :8081]
    Frontend -->|/api/problems/*<br/>/api/admin/*| Problem[Problem Service :8085]
    Frontend -->|/api/submissions/*| Submission[Submission Service :8082]

    Auth --> DB[(PostgreSQL)]
    Problem --> DB
    Submission --> DB

    Submission -->|HTTP: Run| Judge[Judge Service :8084]
    Submission -->|submission.created| Kafka[(Kafka)]
    Kafka --> Judge
    Judge -->|Internal HTTP:<br/>judge bundle| Problem
    Judge -->|submission.judged| Kafka
    Kafka --> Submission

    Judge --> Docker[Isolated runtime containers<br/>Python / Java / C++]
Loading

The frontend is the public entry point. Its Nginx configuration, or the Vite development proxy, routes /api requests to the appropriate backend service. Backend services communicate over HTTP for immediate requests and Kafka for asynchronous submission processing.

Although the three stateful services currently connect to one PostgreSQL database, each service owns its own tables:

Service Responsibility State
auth-service Signup, login, admin seeding, JWT issuance, and /auth/me users
problem-service Problems, function signatures, starter code, sample cases, hidden cases, and admin APIs problems, test_cases
submission-service User-facing run/submit APIs, submission lifecycle, history, and verdict persistence submissions
judge-service Harness generation, sandboxed code execution, output parsing, and grading Stateless
frontend-service React SPA and path-based API reverse proxy Stateless

Authentication and Authorization

  1. auth-service verifies credentials and issues an HS256 access token containing the user ID, email, and role.
  2. The frontend stores the token in local storage and sends it as Authorization: Bearer <token>.
  3. auth-service, problem-service, and submission-service independently validate the token using the same secret, issuer, and audience.
  4. Public problem reads expose sample cases only. Admin problem APIs require the ADMIN role.
  5. The judge fetches hidden cases from problem-service through /internal/problems/{id}/judge-bundle, guarded by a separate internal service token.

Run Flow

The Run action is synchronous, is not persisted, and evaluates either user-provided custom inputs or the problem's public sample cases.

sequenceDiagram
    participant UI as Frontend
    participant S as Submission Service
    participant P as Problem Service
    participant J as Judge Service
    participant D as Docker Sandbox

    UI->>S: POST /submissions/run
    S->>P: GET /problems/{id}
    P-->>S: Signature, starter metadata, sample cases
    S->>J: POST /run
    J->>D: Compile/run generated harness + user code
    D-->>J: stdout, stderr, exit status
    J-->>S: Verdict and case outputs
    S-->>UI: Immediate run result
Loading

Submit Flow

The Submit action is asynchronous. It persists a queued submission, publishes an event, and returns 202 Accepted. The frontend polls the submission API until a final verdict is available.

sequenceDiagram
    participant UI as Frontend
    participant S as Submission Service
    participant K as Kafka
    participant J as Judge Service
    participant P as Problem Service
    participant D as Docker Sandbox

    UI->>S: POST /submissions
    S->>S: Persist status = QUEUED
    S->>K: codebox.submission.created
    S-->>UI: 202 Accepted + submission ID
    K->>J: Consume submission
    J->>P: GET internal judge bundle
    P-->>J: Signature + all test cases
    J->>D: Compile/run in isolated container
    D-->>J: Execution result
    J->>K: codebox.submission.judged
    K->>S: Consume verdict
    S->>S: Persist final status
    UI->>S: GET /submissions/{id}
    S-->>UI: Final verdict
Loading

Judge and Sandbox

The judge generates a language-specific harness that invokes the submitted function for every test case and emits a JSON result array. Outputs are compared using JSON equivalence.

Each execution runs in a temporary Docker container with:

  • No network access
  • Configurable CPU and memory limits
  • A configurable execution timeout
  • Separate stdout and stderr capture
  • Cleanup after process exit with docker run --rm

Runtime images:

Language Image Command
Python python:3.12-alpine python main.py
Java eclipse-temurin:21-alpine javac Main.java && java -cp . Main
C++ gcc:13 g++ -O2 -std=c++17 ...

Docker Compose gives the judge access to the host Docker socket. Kubernetes uses a privileged Docker-in-Docker sidecar with a persistent image cache.

Data Model

  • A User has an email, BCrypt password hash, and USER or ADMIN role.
  • A Problem stores its description, difficulty, tags, function signature, supported languages, and generated starter code.
  • A TestCase stores positional input arguments and expected output as JSONB, plus sample visibility and ordering.
  • A Submission stores the user, problem, language, source code, status, runtime, score, failure details, and timestamps.

Submission states are QUEUED, ACCEPTED, WRONG_ANSWER, RUNTIME_ERROR, COMPILE_ERROR, TIME_LIMIT_EXCEEDED, and INTERNAL_ERROR.

Design Tradeoffs

  • Kafka decouples user-facing submission creation from slower code execution and allows the judge to scale independently.
  • The current persist-then-publish flow does not use a transactional outbox. A process failure between those operations can leave a submission queued without an event.
  • Kafka delivery may be repeated, so verdict application is naturally overwrite-safe, but the judge can still execute a duplicate event more than once.
  • Sharing one PostgreSQL instance simplifies local operation, but separate databases or schemas would provide stronger service isolation.
  • Docker provides practical execution isolation for this project, but access to the host Docker socket and privileged Docker-in-Docker are high-trust capabilities that require careful production hardening.

Repository Layout

.
├── frontend/                    # React SPA, Nginx proxy, frontend Dockerfile
├── services/
│   ├── auth-service/            # Identity and JWTs
│   ├── problem-service/         # Problem catalog and test cases
│   ├── submission-service/      # Runs, submissions, and history
│   └── judge-service/           # Sandboxed execution and grading
├── scripts/                     # Local runner, stop script, and E2E test
├── k8s/                         # Kustomize manifests and deployment guide
├── argocd/                      # ArgoCD Application
├── docker-compose.yml           # Complete local stack
├── Jenkinsfile                  # Changed-service image build and tag bump
└── pom.xml                      # Maven multi-module parent

Getting Started

Prerequisites

  • Docker with Docker Compose
  • JDK 21 and Maven for host-based backend development
  • Node.js 20+ and npm for host-based frontend development
  • Python 3 for the end-to-end test

Option 1: Run Everything with Docker Compose

From the repository root:

docker compose up --build

Open http://localhost:8080.

The default local admin account is:

Email:    [email protected]
Password: admin12345

Stop the stack with:

docker compose down

The first judge request can be slower while Docker downloads the language runtime image.

Option 2: Run Applications on the Host

This mode runs PostgreSQL and Kafka in Docker, the Spring services as local Java processes, and the frontend with Vite:

scripts/run-local.sh

Open http://localhost:5173. Logs are written to .local-run/logs/.

scripts/stop-local.sh
scripts/stop-local.sh --infra   # also stop PostgreSQL and Kafka

Use SKIP_BUILD=1 scripts/run-local.sh to reuse existing service JARs.

Local Ports

Component Port
Frontend, Docker Compose 8080
Frontend, Vite 5173
Auth service 8081
Submission service 8082
Judge service 8084
Problem service 8085
PostgreSQL 5432
Kafka host listener 29092

API Overview

All frontend requests use the /api prefix, which the frontend proxy removes before forwarding.

Method and path Purpose Access
POST /auth/signup Create a user Public
POST /auth/login Issue an access token Public
GET /auth/me Return the authenticated user Authenticated
GET /problems List problems Public
GET /problems/{id} Get problem details and sample cases Public
/admin/problems/** Manage problems and test cases Admin
POST /submissions/run Run against sample or custom inputs Authenticated
POST /submissions Queue a judged submission Authenticated
GET /submissions/{id} Get an owned submission Authenticated
GET /submissions List the current user's submissions Authenticated

Configuration

Important environment variables:

Variable Used by Purpose
DB_URL, DB_USERNAME, DB_PASSWORD Stateful backend services PostgreSQL connection
JWT_SECRET Auth, problem, submission Shared HS256 secret; must be at least 32 bytes
JWT_ISSUER, JWT_AUDIENCE Auth, problem, submission JWT validation boundary
ADMIN_SEED_EMAIL, ADMIN_SEED_PASSWORD Auth Bootstrap administrator
INTERNAL_TOKEN Problem Protect hidden-case endpoint
PROBLEM_SERVICE_INTERNAL_TOKEN Judge Authenticate to hidden-case endpoint
KAFKA_BOOTSTRAP_SERVERS Submission, judge Kafka connection
JUDGE_EXECUTION_TIMEOUT Judge Per-execution timeout, for example PT30S
JUDGE_CONTAINER_MEMORY, JUDGE_CONTAINER_CPUS Judge Sandbox resource limits

The defaults in docker-compose.yml are for local development only. Replace all secrets before deploying.

Build and Test

Build all backend services:

mvn clean package

Build the frontend:

cd frontend
npm install
npm run build

With the full stack running, execute the end-to-end test:

python3 scripts/e2e_test.py

The E2E test creates a problem and user, verifies authorization boundaries, runs custom and sample inputs, submits correct and incorrect Python/Java/C++ solutions, polls verdicts, checks history, and removes the test problem.

Deployment

The production-oriented manifests under k8s/ deploy the five application services to a k3s cluster while PostgreSQL and Kafka run externally on the VM.

The delivery flow is:

Git push
  -> Jenkins detects changed services
  -> builds and pushes only affected images
  -> updates image tags in k8s/kustomization.yaml
  -> commits the tag change
  -> ArgoCD syncs and self-heals the cluster

Secrets are intentionally excluded from Kustomize and ArgoCD. See k8s/README.md for the complete cluster setup and operational notes.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages