This file documents the backend behavior implemented in apps/api.
Base URL (local): http://localhost:5000
Most protected routes require:
Authorization: Bearer <JWT_TOKEN>
Content-Type: application/jsonResponse:
{
"success": true
}Base path: /auth
Starts Google OAuth flow with scopes profile and email.
Handles OAuth callback and redirects to frontend with a JWT query param:
http://localhost:<FPORT>?token=<JWT_TOKEN>
Requires header: Authorization: Bearer <token>
Response:
{
"message": "You are authenticated ",
"user": {
"id": "...",
"email": "...",
"name": "..."
}
}Base path: /room
All room routes require a valid Bearer token.
Get all rooms accessible to the authenticated user.
Success response:
{
"success": true,
"data": [
{
"id": "room-uuid",
"name": "My Code Room",
"language": "java",
"owner_id": "user-uuid"
}
]
}Create room.
Request body:
{
"name": "My Code Room",
"language": "java",
"is_private": false
}Success response:
{
"success": true,
"data": {
"id": "room-uuid",
"name": "My Code Room"
}
}Leave room.
Request body:
{
"roomId": "room-uuid"
}Notes:
- Room owners cannot leave until ownership is transferred.
Add member.
Request body:
{
"roomId": "room-uuid",
"targetUserId": "user-uuid",
"role": "editor"
}Notes:
- For private rooms, only owner can add members.
Transfer ownership.
Request body:
{
"roomId": "room-uuid",
"newOwnerId": "user-uuid"
}Base path: /exams
All exam routes require a valid Bearer token.
List exams visible to the authenticated user. Teachers see exams they created, and active exams are visible for attempts.
Success response:
{
"success": true,
"data": [
{
"id": "exam-uuid",
"title": "Secure DSA Assessment",
"duration_minutes": 75,
"status": "active",
"question_count": 2,
"total_marks": 15
}
]
}Create an exam with MCQ and coding questions.
Request body:
{
"title": "Secure DSA Assessment",
"durationMinutes": 75,
"status": "active",
"allowedLanguages": ["c", "cpp", "python", "java"],
"security": {
"pastePenaltyEnabled": true,
"fullscreenRequired": true,
"tabSwitchWarnings": 3,
"rateLimitPerMinute": 60
},
"questions": [
{
"title": "Two Sum",
"type": "coding",
"prompt": "Given nums and target, return indices...",
"points": 10,
"penalty": 4,
"difficulty": "Easy",
"timeComplexity": "O(n)",
"spaceComplexity": "O(n)",
"starterCode": {
"c": "...",
"cpp": "...",
"python": "...",
"java": "..."
},
"testCases": [
{
"input": "nums = [2,7,11,15], target = 9",
"expectedOutput": "[0,1]",
"isHidden": false
}
]
},
{
"title": "Hash Map Complexity",
"type": "mcq",
"prompt": "What is average lookup time?",
"points": 5,
"penalty": 1,
"options": ["O(1)", "O(log n)", "O(n)", "O(n log n)"],
"correctOption": 0
}
]
}Success response:
{
"success": true,
"data": {
"id": "exam-uuid",
"title": "Secure DSA Assessment",
"durationMinutes": 75,
"status": "active",
"allowedLanguages": ["c", "cpp", "python", "java"],
"questions": []
}
}Fetch one exam with its questions.
Success response:
{
"success": true,
"data": {
"id": "exam-uuid",
"title": "Secure DSA Assessment",
"durationMinutes": 75,
"status": "active",
"security": {
"pastePenaltyEnabled": true,
"fullscreenRequired": true,
"tabSwitchWarnings": 3,
"rateLimitPerMinute": 60
},
"questions": []
}
}Submit an exam attempt with answers, scores, and security events.
Request body:
{
"rawScore": 10,
"penaltyScore": 4,
"finalScore": 6,
"answers": {
"question-uuid": {
"questionId": "question-uuid",
"code": "class Solution {...}",
"language": "java",
"typedCharacters": 140,
"pastedCharacters": 62
}
},
"securityEvents": [
{
"questionId": "question-uuid",
"type": "paste",
"message": "Copy/paste detected.",
"penalty": 4
}
]
}Success response:
{
"success": true,
"data": {
"id": "attempt-uuid",
"exam_id": "exam-uuid",
"raw_score": 10,
"penalty_score": 4,
"final_score": 6
}
}Import public LeetCode problem metadata into a coding-question draft.
Request body:
{
"url": "https://leetcode.com/problems/two-sum/"
}Success response:
{
"success": true,
"data": {
"title": "Two Sum",
"type": "coding",
"prompt": "Given an array of integers...",
"points": 10,
"penalty": 4,
"difficulty": "Easy",
"timeComplexity": "Teacher review required",
"spaceComplexity": "Teacher review required",
"starterCode": {
"c": "...",
"cpp": "...",
"python": "...",
"java": "..."
},
"testCases": [],
"sourceUrl": "https://leetcode.com/problems/two-sum/"
}
}Notes:
- LeetCode hidden judge test cases are not publicly available.
- The importer normalizes public examples and starter snippets. Teachers should review prompts, constraints, complexities, visible tests, and hidden tests before publishing.
The API applies these protections before routes:
X-Content-Type-Options: nosniffX-Frame-Options: DENYReferrer-Policy: no-referrerPermissions-Policydisabling camera, microphone, and geolocationCross-Origin-Resource-Policy: same-site- Per-IP/per-path in-memory rate limiting
- JSON body size limit of
256kb - JSON-only enforcement for
POST,PUT, andPATCH - CORS configured from
CORS_ORIGIN
Socket server shares the same host/port as backend (http://localhost:5000).
join-room
{
"roomId": "room-uuid",
"userId": "user-uuid"
}code-update
{
"roomId": "room-uuid",
"code": "...latest source..."
}leave-room
{
"roomId": "room-uuid",
"userId": "user-uuid"
}init-code
{
"code": "...current room code...",
"version": 1
}code-update
{
"code": "...updated room code...",
"version": 2
}user-joined
{
"userId": "user-uuid",
"message": "A new user joined the room"
}user-left
{
"userId": "user-uuid"
}error
{
"message": "Error description"
}code-updateis validated and applied to Redis.- Backend emits latest code payload to other room clients.
- A debounced BullMQ job (
code-save) is queued. - Worker writes snapshot to
room_snapshotswith version checks.
- Database schema:
apps/api/table.md - Project setup:
README.md