The Grader System is a comprehensive online judge platform designed to facilitate competitive programming and programming education. It features a modern, responsive React frontend, a robust Node.js (Express) backend, and a PostgreSQL database, all orchestrated with Docker for seamless setup, deployment, and scalability. This system allows users to register, solve programming problems by submitting C++ code, and receive real-time feedback on their submissions. Administrators have powerful tools to manage users, problems, contests, and the database, making it a versatile platform for various programming challenges.
Target Audience: This system is ideal for students, educators, and anyone interested in developing their programming skills through competitive programming or structured practice.
The Grader System offers a rich set of features for both users and administrators:
- User Management:
- User Registration and Login: Secure user authentication with encrypted passwords and session management (case-insensitive unique usernames, per-account login lockout against brute force).
- User Roles: Differentiated access for regular users, staffs and administrators.
- Password Management: Self-service password change from the navbar user menu (keeps you signed in on this device, signs out all others), plus admin-initiated resets in User Management (signs the target out everywhere). Self-service changes can be disabled per-site by admins.
- Batch User Creation (Admin): Administrators can generate multiple user accounts with a defined prefix and random passwords.
- User Profiles: Public profile pages with submission statistics, verdict breakdowns, daily-activity heatmaps, streaks, achievements, a per-category radar chart, and XP progression.
- XP / Level / Tier / Global Rank: Solving problems awards XP based on difficulty; levels and tiers (Novice up to Grandmaster) are derived from the XP history, with a first-solve toast and rank on the global scoreboard.
- Problem Management:
- Problem Statements (PDF): Upload and display problem descriptions in PDF format (up to 2GB).
- Test Case Management: Store and manage test cases (input/output) for each problem in the database, with a per-problem testcase viewer in the admin panel.
- Single Problem Upload (Admin): Administrators can add new problems or update existing ones individually, including their metadata, PDF, and test cases (via ZIP upload).
- Batch Problem Upload (Admin): Efficiently upload multiple problems at once using structured ZIP files (up to 2GB).
- Problem Visibility Control (Admin): Administrators can toggle the visibility of problems to users, individually or across a whole collection at once.
- Categories and Difficulty: Problems carry multiple algorithm categories (from a fixed list) and a Codeforces-like numeric difficulty (800–3500), filterable on the problem list.
- Collections: Organizational groups (e.g. teaching chapters) that problems can belong to.
- Author Filter (Admin): Problem Management can be filtered by author.
- Code Submission & Judging:
- C++ and Python Submission: Users can submit C++ or Python (standard library only) solutions; Python gets time ×4 / memory ×2 limits to compensate for interpreter overhead.
- Isolated Judging Environment: Code is compiled and executed as an unprivileged per-submission sandbox identity (separate uid/gid, resource limits, seccomp network denial) — see SANDBOX.md.
- Real-time Judging: Submissions are judged promptly, with live status updates delivered over Server-Sent Events (polling fallback).
- Detailed Results: For each submission, users receive detailed results per test case, including status (Accepted, Wrong Answer, Time Limit Exceeded, Memory Limit Exceeded, Runtime Error, Compilation Error).
- Time and Memory Limits: Configurable time and memory limits for each problem to control resource usage during judging.
- Contest Management:
- Contest Creation and Configuration (Admin): Administrators can create and configure new programming contests with specific problems, start/end times, and a visibility (hide/show) setting independent of the schedule.
- Contest Visibility (Admin): Contests can be hidden from end users without touching their schedule or status — a reversible publishing gate.
- Contest Participation: Users can view available contests and join those that are currently active or scheduled.
- Contest-Specific Submissions: Submissions made within a contest are tracked separately from general problem submissions, ensuring contest integrity.
- Contest Scoreboard: Live scoreboard displaying participants' scores and rankings within a contest (updates pushed over SSE), frozen at contest end.
- Cheat Detection (Admin): Similarity detection flags pairs of contestants whose submissions for the same problem are near-identical after normalization.
- Scoreboard:
- Global Scoreboard: A public scoreboard displaying user rankings based on their performance across all general problems, with tie-aware ranking.
- Authoring Workspace (Admin/Staff): A full problem-authoring pipeline — drafts with author profiles, a Markdown/LaTeX statement editor with live preview, private reference solutions and generators, sandboxed testcase generation, PDF builds, mechanical verification, and transactional publication into the problem pool. Includes an in-app AI Docs API reference with a copy-for-AI-agent button.
- Site Access Modes (Admin): PUBLIC mode allows guests to browse problems, submissions, and scoreboards read-only; PRIVATE mode requires login for all content.
- Analytics (Admin/Staff): Submission analytics with overview KPIs, daily/hourly activity (site timezone), user/problem drill-downs, retention, and CSV export.
- Database Management (Admin Only):
- Export Database: Administrators can export the database to a
.sqldump file for backup purposes (session data is excluded). - Import Database: Administrators can restore the database from a
.sql,.dump, or.tarfile. The site enters maintenance mode (other requests answer 503) while the import runs. WARNING: Importing a database will PERMANENTLY DELETE ALL EXISTING DATA in the database and replace it with the contents of the uploaded file. Proceed with extreme caution and ensure you have a backup of your current database if needed.
- Export Database: Administrators can export the database to a
- System Settings (Admin Only):
- Registration Toggle: Administrators can enable or disable new user registrations.
- Site Access Mode: PUBLIC (default, guests can browse read-only) or PRIVATE (login required for content).
- Password Change Toggle: Enable or disable self-service password changes for users/staff (admins are exempt).
The Grader System is built with a modern, type-safe tech stack (TypeScript-first), ensuring a robust, scalable, and responsive application.
- Frontend:
- React 19 & TypeScript: Modern UI development with strong typing.
- React Router 7: For declarative routing and data loading.
- Axios: Promise-based HTTP client with typed API contracts.
- Vanilla CSS & HTML5: Clean, performant styling without heavy utility frameworks.
- Backend:
- Node.js & Express 5 (TypeScript): High-performance RESTful API framework.
- PostgreSQL: Robust relational database accessed via raw parameterized SQL.
- Zod: Centralized runtime validation for request schemas.
bcrypt: Secure password hashing.multer&unzipper&archiver: File upload, extraction, and asset management.node-cron: For automated contest scheduling and lifecycle management.express-rate-limit: API, auth, and submission rate limiting.- Session-based Auth: Secure authentication using
express-sessionandconnect-pg-simple, revalidated against the database on every request.
- Judging System:
- C++: Compiled with GCC and executed in a per-submission sandbox identity.
- Python: Standard-library-only interpreter execution with adjusted time/memory limits.
- Containerization & Deployment:
- Docker & Docker Compose: Containerized microservices for consistent environments.
- Nginx: Reverse proxy handling
/apirouting and frontend serving.
- Tools & Testing:
- Jest & Supertest: Comprehensive backend integration and unit testing.
- React Testing Library: Component-level testing for the frontend.
- Git: Version control and collaboration.
To get the Grader System up and running, you only need to install a few essential tools on your host machine. The rest of the application's dependencies (Node.js, npm, PostgreSQL) are managed within Docker containers.
- Docker: Essential for containerizing, building, running, and managing the application's services.
- Git: Required to clone the project repository for local development and version control.
- Node.js 20 (optional): Required only for running npm and root-level smoke tests directly on the host. Docker remains the primary runtime.
-
Clone the repository:
git clone https://github.com/Nonbangkok/OJ.git cd OJ -
Create the local environment file:
cp .env.example .env
Change
POSTGRES_PASSWORDandSECRET_KEYbefore sharing the environment with anyone. The remaining defaults are suitable for plain HTTP on localhost. Notable variables:HTTP_PORT— host port for the app (default80).COOKIE_SECURE— must betruein any production deployment over HTTPS; the backend refuses to start in production without it.REACT_APP_LARGE_UPLOAD_API_URL— optional DNS-only origin endpoint for large admin uploads (batch ZIPs, database imports) to bypass proxied upload size limits.
Once the installation and setup are complete, you can start the application:
- Build and Run Docker Containers:
Navigate to the root directory of the project (where
docker-compose.ymlis located) and execute the following command:docker compose up --build -d
- The first time you run this command, it might take several minutes as Docker downloads the PostgreSQL image and builds the frontend and backend images. The stack runs six services:
database, a one-shotmigratejob,backend, an isolatedauthoring-runner(no network),frontend, andnginx-proxy.
- The first time you run this command, it might take several minutes as Docker downloads the PostgreSQL image and builds the frontend and backend images. The stack runs six services:
- Access the Application:
After the containers have successfully started, open your web browser and navigate to:
http://localhost
The Nginx proxy, running in its own Docker container, handles routing. Requests to
/are forwarded to thefrontendservice, while requests to/api/are rewritten and forwarded to thebackendservice.
The migrate service applies versioned, non-destructive migrations before the
backend starts. A fresh database therefore needs no manual schema command, and
restarting the stack does not erase existing data.
To create the first administrator account, run the interactive script after the stack is healthy:
```bash
docker compose exec backend node dist/scripts/create_admin.js
```
Runtime checks are available at /api/health/live (process) and
/api/health/ready (database and schema readiness).
Apply pending non-destructive schema migrations from the host:
cd backend
npm run db:migrateProduction npm start applies the same migrations after compilation and before
the API starts. Applied versions are recorded in schema_migrations; rerunning
the command is safe.
backend/scripts/init_db.ts is a destructive development reset that drops
existing tables. Do not use it to upgrade an existing database.
Production uses the same base Compose file plus a production-only overlay. This keeps local HTTP, cookies, and ports separate from the public domain, secure cookies, allowed origins, certificates, and Cloudflare tunnel.
cp .env.production.example .env
# Replace every placeholder in .env before continuing.
docker compose -f docker-compose.yml -f docker-compose.production.yml config --quiet
./deploy.shThe production overlay sets NODE_ENV=production, enables secure cookies, mounts
the production Nginx configuration and certificate directories, publishes HTTPS
for the DNS-only large-upload hostname, and starts the Cloudflare tunnel. The
certificate at /etc/letsencrypt/live/nonbangkokgrader.com/ must cover the main, www,
and upload hostnames. Do not use this overlay as the localhost configuration.
The project includes automated tests for both the backend and frontend. You can run the full test suite or run tests for each part separately.
From the project root, execute the unified test script to run both backend and frontend tests:
./tests/run_tests.sh- This script runs backend tests first, then frontend tests. It exits with status code 0 if all tests pass, or 1 if any test fails.
- Most backend tests mock PostgreSQL. The migration integration test requires a
PostgreSQL test URL and is skipped unless
INTEGRATION_DATABASE_URLis provided.
To run the complete backend suite including Slices 4–10 HTTP → PostgreSQL → isolated C++/PDF runner and end-to-end authoring checks, use the dedicated disposable stack from the repository root:
docker compose -p oj-authoring-tests -f tests/authoring/compose.yml up --build --abort-on-container-exit --exit-code-from tests
docker compose -p oj-authoring-tests -f tests/authoring/compose.yml down -vThis stack publishes no ports, uses a temporary database, and shares only its own
job volume with the network-disabled runner. The cleanup command deletes only this
test project's containers/network/job volume. Do not reuse its project name for
a stack containing real data. Protocol, limits, recovery, and configuration are
documented in .context/AUTHORING_RUNNER.md.
Slice 5 generator/seed and manual testcase API conventions are documented in
.context/AUTHORING_TESTCASES.md.
Slice 6 output generation and Slice 7 statement/PDF contracts are documented in
.context/AUTHORING_OUTPUTS.md and
.context/AUTHORING_PDF.md.
After building the runner image above, verify the actual PDF runtime and approved
three-page Red Gate layout (Poppler pdftoppm must be on PATH for the last command):
mkdir -p output/pdf
docker run --rm --network none --read-only --init \
--label com.docker.compose.project=oj-pdf-runtime-tests \
--security-opt no-new-privileges:true --cap-drop ALL \
--cap-add SETUID --cap-add SETGID --cap-add KILL --cap-add SYS_CHROOT --cap-add DAC_OVERRIDE \
--pids-limit 256 --memory 1g --cpus 1 \
--tmpfs /work:rw,exec,nosuid,nodev,size=768m,mode=0755 \
--tmpfs /tmp:rw,noexec,nosuid,nodev,size=32m \
-v "$PWD/tests/authoring/pdf-runtime.mjs:/tests/pdf-runtime.mjs:ro" \
-v "$PWD/backend/tests/fixtures/pdf:/fixtures:ro" \
-v "$PWD/output/pdf:/qa" -e PDF_QA_OUTPUT=/qa \
--entrypoint node oj-authoring-tests-authoring-runner --test /tests/pdf-runtime.mjs
node tests/authoring/pdf-visual.mjsThe visual check ignores PDF timestamps by comparing rasterized pages. A difference fails explicitly; inspect the pages instead of automatically replacing the baseline.
Slice8 Verify All is documented in .context/AUTHORING_VERIFY.md.
Slice9 Publish, hidden legacy-record mapping, privacy and transactional conflicts
are documented in .context/AUTHORING_PUBLISH.md.
Slice10 Admin UI, fast preview, history polling and browser workflow are documented
in .context/AUTHORING_UI.md.
The full Compose suite includes real Verify→Publish and rollback/concurrency tests.
The standalone runtime matrix checks exact output matching, compile-only generators,
runtime failures, resource bounds and expected-output isolation with the same worker image:
docker run --rm --network none --read-only --init \
--label com.docker.compose.project=oj-verify-runtime-tests \
--security-opt no-new-privileges:true --cap-drop ALL \
--cap-add SETUID --cap-add SETGID --cap-add KILL --cap-add SYS_CHROOT --cap-add DAC_OVERRIDE \
--pids-limit 256 --memory 1g --cpus 1 \
--tmpfs /work:rw,exec,nosuid,nodev,size=768m,mode=0755 \
--tmpfs /tmp:rw,noexec,nosuid,nodev,size=32m \
-v "$PWD/tests/authoring/verify-runtime.mjs:/tests/verify-runtime.mjs:ro" \
-v "$PWD/backend/tests/fixtures/pdf:/fixtures:ro" \
--entrypoint node oj-authoring-tests-authoring-runner --test /tests/verify-runtime.mjsThe commands below run integration tests without the separate runner; the
HTTP-to-runner cases are skipped when INTEGRATION_RUNNER_SPOOL is unset.
The Slice 3 integration suite sends HTTP requests through real image processing and
PostgreSQL transactions. It creates a randomly named schema and removes only that
schema when finished. The older migration/restore tests reset the public schema,
so run the complete integration suite only on a disposable database.
From the repository root, build the backend and start a dedicated test database:
docker build -t oj-authoring-test-backend backend
docker run --rm -d --name oj-authoring-test-db \
-e POSTGRES_USER=oj_test -e POSTGRES_PASSWORD=oj_test -e POSTGRES_DB=oj_test \
postgres:16-alpine
docker exec oj-authoring-test-db pg_isready -U oj_test -d oj_testOnce pg_isready reports accepting connections, run the suite in Node 20 with the
same native dependencies and fonts as the deployed backend:
docker run --rm --network container:oj-authoring-test-db \
-e INTEGRATION_DATABASE_URL=postgres://oj_test:oj_test@127.0.0.1:5432/oj_test \
oj-authoring-test-backend npm test -- --runInBand --verbose=false
docker stop oj-authoring-test-dbNo production/local-stack database or persistent volume is used by these commands.
For host-only tests, provide a disposable INTEGRATION_DATABASE_URL. On Linux,
install fontconfig, fonts-dejavu-core, and fonts-tlwg-garuda for fallback-avatar
Thai/Latin coverage. The font-registry check runs on Linux and is skipped on macOS.
With the stack running on the default port:
node --test tests/composeConfig.test.mjs
BASE_URL=http://127.0.0.1 node --test tests/localSessionSmoke.test.mjsThe session smoke test creates a uniquely named local test user, verifies the cookie, reads the authenticated session, logs out, and confirms that the session is gone.
From the project root:
cd backend
npm test- Backend tests use Jest and Supertest to test API endpoints, controllers, and middleware (e.g., authentication, submissions, problems, contests, admin).
- Backend tests are located in
backend/tests/.
From the project root:
cd frontend
npm test- Frontend tests use Jest and React Testing Library (
@testing-library/react,@testing-library/jest-dom,@testing-library/user-event) to test pages, hooks, services, and components. - By default,
npm testruns in watch mode (re-runs on file changes). To run once and exit (e.g., in CI), use:CI=true npm test - Frontend tests are located in
frontend/src/tests/.
The system provides administration tools for exporting and importing the entire database, useful for backups or emergency recovery. These features are accessible via the Admin Panel under "Settings".
Administrators can export the current state of the database to a .sql dump file.
- Navigate to the Admin Panel.
- Go to the "Settings" section.
- Click the "Export Database" button.
- The browser will download an
oj_backup_*.sqlfile containing your entire database.
Administrators can import a database dump file (.sql, .dump, or .tar) to restore the database to a previous state.
WARNING: Importing a database will PERMANENTLY DELETE ALL EXISTING DATA in the database and replace it with the contents of the uploaded file. Proceed with extreme caution and ensure you have a backup of your current database if needed.
- Navigate to the Admin Panel.
- Go to the "Settings" section.
- In the "Import Database" section, click "Choose File" and select your database dump file.
- Click the "Upload & Import" button.
- Confirm the action when prompted. The import process will begin.
- After a successful import, you may need to refresh your browser or re-login.
The system provides robust tools for administrators to manage programming problems, accessible via the Admin Panel under the "Problem Management" section.
- Single Problem Upload:
Administrators can add new problems or update existing ones individually. This includes:
- Problem Metadata: Define problem ID, title, author, time limit (in milliseconds), and memory limit (in megabytes).
- Problem Statement (PDF): Upload a single PDF file (up to 2GB) containing the problem description.
- Test Cases: Upload a
.ziparchive (up to 2GB) containing the input and output test cases for the problem. The system will automatically parse and store these test cases.
- Note: Uploading new test cases for an existing problem will clear all previously associated test cases before inserting the new ones.
- Batch Problem Upload:
For efficiency, administrators can upload multiple problems simultaneously using a single structured
.zipfile (up to 2GB). This feature is available via the "Batch Upload" button on the Problem Management page.- Zip File Structure: The uploaded
.zipfile can follow two main structures:- Single Problem ZIP: The root of the ZIP contains all files for one problem (
config.json, PDF, and test cases). - Multiple Problems ZIP: The root of the ZIP contains multiple directories, where each directory represents a distinct problem.
- Single Problem ZIP: The root of the ZIP contains all files for one problem (
- Zip File Structure: The uploaded
The ZIP file contains all necessary files for a single problem at its root level.
Problem.zip
├── config.json
├── Problem.pdf
└── testcases/
├── input/
│ ├── 1.in
│ └── 2.in
└── output/
├── 1.out
└── 2.out
The ZIP file contains multiple problem directories. The system will iterate through each directory and process it as a separate problem, demonstrating the various ways test cases can be structured.
ProblemSet.zip
├── MyFirstProblem/
│ ├── config.json
│ ├── MyFirstProblem.pdf
│ └── testcases.zip (Internal structure: Flat)
│ ├── 1.in
│ ├── 1.out
│ ├── input_02.txt
│ └── output_02.txt
│
├── MySecondProblem/
│ ├── config.json
│ ├── MySecondProblem.pdf
│ └── testcases/ (Internal structure: Directories)
│ ├── input/
│ │ ├── 01.in
│ │ └── 02.in
│ └── output/
│ ├── 01.out
│ └── 02.out
│
└── MyThirdProblem/
├── config.json
├── MyThirdProblem.pdf
└── data/ (Internal structure: Flat directory)
├── case_01.txt
├── out_case_01.txt
├── case_02.txt
└── out_case_02.txt
Each problem directory (either at the root of a multi-problem ZIP or the content of a single-problem ZIP) must contain:
-
config.json: A JSON file detailing the problem's metadata.{ "id": "plus", "title": "Plus", "author": "Nonbangkok", "time_limit_ms": 1000, "memory_limit_mb": 32, "categories": ["Math", "Implementation"], "difficulty": 800, "collection": "Chapter 1" }id: A unique string identifier for the problem (e.g., "plus").title: The display name of the problem (e.g., "Plus").author: The author of the problem.time_limit_ms: The maximum allowed execution time for a solution, in milliseconds.memory_limit_mb: The maximum allowed memory usage for a solution, in megabytes.categories(optional): An array of category names from the fixed list below. Whitespace is trimmed and duplicates are removed. An empty array (ornull) means uncategorized. Unknown names are rejected with an error that names the offending value and the allowed list — the importer never creates arbitrary categories.- Allowed:
Dynamic Programming,Greedy,Graph,Tree,Data Structures,String,Math,Geometry,Divide and Conquer,Binary Search,Constructive,Bitmasks,Sorting,2D-Grid,Implementation,Other
- Allowed:
difficulty(optional): A rating on the same 800–3500 scale (in steps of 100) used by normal problem editing.null(or omitting the field on a new problem) means Unrated. Values outside the scale, non-integers, or strings are rejected.collection(optional): The name of a collection (e.g.,"Chapter 1") — a name, not a numeric id, so archives stay portable across databases. If a collection with that name exists it is reused; otherwise it is created once (multiple problems in one batch referencing the same new name all land in the same collection). An empty/whitespace-only string is treated the same asnull: no collection.
Updating an existing problem (a ZIP whose
config.jsonidmatches an existing problem — the problem is reported as skipped, not re-created, but its metadata fields below are applied). For the three optional fields, omitted vs. explicit matters:- Field omitted → the stored value is preserved.
- Field set to
null(or[]for categories, or""for collection) → the stored value is cleared. - Field set to a value → the stored value is replaced.
Example: importing an existing problem with
{"id": "plus", "title": "Plus", "author": "Nonbangkok", "time_limit_ms": 1000, "memory_limit_mb": 32, "difficulty": null}keeps its current categories and collection but clears its rating. (PDF and test cases for existing problems are managed from the individual problem's upload form, not the ZIP import.)A problem that fails validation is reported per-problem with a specific message (e.g.,
Problem "tree-dp": config.json "categories.0" is invalid: Unknown category "Graphs". Allowed: ...); the rest of the batch continues, and any collection the failed problem would have created is rolled back. -
Problem Statement PDF: A single
.pdffile (up to 2GB) containing the problem description. The system will automatically detect and use the first PDF file found within the problem's directory. -
Test Cases: Test cases can be organized in one of several flexible formats:
-
Option A: Zip Archive (
testcases.zip) A single.zipfile namedtestcases.zipplaced within the problem directory. This inner ZIP file can itself have two internal structures:- Flat Structure: Input and output files are directly at the root of
testcases.zip. Files must be numerically paired. Supported naming conventions include:input1.in/output1.out,1.in/1.out,input1.txt/output1.txt,test_01.in/test_01.out,case_A.in/case_A.out, etc.Internal structure of MyFirstProblem/testcases.zip ├── 1.in ├── 1.out ├── input_02.txt └── output_02.txt - Directory Structure: The
testcases.zipcontainsinput/andoutput/subdirectories. All input files go intoinput/, and all corresponding output files go intooutput/. Files are paired by sorting them alphabetically/numerically.Another example of a testcases.zip internal structure ├── input/ │ ├── 01.in │ └── 02.in └── output/ ├── 01.out └── 02.out
- Flat Structure: Input and output files are directly at the root of
-
Option B:
input/outputSubdirectories Instead of atestcases.zip, you can provide a subdirectory (e.g.,testcases/ordata/) that directly contains two subfolders:input/andoutput/. All input files are placed ininput/, and their corresponding output files inoutput/. Files are paired by sorting them alphabetically/numerically (e.g.,01.inininput/matches01.outinoutput/).Internal structure of problem testcases , Option B : └── testcases/ ├── input/ │ ├── 01.in │ └── 02.in └── output/ ├── 01.out └── 02.out -
Option C: Flat Directory Structure As an alternative, you can place all test case files directly within a single subdirectory (e.g.,
testcases/ordata/). The system will pair files based on the numbers in their filenames (e.g.,input1.txtwithoutput1.txt,case_02.inwithcase_02.out,test_01.inwithtest_01.out,case_A.inwithcase_A.out).Internal structure of problem testcases , Option C : └── data/ ├── input1.txt ├── output1.txt ├── case_02.in └── case_02.out -
Recommendation: It is highly recommended to use numerically sequential and consistently named test case files (e.g.,
01.in,02.in, ...,01.out,02.out, ...) to ensure correct pairing and ordering during the judging process. This helps avoid unexpected behavior across different file systems and parsing logic.
-
Note: The script will clear any existing test cases for a problem before inserting the new ones.
The Grader System includes a dedicated module for managing programming contests, providing a structured environment for competitive events. This functionality is primarily accessible and configurable by administrators.
- Contest Creation and Configuration:
Administrators can create new contests, defining:
- Basic Information: Contest title and description.
- Schedule: Start and end times for the contest.
- Associated Problems: Select existing problems to be part of the contest. Important: Problems assigned to an active contest become inaccessible as standalone problems for general submission during the contest period. Their pre-contest visibility is restored automatically on every exit path.
- Status Management: Contests progress through various statuses:
scheduled(waiting to start),running(currently active),finishing(end time reached, final migration running), andfinished(concluded). These statuses are managed automatically by the system's scheduler.
- Contest Visibility: Independent from scheduling, administrators can hide or show a contest. Hidden contests are invisible to regular users and guests on every public surface (list, detail, join, scoreboard) while remaining fully manageable by staff and admins.
- Contest Participation: Users can view available contests and join those that are currently active or scheduled. Submission eligibility is checked against the contest's actual start/end times, not just its scheduler status.
- Contest-Specific Submissions: During a contest, participants submit solutions to problems specifically within the contest environment. These submissions are tracked separately from general problem submissions, ensuring contest integrity.
- Contest Scoreboard: A real-time scoreboard is available for each contest, displaying the scores and rankings of participants. This scoreboard is dynamically updated as participants submit and their solutions are judged, and freezes (with all participants listed) once the contest finishes.
- Contest Scheduler:
The backend includes a dedicated service (
contestScheduler.ts) that automatically manages the status of contests based on their defined start and end times.
