├── backend ├── .env.local ├── .gitignore ├── .prettierignore ├── .prettierrc ├── features.md ├── image-1.png ├── image.png ├── leetcode-api.postman_collection.json ├── package-lock.json ├── package.json ├── prisma │ ├── migrations │ │ ├── 20250408200328_user_model_added │ │ │ └── migration.sql │ │ ├── 20250408211129_added_problem_model │ │ │ └── migration.sql │ │ ├── 20250408213125_made_changes_into_problem_schema │ │ │ └── migration.sql │ │ ├── 20250410041452_added_submission_and_problemsolved_table │ │ │ └── migration.sql │ │ ├── 20250410044837_added_sheet │ │ │ └── migration.sql │ │ ├── 20250410183734_playlist_model_added │ │ │ └── migration.sql │ │ ├── 20250410190453_added_blacklist_model │ │ │ └── migration.sql │ │ ├── 20250412211632_test_result_added │ │ │ └── migration.sql │ │ ├── 20250414054523_init │ │ │ └── migration.sql │ │ └── migration_lock.toml │ └── schema.prisma └── src │ ├── controllers │ ├── auth.controller.js │ ├── executeCode.controller.js │ ├── playlist.controller.js │ ├── problem.controller.js │ └── submission.controller.js │ ├── generated │ └── prisma │ │ ├── client.d.ts │ │ ├── client.js │ │ ├── default.d.ts │ │ ├── default.js │ │ ├── edge.d.ts │ │ ├── edge.js │ │ ├── index-browser.js │ │ ├── index.d.ts │ │ ├── index.js │ │ ├── package.json │ │ ├── query_engine-windows.dll.node │ │ ├── query_engine-windows.dll.node.tmp17532 │ │ ├── query_engine-windows.dll.node.tmp20964 │ │ ├── runtime │ │ ├── edge-esm.js │ │ ├── edge.js │ │ ├── index-browser.d.ts │ │ ├── index-browser.js │ │ ├── library.d.ts │ │ ├── library.js │ │ ├── react-native.js │ │ └── wasm.js │ │ ├── schema.prisma │ │ ├── wasm.d.ts │ │ └── wasm.js │ ├── index.js │ ├── libs │ ├── db.js │ └── problem.libs.js │ ├── middlewares │ └── auth.middleware.js │ └── routes │ ├── auth.routes.js │ ├── executeCode.routes.js │ ├── playlist.routes.js │ ├── problems.routes.js │ └── submission.routes.js ├── frontend ├── .env ├── .env.local ├── .gitignore ├── README.md ├── eslint.config.js ├── index.html ├── package-lock.json ├── package.json ├── public │ ├── Vite + React.html │ ├── Vite + React_files │ │ ├── client │ │ ├── editor.main.css │ │ ├── editor.main.js.download │ │ ├── java.js.download │ │ ├── javascript.js.download │ │ ├── loader.js.download │ │ ├── main.jsx │ │ ├── python.js.download │ │ ├── tsMode.js.download │ │ └── typescript.js.download │ ├── leetlab.svg │ ├── logo.svg │ ├── thumbnail.png │ └── vite.svg ├── src │ ├── App.jsx │ ├── assets │ │ └── react.svg │ ├── components │ │ ├── AddProblemForm.jsx │ │ ├── AddToPlaylist.jsx │ │ ├── AdminRoute.jsx │ │ ├── AuthImagePattern.jsx │ │ ├── CreatePlaylistModal.jsx │ │ ├── Layout.jsx │ │ ├── LogoutButton.jsx │ │ ├── Navbar.jsx │ │ ├── PlaylistProfile.jsx │ │ ├── ProblemCard.jsx │ │ ├── ProblemSolvedByUser.jsx │ │ ├── ProblemTable.jsx │ │ ├── ProfileSubmission.jsx │ │ ├── SampleProblemSelector.jsx │ │ ├── Submission.jsx │ │ └── SubmissionList.jsx │ ├── data │ │ └── index.js │ ├── index.css │ ├── libs │ │ ├── axios.js │ │ └── utils.js │ ├── main.jsx │ ├── pages │ │ ├── AddProblem.jsx │ │ ├── HomePage.jsx │ │ ├── LoginPage.jsx │ │ ├── ProblemPage.jsx │ │ ├── Profile.jsx │ │ └── SignUpPage.jsx │ └── store │ │ ├── useActions.js │ │ ├── useAuthStore.js │ │ ├── useExecution.js │ │ ├── usePlaylistStore.js │ │ ├── useProblemStore.js │ │ └── useSubmissionStore.js └── vite.config.js ├── judge0-installation-guide.md ├── readme.md ├── realine.md └── test.js /backend/.env.local: -------------------------------------------------------------------------------- 1 | PORT = 2 | DATABASE_URL= 3 | JWT_SECRET= 4 | JUDGE0_API_URL=http://localhost:2358/ -------------------------------------------------------------------------------- /backend/.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.toptal.com/developers/gitignore/api/node 2 | # Edit at https://www.toptal.com/developers/gitignore?templates=node 3 | 4 | ### Node ### 5 | # Logs 6 | logs 7 | *.log 8 | npm-debug.log* 9 | yarn-debug.log* 10 | yarn-error.log* 11 | lerna-debug.log* 12 | .pnpm-debug.log* 13 | 14 | # Diagnostic reports (https://nodejs.org/api/report.html) 15 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 16 | 17 | # Runtime data 18 | pids 19 | *.pid 20 | *.seed 21 | *.pid.lock 22 | 23 | # Directory for instrumented libs generated by jscoverage/JSCover 24 | lib-cov 25 | 26 | # Coverage directory used by tools like istanbul 27 | coverage 28 | *.lcov 29 | 30 | # nyc test coverage 31 | .nyc_output 32 | 33 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 34 | .grunt 35 | 36 | # Bower dependency directory (https://bower.io/) 37 | bower_components 38 | 39 | # node-waf configuration 40 | .lock-wscript 41 | 42 | # Compiled binary addons (https://nodejs.org/api/addons.html) 43 | build/Release 44 | 45 | # Dependency directories 46 | node_modules/ 47 | jspm_packages/ 48 | 49 | # Snowpack dependency directory (https://snowpack.dev/) 50 | web_modules/ 51 | 52 | # TypeScript cache 53 | *.tsbuildinfo 54 | 55 | # Optional npm cache directory 56 | .npm 57 | 58 | # Optional eslint cache 59 | .eslintcache 60 | 61 | # Optional stylelint cache 62 | .stylelintcache 63 | 64 | # Microbundle cache 65 | .rpt2_cache/ 66 | .rts2_cache_cjs/ 67 | .rts2_cache_es/ 68 | .rts2_cache_umd/ 69 | 70 | # Optional REPL history 71 | .node_repl_history 72 | 73 | # Output of 'npm pack' 74 | *.tgz 75 | 76 | # Yarn Integrity file 77 | .yarn-integrity 78 | 79 | # dotenv environment variable files 80 | .env 81 | .env.development.local 82 | .env.test.local 83 | .env.production.local 84 | 85 | 86 | # parcel-bundler cache (https://parceljs.org/) 87 | .cache 88 | .parcel-cache 89 | 90 | # Next.js build output 91 | .next 92 | out 93 | 94 | # Nuxt.js build / generate output 95 | .nuxt 96 | dist 97 | 98 | # Gatsby files 99 | .cache/ 100 | # Comment in the public line in if your project uses Gatsby and not Next.js 101 | # https://nextjs.org/blog/next-9-1#public-directory-support 102 | # public 103 | 104 | # vuepress build output 105 | .vuepress/dist 106 | 107 | # vuepress v2.x temp and cache directory 108 | .temp 109 | 110 | # Docusaurus cache and generated files 111 | .docusaurus 112 | 113 | # Serverless directories 114 | .serverless/ 115 | 116 | # FuseBox cache 117 | .fusebox/ 118 | 119 | # DynamoDB Local files 120 | .dynamodb/ 121 | 122 | # TernJS port file 123 | .tern-port 124 | 125 | # Stores VSCode versions used for testing VSCode extensions 126 | .vscode-test 127 | 128 | # yarn v2 129 | .yarn/cache 130 | .yarn/unplugged 131 | .yarn/build-state.yml 132 | .yarn/install-state.gz 133 | .pnp.* 134 | 135 | ### Node Patch ### 136 | # Serverless Webpack directories 137 | .webpack/ 138 | 139 | # Optional stylelint cache 140 | 141 | # SvelteKit build / generate output 142 | .svelte-kit 143 | 144 | # End of https://www.toptal.com/developers/gitignore/api/node -------------------------------------------------------------------------------- /backend/.prettierignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | dist/ 3 | .env -------------------------------------------------------------------------------- /backend/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": true, 3 | "singleQuote": true, 4 | "tabWidth": 2, 5 | "trailingComma": "es5", 6 | "printWidth": 80 7 | } -------------------------------------------------------------------------------- /backend/features.md: -------------------------------------------------------------------------------- 1 | Core Features 2 | 1. User Authentication:✅ 3 | 4 | - Login and signup using JWT tokens. 5 | - Protected routes for authenticated users. 6 | 7 | 2. Admin Features:✅ 8 | 9 | - Create custom coding problems. 10 | - Test problems with Judge0 before saving. 11 | - AI-generated problem creation. ( on hold) 12 | 13 | 3. Problem Management:✅ 14 | 15 | - Admin can view, edit, and delete problems. 16 | - Users can browse all available problems. 17 | 18 | 4. Code Execution: 19 | 20 | - Users can write and submit code in multiple languages. 21 | - Use Judge0 API for real-time code execution and result validation. 22 | 23 | 5. Submission Tracking: 24 | 25 | - Save user submissions to the database.✅ 26 | - Mark problems as "Solved" after successful submission.✅ 27 | 28 | 6. Progress Tracking: 29 | 30 | - Highlight solved problems for users. 31 | - Display user progress (e.g., number of problems solved). 32 |  33 |  34 | --- 35 | AI-Powered Features 36 | 37 | 1. Problem Recommendations: 38 | 39 | - Recommend problems based on user skill level and past performance. 40 | 41 | 2. Solution Assistance: 42 | 43 | - Provide hints or partial solutions for stuck users. 44 | - Debugging assistance for submitted code. 45 | 46 | 47 | 48 | 4. AI-Generated Problems: 49 | - Automatically generate new coding problems with test cases and solutions. 50 | 51 | 52 | Frontend Features 53 | 54 | 1. Responsive Design: 55 | 56 | - Clean and Modern UI using Daisy-UI and Tailwind CSS. 57 | - Dark mode support. 58 | 59 | 2. Code Editor: 60 | 61 | - Integrate Monaco Editor for writing and submitting code. 62 | 63 | 3. Real-Time Feedback: 64 | 65 | - Display code execution results (e.g., "Accepted", "Wrong Answer") immediately. 66 | 67 | 4. Dashboard: 68 | 69 | - Show user statistics (e.g., solved problems, accuracy rate). -------------------------------------------------------------------------------- /backend/image-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aestheticsuraj234/chai-or-leetcode/0db1a64e0e1c4cd636cfb6d2f8e7f02e30796777/backend/image-1.png -------------------------------------------------------------------------------- /backend/image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aestheticsuraj234/chai-or-leetcode/0db1a64e0e1c4cd636cfb6d2f8e7f02e30796777/backend/image.png -------------------------------------------------------------------------------- /backend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "leetcode-backend", 3 | "version": "1.0.0", 4 | "main": "src/index.js", 5 | "scripts": { 6 | "dev": "nodemon src/index.js", 7 | "format": "prettier --write \"src/**/*.js\"", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "keywords": [ 11 | "backend", 12 | "leetcode", 13 | "clone", 14 | "api", 15 | "nodejs" 16 | ], 17 | "author": "", 18 | "license": "ISC", 19 | "type": "module", 20 | "description": "This is a backend for leetcode-clone", 21 | "dependencies": { 22 | "@prisma/client": "^6.6.0", 23 | "axios": "^1.8.4", 24 | "bcryptjs": "^3.0.2", 25 | "cookie-parser": "^1.4.7", 26 | "cors": "^2.8.5", 27 | "dotenv": "^16.4.7", 28 | "express": "^5.1.0", 29 | "jsonwebtoken": "^9.0.2", 30 | "morgan": "^1.10.0", 31 | "node-cron": "^3.0.3", 32 | "nodemon": "^3.1.9", 33 | "prettier": "^3.5.3", 34 | "prisma": "^6.6.0" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /backend/prisma/migrations/20250408200328_user_model_added/migration.sql: -------------------------------------------------------------------------------- 1 | -- CreateEnum 2 | CREATE TYPE "UserRole" AS ENUM ('ADMIN', 'USER'); 3 | 4 | -- CreateTable 5 | CREATE TABLE "User" ( 6 | "id" TEXT NOT NULL, 7 | "name" TEXT, 8 | "email" TEXT NOT NULL, 9 | "image" TEXT, 10 | "role" "UserRole" NOT NULL DEFAULT 'USER', 11 | "password" TEXT NOT NULL, 12 | "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, 13 | "updatedAt" TIMESTAMP(3) NOT NULL, 14 | 15 | CONSTRAINT "User_pkey" PRIMARY KEY ("id") 16 | ); 17 | 18 | -- CreateIndex 19 | CREATE UNIQUE INDEX "User_name_key" ON "User"("name"); 20 | 21 | -- CreateIndex 22 | CREATE UNIQUE INDEX "User_email_key" ON "User"("email"); 23 | 24 | -- CreateIndex 25 | CREATE INDEX "User_role_idx" ON "User"("role"); 26 | -------------------------------------------------------------------------------- /backend/prisma/migrations/20250408211129_added_problem_model/migration.sql: -------------------------------------------------------------------------------- 1 | -- CreateEnum 2 | CREATE TYPE "Difficulty" AS ENUM ('EASY', 'MEDIUM', 'HARD'); 3 | 4 | -- CreateTable 5 | CREATE TABLE "Problem" ( 6 | "id" TEXT NOT NULL, 7 | "title" TEXT NOT NULL, 8 | "description" TEXT NOT NULL, 9 | "difficulty" "Difficulty" NOT NULL, 10 | "tags" TEXT[], 11 | "userId" TEXT NOT NULL, 12 | "examples" JSONB NOT NULL, 13 | "constraints" TEXT NOT NULL, 14 | "hints" JSONB, 15 | "editorial" JSONB, 16 | "testCases" JSONB NOT NULL, 17 | "codeSnippets" JSONB NOT NULL, 18 | "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, 19 | "updatedAt" TIMESTAMP(3) NOT NULL, 20 | 21 | CONSTRAINT "Problem_pkey" PRIMARY KEY ("id") 22 | ); 23 | 24 | -- CreateIndex 25 | CREATE UNIQUE INDEX "Problem_title_key" ON "Problem"("title"); 26 | 27 | -- CreateIndex 28 | CREATE INDEX "Problem_difficulty_idx" ON "Problem"("difficulty"); 29 | 30 | -- AddForeignKey 31 | ALTER TABLE "Problem" ADD CONSTRAINT "Problem_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE; 32 | -------------------------------------------------------------------------------- /backend/prisma/migrations/20250408213125_made_changes_into_problem_schema/migration.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Warnings: 3 | 4 | - Added the required column `referenceSolutions` to the `Problem` table without a default value. This is not possible if the table is not empty. 5 | 6 | */ 7 | -- DropIndex 8 | DROP INDEX "Problem_title_key"; 9 | 10 | -- AlterTable 11 | ALTER TABLE "Problem" ADD COLUMN "referenceSolutions" JSONB NOT NULL, 12 | ALTER COLUMN "hints" SET DATA TYPE TEXT, 13 | ALTER COLUMN "editorial" SET DATA TYPE TEXT; 14 | -------------------------------------------------------------------------------- /backend/prisma/migrations/20250410041452_added_submission_and_problemsolved_table/migration.sql: -------------------------------------------------------------------------------- 1 | -- CreateTable 2 | CREATE TABLE "Submission" ( 3 | "id" TEXT NOT NULL, 4 | "userId" TEXT NOT NULL, 5 | "problemId" TEXT NOT NULL, 6 | "sourceCode" TEXT NOT NULL, 7 | "language" TEXT NOT NULL, 8 | "stdin" TEXT, 9 | "stdout" TEXT, 10 | "stderr" TEXT, 11 | "compileOutput" TEXT, 12 | "status" TEXT NOT NULL, 13 | "memory" TEXT, 14 | "time" TEXT, 15 | "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, 16 | 17 | CONSTRAINT "Submission_pkey" PRIMARY KEY ("id") 18 | ); 19 | 20 | -- CreateTable 21 | CREATE TABLE "ProblemSolved" ( 22 | "id" TEXT NOT NULL, 23 | "userId" TEXT NOT NULL, 24 | "problemId" TEXT NOT NULL, 25 | "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, 26 | 27 | CONSTRAINT "ProblemSolved_pkey" PRIMARY KEY ("id") 28 | ); 29 | 30 | -- CreateIndex 31 | CREATE INDEX "Submission_status_idx" ON "Submission"("status"); 32 | 33 | -- CreateIndex 34 | CREATE UNIQUE INDEX "ProblemSolved_userId_problemId_key" ON "ProblemSolved"("userId", "problemId"); 35 | 36 | -- AddForeignKey 37 | ALTER TABLE "Submission" ADD CONSTRAINT "Submission_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE; 38 | 39 | -- AddForeignKey 40 | ALTER TABLE "Submission" ADD CONSTRAINT "Submission_problemId_fkey" FOREIGN KEY ("problemId") REFERENCES "Problem"("id") ON DELETE CASCADE ON UPDATE CASCADE; 41 | 42 | -- AddForeignKey 43 | ALTER TABLE "ProblemSolved" ADD CONSTRAINT "ProblemSolved_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE; 44 | 45 | -- AddForeignKey 46 | ALTER TABLE "ProblemSolved" ADD CONSTRAINT "ProblemSolved_problemId_fkey" FOREIGN KEY ("problemId") REFERENCES "Problem"("id") ON DELETE CASCADE ON UPDATE CASCADE; 47 | -------------------------------------------------------------------------------- /backend/prisma/migrations/20250410044837_added_sheet/migration.sql: -------------------------------------------------------------------------------- 1 | -- CreateTable 2 | CREATE TABLE "Sheet" ( 3 | "id" TEXT NOT NULL, 4 | "name" TEXT NOT NULL, 5 | "description" TEXT, 6 | "userId" TEXT NOT NULL, 7 | "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, 8 | "updatedAt" TIMESTAMP(3) NOT NULL, 9 | 10 | CONSTRAINT "Sheet_pkey" PRIMARY KEY ("id") 11 | ); 12 | 13 | -- CreateTable 14 | CREATE TABLE "Topic" ( 15 | "id" TEXT NOT NULL, 16 | "name" TEXT NOT NULL, 17 | "sheetId" TEXT NOT NULL, 18 | "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, 19 | "updatedAt" TIMESTAMP(3) NOT NULL, 20 | 21 | CONSTRAINT "Topic_pkey" PRIMARY KEY ("id") 22 | ); 23 | 24 | -- CreateTable 25 | CREATE TABLE "Subtopic" ( 26 | "id" TEXT NOT NULL, 27 | "name" TEXT NOT NULL, 28 | "topicId" TEXT NOT NULL, 29 | "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, 30 | "updatedAt" TIMESTAMP(3) NOT NULL, 31 | 32 | CONSTRAINT "Subtopic_pkey" PRIMARY KEY ("id") 33 | ); 34 | 35 | -- CreateTable 36 | CREATE TABLE "ProblemInSubtopic" ( 37 | "id" TEXT NOT NULL, 38 | "subtopicId" TEXT NOT NULL, 39 | "problemId" TEXT NOT NULL, 40 | "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, 41 | 42 | CONSTRAINT "ProblemInSubtopic_pkey" PRIMARY KEY ("id") 43 | ); 44 | 45 | -- CreateIndex 46 | CREATE INDEX "Sheet_userId_idx" ON "Sheet"("userId"); 47 | 48 | -- CreateIndex 49 | CREATE INDEX "Topic_sheetId_idx" ON "Topic"("sheetId"); 50 | 51 | -- CreateIndex 52 | CREATE INDEX "Subtopic_topicId_idx" ON "Subtopic"("topicId"); 53 | 54 | -- CreateIndex 55 | CREATE UNIQUE INDEX "ProblemInSubtopic_subtopicId_problemId_key" ON "ProblemInSubtopic"("subtopicId", "problemId"); 56 | 57 | -- AddForeignKey 58 | ALTER TABLE "Sheet" ADD CONSTRAINT "Sheet_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE; 59 | 60 | -- AddForeignKey 61 | ALTER TABLE "Topic" ADD CONSTRAINT "Topic_sheetId_fkey" FOREIGN KEY ("sheetId") REFERENCES "Sheet"("id") ON DELETE CASCADE ON UPDATE CASCADE; 62 | 63 | -- AddForeignKey 64 | ALTER TABLE "Subtopic" ADD CONSTRAINT "Subtopic_topicId_fkey" FOREIGN KEY ("topicId") REFERENCES "Topic"("id") ON DELETE CASCADE ON UPDATE CASCADE; 65 | 66 | -- AddForeignKey 67 | ALTER TABLE "ProblemInSubtopic" ADD CONSTRAINT "ProblemInSubtopic_subtopicId_fkey" FOREIGN KEY ("subtopicId") REFERENCES "Subtopic"("id") ON DELETE CASCADE ON UPDATE CASCADE; 68 | 69 | -- AddForeignKey 70 | ALTER TABLE "ProblemInSubtopic" ADD CONSTRAINT "ProblemInSubtopic_problemId_fkey" FOREIGN KEY ("problemId") REFERENCES "Problem"("id") ON DELETE CASCADE ON UPDATE CASCADE; 71 | -------------------------------------------------------------------------------- /backend/prisma/migrations/20250410183734_playlist_model_added/migration.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Warnings: 3 | 4 | - You are about to drop the `ProblemInSubtopic` table. If the table is not empty, all the data it contains will be lost. 5 | - You are about to drop the `Sheet` table. If the table is not empty, all the data it contains will be lost. 6 | - You are about to drop the `Subtopic` table. If the table is not empty, all the data it contains will be lost. 7 | - You are about to drop the `Topic` table. If the table is not empty, all the data it contains will be lost. 8 | 9 | */ 10 | -- DropForeignKey 11 | ALTER TABLE "ProblemInSubtopic" DROP CONSTRAINT "ProblemInSubtopic_problemId_fkey"; 12 | 13 | -- DropForeignKey 14 | ALTER TABLE "ProblemInSubtopic" DROP CONSTRAINT "ProblemInSubtopic_subtopicId_fkey"; 15 | 16 | -- DropForeignKey 17 | ALTER TABLE "Sheet" DROP CONSTRAINT "Sheet_userId_fkey"; 18 | 19 | -- DropForeignKey 20 | ALTER TABLE "Subtopic" DROP CONSTRAINT "Subtopic_topicId_fkey"; 21 | 22 | -- DropForeignKey 23 | ALTER TABLE "Topic" DROP CONSTRAINT "Topic_sheetId_fkey"; 24 | 25 | -- DropTable 26 | DROP TABLE "ProblemInSubtopic"; 27 | 28 | -- DropTable 29 | DROP TABLE "Sheet"; 30 | 31 | -- DropTable 32 | DROP TABLE "Subtopic"; 33 | 34 | -- DropTable 35 | DROP TABLE "Topic"; 36 | 37 | -- CreateTable 38 | CREATE TABLE "Playlist" ( 39 | "id" TEXT NOT NULL, 40 | "name" TEXT NOT NULL, 41 | "description" TEXT, 42 | "userId" TEXT NOT NULL, 43 | "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, 44 | "updatedAt" TIMESTAMP(3) NOT NULL, 45 | 46 | CONSTRAINT "Playlist_pkey" PRIMARY KEY ("id") 47 | ); 48 | 49 | -- CreateTable 50 | CREATE TABLE "ProblemInPlaylist" ( 51 | "id" TEXT NOT NULL, 52 | "playlistId" TEXT NOT NULL, 53 | "problemId" TEXT NOT NULL, 54 | "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, 55 | 56 | CONSTRAINT "ProblemInPlaylist_pkey" PRIMARY KEY ("id") 57 | ); 58 | 59 | -- CreateIndex 60 | CREATE UNIQUE INDEX "Playlist_name_userId_key" ON "Playlist"("name", "userId"); 61 | 62 | -- CreateIndex 63 | CREATE UNIQUE INDEX "ProblemInPlaylist_playlistId_problemId_key" ON "ProblemInPlaylist"("playlistId", "problemId"); 64 | 65 | -- AddForeignKey 66 | ALTER TABLE "Playlist" ADD CONSTRAINT "Playlist_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE; 67 | 68 | -- AddForeignKey 69 | ALTER TABLE "ProblemInPlaylist" ADD CONSTRAINT "ProblemInPlaylist_playlistId_fkey" FOREIGN KEY ("playlistId") REFERENCES "Playlist"("id") ON DELETE CASCADE ON UPDATE CASCADE; 70 | 71 | -- AddForeignKey 72 | ALTER TABLE "ProblemInPlaylist" ADD CONSTRAINT "ProblemInPlaylist_problemId_fkey" FOREIGN KEY ("problemId") REFERENCES "Problem"("id") ON DELETE CASCADE ON UPDATE CASCADE; 73 | -------------------------------------------------------------------------------- /backend/prisma/migrations/20250410190453_added_blacklist_model/migration.sql: -------------------------------------------------------------------------------- 1 | -- CreateTable 2 | CREATE TABLE "TokenBlacklist" ( 3 | "id" TEXT NOT NULL, 4 | "token" TEXT NOT NULL, 5 | "expiresAt" TIMESTAMP(3) NOT NULL, 6 | "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, 7 | 8 | CONSTRAINT "TokenBlacklist_pkey" PRIMARY KEY ("id") 9 | ); 10 | 11 | -- CreateIndex 12 | CREATE UNIQUE INDEX "TokenBlacklist_token_key" ON "TokenBlacklist"("token"); 13 | -------------------------------------------------------------------------------- /backend/prisma/migrations/20250412211632_test_result_added/migration.sql: -------------------------------------------------------------------------------- 1 | -- CreateTable 2 | CREATE TABLE "TestCaseResult" ( 3 | "id" TEXT NOT NULL, 4 | "submissionId" TEXT NOT NULL, 5 | "testCase" INTEGER NOT NULL, 6 | "passed" BOOLEAN NOT NULL, 7 | "stdout" TEXT, 8 | "expected" TEXT NOT NULL, 9 | "stderr" TEXT, 10 | "compileOutput" TEXT, 11 | "status" TEXT NOT NULL, 12 | "memory" TEXT, 13 | "time" TEXT, 14 | "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, 15 | 16 | CONSTRAINT "TestCaseResult_pkey" PRIMARY KEY ("id") 17 | ); 18 | 19 | -- CreateIndex 20 | CREATE INDEX "TestCaseResult_submissionId_idx" ON "TestCaseResult"("submissionId"); 21 | 22 | -- AddForeignKey 23 | ALTER TABLE "TestCaseResult" ADD CONSTRAINT "TestCaseResult_submissionId_fkey" FOREIGN KEY ("submissionId") REFERENCES "Submission"("id") ON DELETE CASCADE ON UPDATE CASCADE; 24 | -------------------------------------------------------------------------------- /backend/prisma/migrations/20250414054523_init/migration.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Warnings: 3 | 4 | - Changed the type of `sourceCode` on the `Submission` table. No cast exists, the column would be dropped and recreated, which cannot be done if there is data, since the column is required. 5 | 6 | */ 7 | -- AlterTable 8 | ALTER TABLE "Submission" DROP COLUMN "sourceCode", 9 | ADD COLUMN "sourceCode" JSONB NOT NULL; 10 | -------------------------------------------------------------------------------- /backend/prisma/migrations/migration_lock.toml: -------------------------------------------------------------------------------- 1 | # Please do not edit this file manually 2 | # It should be added in your version-control system (e.g., Git) 3 | provider = "postgresql" 4 | -------------------------------------------------------------------------------- /backend/prisma/schema.prisma: -------------------------------------------------------------------------------- 1 | generator client { 2 | provider = "prisma-client-js" 3 | output = "../src/generated/prisma" 4 | } 5 | 6 | datasource db { 7 | provider = "postgresql" 8 | url = env("DATABASE_URL") 9 | } 10 | 11 | enum UserRole { 12 | ADMIN 13 | USER 14 | } 15 | 16 | enum Difficulty { 17 | EASY 18 | MEDIUM 19 | HARD 20 | } 21 | 22 | model User { 23 | id String @id @default(uuid()) 24 | name String? @unique 25 | email String @unique 26 | image String? 27 | role UserRole @default(USER) 28 | password String 29 | createdAt DateTime @default(now()) 30 | updatedAt DateTime @updatedAt 31 | 32 | // Relationships 33 | problems Problem[] // Problems created by this user 34 | submissions Submission[] // Submissions made by this user 35 | solvedProblems ProblemSolved[] // Problems solved by this user 36 | playlists Playlist[] // Playlists created by this user 37 | 38 | @@index([role]) 39 | } 40 | 41 | model TokenBlacklist { 42 | id String @id @default(uuid()) 43 | token String @unique 44 | expiresAt DateTime 45 | createdAt DateTime @default(now()) 46 | 47 | } 48 | 49 | model Problem { 50 | id String @id @default(uuid()) 51 | title String 52 | description String 53 | difficulty Difficulty // EASY, MEDIUM, HARD 54 | tags String[] 55 | userId String // Creator of the problem 56 | examples Json // Language-specific examples 57 | constraints String 58 | hints String? 59 | editorial String? 60 | 61 | testCases Json // Universal test cases (input/output pairs) 62 | codeSnippets Json // Language-specific starter code snippets 63 | referenceSolutions Json // Correct solutions for each supported language 64 | 65 | createdAt DateTime @default(now()) 66 | updatedAt DateTime @updatedAt 67 | 68 | // Relationships 69 | user User @relation(fields: [userId], references: [id], onDelete: Cascade) 70 | submissions Submission[] // Submissions for this problem 71 | solvedBy ProblemSolved[] // Users who have solved this problem 72 | problemsPlaylists ProblemInPlaylist[] // Playlists that include this problem 73 | 74 | @@index([difficulty]) 75 | } 76 | 77 | model Submission { 78 | id String @id @default(uuid()) 79 | userId String // User who submitted the code 80 | problemId String // Problem for which the code was submitted 81 | sourceCode Json // Submitted code 82 | language String // Programming language used 83 | stdin String? // Input provided for execution 84 | stdout String? // Output of the execution 85 | stderr String? // Error messages (if any) 86 | compileOutput String? // Compilation errors (if any) 87 | status String // Execution status (e.g., Accepted, Wrong Answer, etc.) 88 | memory String? // Memory usage 89 | time String? // Execution time 90 | createdAt DateTime @default(now()) 91 | 92 | // Relationships 93 | user User @relation(fields: [userId], references: [id], onDelete: Cascade) 94 | problem Problem @relation(fields: [problemId], references: [id], onDelete: Cascade) 95 | testCases TestCaseResult[] 96 | 97 | @@index([status]) 98 | } 99 | 100 | model TestCaseResult { 101 | id String @id @default(uuid()) 102 | submissionId String 103 | testCase Int 104 | passed Boolean 105 | stdout String? 106 | expected String 107 | stderr String? 108 | compileOutput String? 109 | status String 110 | memory String? 111 | time String? 112 | 113 | createdAt DateTime @default(now()) 114 | 115 | submission Submission @relation(fields: [submissionId], references: [id], onDelete: Cascade) 116 | 117 | @@index([submissionId]) 118 | } 119 | 120 | 121 | model ProblemSolved { 122 | id String @id @default(uuid()) 123 | userId String // User who solved the problem 124 | problemId String // Problem that was solved 125 | createdAt DateTime @default(now()) 126 | 127 | // Relationships 128 | user User @relation(fields: [userId], references: [id], onDelete: Cascade) 129 | problem Problem @relation(fields: [problemId], references: [id], onDelete: Cascade) 130 | 131 | 132 | 133 | 134 | @@unique([userId, problemId]) // Ensure a user cannot solve the same problem multiple times 135 | } 136 | 137 | 138 | model Playlist { 139 | id String @id @default(uuid()) 140 | name String 141 | description String? 142 | userId String // User who created the playlist 143 | createdAt DateTime @default(now()) 144 | updatedAt DateTime @updatedAt 145 | 146 | problems ProblemInPlaylist[] // Problems in this playlist 147 | 148 | user User @relation(fields: [userId], references: [id], onDelete: Cascade) 149 | 150 | @@unique([name, userId]) // Ensure unique playlist names per user 151 | } 152 | 153 | model ProblemInPlaylist { 154 | id String @id @default(uuid()) 155 | playlistId String 156 | problemId String 157 | createdAt DateTime @default(now()) 158 | 159 | playlist Playlist @relation(fields: [playlistId], references: [id], onDelete: Cascade) 160 | problem Problem @relation(fields: [problemId], references: [id], onDelete: Cascade) 161 | 162 | @@unique([playlistId, problemId]) // Ensure no duplicate problems in the same playlist 163 | } -------------------------------------------------------------------------------- /backend/src/controllers/auth.controller.js: -------------------------------------------------------------------------------- 1 | import jwt from "jsonwebtoken"; 2 | import bcrypt from "bcryptjs"; 3 | import { db } from "../libs/db.js"; 4 | import { UserRole } from "../generated/prisma/index.js"; 5 | 6 | // REGISTER 7 | export const register = async (req, res) => { 8 | const { email, password, name } = req.body; 9 | 10 | try { 11 | const existingUser = await db.user.findUnique({ where: { email } }); 12 | if (existingUser) { 13 | return res.status(400).json({ error: "User already exists" }); 14 | } 15 | 16 | const hashedPassword = await bcrypt.hash(password, 10); 17 | 18 | const newUser = await db.user.create({ 19 | data: { 20 | email, 21 | password: hashedPassword, 22 | name, 23 | role: UserRole.USER, 24 | }, 25 | }); 26 | 27 | const token = jwt.sign({ id: newUser.id }, process.env.JWT_SECRET, { 28 | expiresIn: "7d", 29 | }); 30 | 31 | res.cookie("jwt", token, { 32 | httpOnly: true, 33 | sameSite: "strict", 34 | secure: process.env.NODE_ENV !== "development", 35 | maxAge: 7 * 24 * 60 * 60 * 1000, 36 | }); 37 | 38 | res.status(201).json({ 39 | message: "User registered successfully", 40 | user: { 41 | id: newUser.id, 42 | email: newUser.email, 43 | name: newUser.name, 44 | role: newUser.role, 45 | image: newUser?.image, 46 | }, 47 | }); 48 | } catch (error) { 49 | console.error("Registration Error:", error); 50 | res.status(500).json({ error: error.message }); 51 | } 52 | }; 53 | 54 | // LOGIN 55 | export const login = async (req, res) => { 56 | const { email, password } = req.body; 57 | 58 | try { 59 | const user = await db.user.findUnique({ where: { email } }); 60 | if (!user) { 61 | return res.status(404).json({ error: "User not found" }); 62 | } 63 | 64 | const isMatch = await bcrypt.compare(password, user.password); 65 | if (!isMatch) { 66 | return res.status(401).json({ error: "Invalid credentials" }); 67 | } 68 | 69 | const token = jwt.sign({ id: user.id }, process.env.JWT_SECRET, { 70 | expiresIn: "7d", 71 | }); 72 | 73 | res.cookie("jwt", token, { 74 | httpOnly: true, 75 | sameSite: "strict", 76 | secure: process.env.NODE_ENV !== "development", 77 | maxAge: 7 * 24 * 60 * 60 * 1000, 78 | }); 79 | 80 | res.status(200).json({ 81 | message: "Login successful", 82 | user: { 83 | id: user.id, 84 | email: user.email, 85 | name: user.name, 86 | role: user.role, 87 | image: user?.image, 88 | }, 89 | }); 90 | } catch (error) { 91 | console.error("Login Error:", error); 92 | res.status(500).json({ error: "Login failed" }); 93 | } 94 | }; 95 | 96 | // LOGOUT 97 | export const logout = async (req, res) => { 98 | try { 99 | res.clearCookie("jwt", { 100 | httpOnly: true, 101 | sameSite: "strict", 102 | secure: process.env.NODE_ENV !== "development", 103 | }); 104 | 105 | res.status(200).json({ success: true, message: "Logout successful" }); 106 | } catch (error) { 107 | console.error("Logout Error:", error); 108 | res.status(500).json({ error: "Failed to log out" }); 109 | } 110 | }; 111 | 112 | // CHECK AUTH 113 | export const checkAuth = async (req, res) => { 114 | try { 115 | res.status(200).json({ 116 | success: true, 117 | message: "User authenticated successfully", 118 | user: req.user, 119 | }); 120 | } catch (error) { 121 | console.error("Auth Check Error:", error); 122 | res.status(500).json({ error: "Failed to check authentication" }); 123 | } 124 | }; 125 | 126 | // GET SUBMISSIONS 127 | export const getSubmissions = async (req, res) => { 128 | try { 129 | const submissions = await db.submission.findMany({ 130 | where: { 131 | userId: req.user.id, 132 | }, 133 | }); 134 | res.status(200).json({ 135 | success: true, 136 | message: "Submissions fetched successfully", 137 | submissions, 138 | }); 139 | } catch (error) { 140 | console.error("Fetch Submissions Error:", error); 141 | res.status(500).json({ error: "Failed to fetch submissions" }); 142 | } 143 | }; 144 | 145 | // GET USER PLAYLISTS 146 | export const getUserPlaylists = async (req, res) => { 147 | try { 148 | const playLists = await db.playlist.findMany({ 149 | where: { 150 | userId: req.user.id, 151 | }, 152 | select: { 153 | id: true, 154 | name: true, 155 | description: true, 156 | createdAt: true, 157 | }, 158 | }); 159 | 160 | res.status(200).json({ 161 | success: true, 162 | message: "Playlists fetched successfully", 163 | playLists, 164 | }); 165 | } catch (error) { 166 | console.error("Fetch Playlists Error:", error); 167 | res.status(500).json({ error: "Failed to fetch playlists" }); 168 | } 169 | }; 170 | -------------------------------------------------------------------------------- /backend/src/controllers/executeCode.controller.js: -------------------------------------------------------------------------------- 1 | import { db } from '../libs/db.js'; 2 | import { 3 | getLanguageName, 4 | pollBatchResults, 5 | submitBatch, 6 | } from '../libs/problem.libs.js'; 7 | 8 | // 🌟 Main controller function to handle code execution and submission 9 | export const executeCode = async (req, res) => { 10 | const { source_code, language_id, stdin, expected_outputs, problemId } = req.body; 11 | const userId = req.user.id; 12 | 13 | try { 14 | // ✅ 1. Validate incoming test cases 15 | if ( 16 | !Array.isArray(stdin) || 17 | stdin.length === 0 || 18 | !Array.isArray(expected_outputs) || 19 | expected_outputs.length !== stdin.length 20 | ) { 21 | return res.status(400).json({ error: 'Invalid or missing test cases' }); 22 | } 23 | 24 | // 📦 2. Prepare submissions for Judge0 25 | const submissions = stdin.map((input) => ({ 26 | source_code, 27 | language_id, 28 | stdin: input, 29 | base64_encoded: false, 30 | wait: false, 31 | })); 32 | 33 | // 🚀 3. Submit batch 34 | const submitResponse = await submitBatch(submissions); 35 | const tokens = submitResponse.map((res) => res.token); 36 | 37 | // ⏳ 4. Poll for results 38 | const results = await pollBatchResults(tokens); 39 | 40 | // 📊 5. Analyze test results 41 | let allPassed = true; 42 | const detailedResults = results.map((result, i) => { 43 | const stdout = result.stdout?.trim() || null; 44 | const expected_output = expected_outputs[i]?.trim(); 45 | const passed = stdout === expected_output; 46 | 47 | if (!passed) allPassed = false; 48 | 49 | return { 50 | testCase: i + 1, 51 | passed, 52 | stdout, 53 | expected: expected_output, 54 | stderr: result.stderr || null, 55 | compile_output: result.compile_output || null, 56 | status: result.status.description, 57 | memory: result.memory ? `${result.memory} KB` : undefined, 58 | time: result.time ? `${result.time} s` : undefined, 59 | }; 60 | }); 61 | 62 | // 💾 6. Store submission summary 63 | const submission = await db.submission.create({ 64 | data: { 65 | userId, 66 | problemId, 67 | sourceCode: source_code, 68 | language: getLanguageName(language_id), 69 | stdin: stdin.join('\n'), 70 | stdout: JSON.stringify(detailedResults.map((r) => r.stdout)), 71 | stderr: detailedResults.some((r) => r.stderr) 72 | ? JSON.stringify(detailedResults.map((r) => r.stderr)) 73 | : null, 74 | compileOutput: detailedResults.some((r) => r.compile_output) 75 | ? JSON.stringify(detailedResults.map((r) => r.compile_output)) 76 | : null, 77 | status: allPassed ? 'Accepted' : 'Wrong Answer', 78 | memory: detailedResults.some((r) => r.memory) 79 | ? JSON.stringify(detailedResults.map((r) => r.memory)) 80 | : null, 81 | time: detailedResults.some((r) => r.time) 82 | ? JSON.stringify(detailedResults.map((r) => r.time)) 83 | : null, 84 | }, 85 | }); 86 | 87 | // 🏆 7. Mark problem as solved if all test cases passed 88 | if (allPassed) { 89 | await db.problemSolved.upsert({ 90 | where: { 91 | userId_problemId: { userId, problemId }, 92 | }, 93 | update: {}, 94 | create: { userId, problemId }, 95 | }); 96 | } 97 | 98 | // 📁 8. Save individual test case results using detailedResults directly 99 | const testCaseResults = detailedResults.map((result) => ({ 100 | submissionId: submission.id, 101 | testCase: result.testCase, 102 | passed: result.passed, 103 | stdout: result.stdout, 104 | expected: result.expected, 105 | stderr: result.stderr, 106 | compileOutput: result.compile_output, 107 | status: result.status, 108 | memory: result.memory, 109 | time: result.time, 110 | })); 111 | 112 | await db.testCaseResult.createMany({ data: testCaseResults }); 113 | 114 | // 🔍 9. Fetch full submission with test cases 115 | const submissionWithTestCases = await db.submission.findUnique({ 116 | where: { id: submission.id }, 117 | include: { testCases: true }, 118 | }); 119 | 120 | // 📤 10. Respond to client 121 | res.status(200).json({ 122 | success: true, 123 | message: 'Code executed successfully', 124 | submission: submissionWithTestCases, 125 | }); 126 | } catch (error) { 127 | console.error('Error executing code:', error.message); 128 | res.status(500).json({ error: 'Failed to execute code' }); 129 | } 130 | }; 131 | -------------------------------------------------------------------------------- /backend/src/controllers/playlist.controller.js: -------------------------------------------------------------------------------- 1 | import { db } from '../libs/db.js'; 2 | 3 | export const createPlayList = async (req, res) => { 4 | try { 5 | const { name, description } = req.body; 6 | const userId = req.user.id; 7 | 8 | const playList = await db.playlist.create({ 9 | data: { 10 | name, 11 | description, 12 | userId, 13 | }, 14 | }); 15 | res.status(200).json({ 16 | success: true, 17 | message: 'Playlist created successfully', 18 | playList, 19 | }); 20 | } catch (error) { 21 | console.error('Error creating playlist:', error); 22 | res.status(500).json({ error: 'Failed to create playlist' }); 23 | } 24 | }; 25 | 26 | export const getPlayAllListDetails = async (req, res) => { 27 | try { 28 | const playLists = await db.playlist.findMany({ 29 | where: { 30 | userId: req.user.id, 31 | }, 32 | include:{ 33 | problems: { 34 | include: { 35 | problem: true, 36 | }, 37 | }, 38 | } 39 | }); 40 | res.status(200).json({ 41 | success: true, 42 | message: 'Playlist fetched successfully', 43 | playLists, 44 | }); 45 | } catch (error) { 46 | console.error('Error fetching playlist:', error); 47 | res.status(500).json({ error: 'Failed to fetch playlist' }); 48 | } 49 | }; 50 | export const getPlayListDetails = async (req, res) => { 51 | const { playlistId } = req.params; 52 | 53 | try { 54 | const playList = await db.playlist.findUnique({ 55 | where: { id: playlistId , userId: req.user.id }, 56 | include: { 57 | problems: { 58 | include: { 59 | problem: true, 60 | }, 61 | }, 62 | }, 63 | }); 64 | 65 | if (!playList) { 66 | return res.status(404).json({ error: 'Playlist not found' }); 67 | } 68 | 69 | res.status(200).json({ 70 | success: true, 71 | message: 'Playlist fetched successfully', 72 | playList, 73 | }); 74 | } catch (error) { 75 | console.error('Error fetching playlist:', error); 76 | res.status(500).json({ error: 'Failed to fetch playlist' }); 77 | } 78 | }; 79 | 80 | export const addProblemToPlaylist = async (req, res) => { 81 | const { playlistId } = req.params; 82 | const { problemIds } = req.body; // Accept an array of problem IDs 83 | 84 | console.log(problemIds); 85 | try { 86 | // Ensure problemIds is an array 87 | if (!Array.isArray(problemIds) || problemIds.length === 0) { 88 | return res.status(400).json({ error: 'Invalid or missing problemIds' }); 89 | } 90 | 91 | 92 | // Create records for each problem in the playlist 93 | const problemsInPlaylist = await db.problemInPlaylist.createMany({ 94 | data: problemIds.map((problemId) => ({ 95 | playlistId, 96 | problemId, 97 | })), 98 | }); 99 | 100 | res.status(201).json({ 101 | success: true, 102 | message: 'Problems added to playlist successfully', 103 | problemsInPlaylist, 104 | }); 105 | } catch (error) { 106 | console.error('Error adding problems to playlist:', error.message); 107 | res.status(500).json({ error: 'Failed to add problems to playlist' }); 108 | } 109 | }; 110 | 111 | export const deletePlayList = async (req, res) => { 112 | const { playlistId } = req.params; 113 | 114 | try { 115 | const deletedPlaylist = await db.playlist.delete({ 116 | where: { 117 | id: playlistId, 118 | }, 119 | }); 120 | 121 | res.status(200).json({ 122 | success: true, 123 | message: 'Playlist deleted successfully', 124 | deletedPlaylist, 125 | }); 126 | } catch (error) { 127 | console.error('Error deleting playlist:', error.message); 128 | res.status(500).json({ error: 'Failed to delete playlist' }); 129 | } 130 | }; 131 | 132 | export const removeProblemFromPlaylist = async (req, res) => { 133 | const { playlistId } = req.params; 134 | const { problemIds } = req.body; 135 | 136 | try { 137 | if (!Array.isArray(problemIds) || problemIds.length === 0) { 138 | return res.status(400).json({ error: 'Invalid or missing problemIds' }); 139 | } 140 | // Only delete given problemIds not all 141 | 142 | const deletedProblem = await db.problemInPlaylist.deleteMany({ 143 | where: { 144 | playlistId, 145 | problemId: { 146 | in: problemIds, 147 | }, 148 | }, 149 | }); 150 | 151 | res.status(200).json({ 152 | success: true, 153 | message: 'Problem removed from playlist successfully', 154 | deletedProblem, 155 | }); 156 | } catch (error) { 157 | console.error('Error removing problem from playlist:', error.message); 158 | res.status(500).json({ error: 'Failed to remove problem from playlist' }); 159 | } 160 | }; 161 | -------------------------------------------------------------------------------- /backend/src/controllers/problem.controller.js: -------------------------------------------------------------------------------- 1 | import { db } from '../libs/db.js'; 2 | 3 | import { 4 | getJudge0LanguageId, 5 | getJudge0Result, 6 | pollBatchResults, 7 | submitBatch, 8 | } from '../libs/problem.libs.js'; 9 | 10 | // Final Create Problem Handler 11 | export const createProblem = async (req, res) => { 12 | const { 13 | title, 14 | description, 15 | difficulty, 16 | tags, 17 | examples, 18 | constraints, 19 | testCases, 20 | codeSnippets, 21 | referenceSolutions, 22 | } = req.body; 23 | 24 | // Step 1: Check if the requesting user is an admin 25 | if (req.user.role !== 'ADMIN') { 26 | return res.status(401).json({ error: 'Unauthorized' }); 27 | } 28 | 29 | try { 30 | // Step 2: Loop through each reference solution for different languages 31 | for (const [language, solutionCode] of Object.entries(referenceSolutions)) { 32 | // Step 2.1: Get Judge0 language ID for the current language 33 | const languageId = getJudge0LanguageId(language); 34 | if (!languageId) { 35 | return res 36 | .status(400) 37 | .json({ error: `Unsupported language: ${language}` }); 38 | } 39 | 40 | // Step 2.2: Prepare Judge0 submissions for all test cases 41 | const submissions = testCases.map(({ input, output }) => ({ 42 | source_code: solutionCode, 43 | language_id: languageId, 44 | stdin: input, 45 | expected_output: output, 46 | })); 47 | 48 | console.log('Submissions:', submissions); 49 | 50 | // TODO: CONVERT SUBMISSION TO CHUNKS OF 20 51 | 52 | // Step 2.3: Submit all test cases in one batch 53 | const submissionResults = await submitBatch(submissions); 54 | 55 | // Step 2.4: Extract tokens from response 56 | const tokens = submissionResults.map((res) => res.token); 57 | 58 | // Step 2.5: Poll Judge0 until all submissions are done 59 | const results = await pollBatchResults(tokens); 60 | 61 | // Step 2.6: Validate that each test case passed (status.id === 3) 62 | for (let i = 0; i < results.length; i++) { 63 | const result = results[i]; 64 | if (result.status.id !== 3) { 65 | return res.status(400).json({ 66 | error: `Validation failed for ${language} on input: ${submissions[i].stdin}`, 67 | details: result, 68 | }); 69 | } 70 | } 71 | } 72 | 73 | // Step 3: Save the problem in the database after all validations pass 74 | const newProblem = await db.problem.create({ 75 | data: { 76 | title, 77 | description, 78 | difficulty, 79 | tags, 80 | examples, 81 | constraints, 82 | testCases, 83 | codeSnippets, 84 | referenceSolutions, 85 | userId: req.user.id, 86 | }, 87 | }); 88 | 89 | // Step 4: Return success response with newly created problem 90 | res.status(201).json({ 91 | success: true, 92 | message: 'Problem created successfully', 93 | problem: newProblem, 94 | }); 95 | } catch (error) { 96 | console.error('Error creating problem:', error); 97 | res.status(500).json({ error: 'Failed to create problem' }); 98 | } 99 | }; 100 | 101 | export const getAllProblems = async (req, res) => { 102 | try { 103 | // Get all the problem and also check that this is solved by current user or not 104 | const problems = await db.problem.findMany({ 105 | include: { 106 | solvedBy: { 107 | where: { 108 | userId: req.user.id, 109 | }, 110 | }, 111 | }, 112 | }); 113 | res.status(200).json({ 114 | success: true, 115 | message: 'Problems fetched successfully', 116 | problems, 117 | }); 118 | } catch (error) { 119 | console.error('Error fetching problems:', error); 120 | res.status(500).json({ error: 'Failed to fetch problems' }); 121 | } 122 | }; 123 | 124 | export const getProblemById = async (req, res) => { 125 | const { id } = req.params; 126 | 127 | try { 128 | const problem = await db.problem.findUnique({ where: { id } }); 129 | if (!problem) { 130 | return res.status(404).json({ error: 'Problem not found' }); 131 | } 132 | res.status(200).json({ 133 | success: true, 134 | message: 'Problem fetched successfully', 135 | problem, 136 | }); 137 | } catch (error) { 138 | console.error('Error fetching problem:', error); 139 | res.status(500).json({ error: 'Failed to fetch problem' }); 140 | } 141 | }; 142 | 143 | export const updateProblem = async (req, res) => { 144 | try { 145 | const { id } = req.params; 146 | 147 | const { 148 | title, 149 | description, 150 | difficulty, 151 | tags, 152 | examples, 153 | constraints, 154 | testCases, 155 | codeSnippets, 156 | referenceSolutions, 157 | } = req.body; 158 | 159 | const problem = await db.problem.findUnique({ where: { id } }); 160 | 161 | if (!problem) { 162 | return res.status(404).json({ error: 'Problem not found' }); 163 | } 164 | 165 | if (req.user.role !== 'ADMIN') { 166 | return res 167 | .status(403) 168 | .json({ error: 'Forbidden: Only admin can update problems' }); 169 | } 170 | 171 | // Step 1: Validate each reference solution using testCases 172 | for (const [language, solutionCode] of Object.entries(referenceSolutions)) { 173 | const languageId = getJudge0LanguageId(language); 174 | if (!languageId) { 175 | return res 176 | .status(400) 177 | .json({ error: `Unsupported language: ${language}` }); 178 | } 179 | 180 | const submissions = testCases.map(({ input, output }) => ({ 181 | source_code: solutionCode, 182 | language_id: languageId, 183 | stdin: input, 184 | expected_output: output, 185 | })); 186 | 187 | // console.log('Submissions:', submissions); 188 | 189 | // Step 2.3: Submit all test cases in one batch 190 | const submissionResults = await submitBatch(submissions); 191 | 192 | // Step 2.4: Extract tokens from response 193 | const tokens = submissionResults.map((res) => res.token); 194 | 195 | const results = await pollBatchResults(tokens); 196 | 197 | // Step 2.6: Validate that each test case passed (status.id === 3) 198 | for (let i = 0; i < results.length; i++) { 199 | const result = results[i]; 200 | if (result.status.id !== 3) { 201 | return res.status(400).json({ 202 | error: `Validation failed for ${language} on input: ${submissions[i].stdin}`, 203 | details: result, 204 | }); 205 | } 206 | } 207 | } 208 | 209 | // Step 3. Update the problem in the database 210 | 211 | const updatedProblem = await db.problem.update({ 212 | where: { id }, 213 | data: { 214 | title, 215 | description, 216 | difficulty, 217 | tags, 218 | examples, 219 | constraints, 220 | testCases, 221 | codeSnippets, 222 | referenceSolutions, 223 | }, 224 | }); 225 | 226 | res.status(200).json({ 227 | success: true, 228 | message: 'Problem updated successfully', 229 | problem: updatedProblem, 230 | }); 231 | } catch (error) { 232 | console.error('Error creating problem:', error); 233 | res.status(500).json({ error: 'Failed to update problem' }); 234 | } 235 | }; 236 | 237 | export const deleteProblem = async (req, res) => { 238 | try { 239 | const { id } = req.params; 240 | 241 | const problem = await db.problem.findUnique({ where: { id } }); 242 | 243 | if (!problem) { 244 | return res.status(404).json({ error: 'Problem not found' }); 245 | } 246 | 247 | await db.problem.delete({ where: { id } }); 248 | 249 | res.status(200).json({ 250 | success: true, 251 | message: 'Problem deleted successfully', 252 | }); 253 | } catch (error) { 254 | console.error('Error deleting problem:', error); 255 | } 256 | }; 257 | 258 | export const getAllProblemsSolvedByUser = async (req, res) => { 259 | try { 260 | const problems = await db.problem.findMany({ 261 | where: { 262 | solvedBy: { 263 | some: { 264 | userId: req.user.id, 265 | }, 266 | }, 267 | }, 268 | include: { 269 | solvedBy: { 270 | where: { 271 | userId: req.user.id, 272 | }, 273 | }, 274 | }, 275 | }); 276 | res.status(200).json({ 277 | success: true, 278 | message: 'Problems fetched successfully', 279 | problems, 280 | }); 281 | } catch (error) { 282 | console.error('Error fetching problems:', error); 283 | res.status(500).json({ error: 'Failed to fetch problems' }); 284 | } 285 | }; 286 | -------------------------------------------------------------------------------- /backend/src/controllers/submission.controller.js: -------------------------------------------------------------------------------- 1 | import { db } from "../libs/db.js"; 2 | 3 | 4 | export const getAllSubmissions = async (req, res) => { 5 | try { 6 | const userId = req.user.id; 7 | const submissions = await db.submission.findMany({ 8 | where:{ 9 | userId: userId 10 | }, 11 | }) 12 | 13 | res.status(200).json({ 14 | success: true, 15 | message: "Submissions fetched successfully", 16 | submissions, 17 | }); 18 | } catch (error) { 19 | console.error("Fetch Submissions Error:", error); 20 | res.status(500).json({ error: "Failed to fetch submissions" }); 21 | } 22 | } 23 | 24 | 25 | export const getSubmissionsForProblem = async (req, res) => { 26 | try { 27 | const userId = req.user.id; 28 | const problemId = req.params.problemId; 29 | const submissions = await db.submission.findMany({ 30 | where:{ 31 | userId: userId, 32 | problemId: problemId 33 | }, 34 | }) 35 | 36 | res.status(200).json({ 37 | success: true, 38 | message: "Submissions fetched successfully", 39 | submissions, 40 | }); 41 | } catch (error) { 42 | console.error("Fetch Submissions Error:", error); 43 | res.status(500).json({ error: "Failed to fetch submissions" }); 44 | } 45 | } 46 | 47 | export const getAllTheSubmissionsForProblem = async (req, res) => { 48 | try { 49 | const problemId = req.params.problemId; 50 | const submissions = await db.submission.count({ 51 | where:{ 52 | problemId: problemId 53 | }, 54 | }) 55 | 56 | res.status(200).json({ 57 | success: true, 58 | message: "Submissions fetched successfully", 59 | count: submissions, 60 | }); 61 | } catch (error) { 62 | console.error("Fetch Submissions Error:", error); 63 | res.status(500).json({ error: "Failed to fetch submissions" }); 64 | } 65 | } -------------------------------------------------------------------------------- /backend/src/generated/prisma/client.d.ts: -------------------------------------------------------------------------------- 1 | export * from "./index" -------------------------------------------------------------------------------- /backend/src/generated/prisma/client.js: -------------------------------------------------------------------------------- 1 | module.exports = { ...require('.') } -------------------------------------------------------------------------------- /backend/src/generated/prisma/default.d.ts: -------------------------------------------------------------------------------- 1 | export * from "./index" -------------------------------------------------------------------------------- /backend/src/generated/prisma/default.js: -------------------------------------------------------------------------------- 1 | module.exports = { ...require('.') } -------------------------------------------------------------------------------- /backend/src/generated/prisma/edge.d.ts: -------------------------------------------------------------------------------- 1 | export * from "./default" -------------------------------------------------------------------------------- /backend/src/generated/prisma/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "prisma-client-8ab3d1df1c944320b521eb8212cabfbcc8501a835a7319c780c2e00549d4273f", 3 | "main": "index.js", 4 | "types": "index.d.ts", 5 | "browser": "index-browser.js", 6 | "exports": { 7 | "./client": { 8 | "require": { 9 | "node": "./index.js", 10 | "edge-light": "./wasm.js", 11 | "workerd": "./wasm.js", 12 | "worker": "./wasm.js", 13 | "browser": "./index-browser.js", 14 | "default": "./index.js" 15 | }, 16 | "import": { 17 | "node": "./index.js", 18 | "edge-light": "./wasm.js", 19 | "workerd": "./wasm.js", 20 | "worker": "./wasm.js", 21 | "browser": "./index-browser.js", 22 | "default": "./index.js" 23 | }, 24 | "default": "./index.js" 25 | }, 26 | "./package.json": "./package.json", 27 | ".": { 28 | "require": { 29 | "node": "./index.js", 30 | "edge-light": "./wasm.js", 31 | "workerd": "./wasm.js", 32 | "worker": "./wasm.js", 33 | "browser": "./index-browser.js", 34 | "default": "./index.js" 35 | }, 36 | "import": { 37 | "node": "./index.js", 38 | "edge-light": "./wasm.js", 39 | "workerd": "./wasm.js", 40 | "worker": "./wasm.js", 41 | "browser": "./index-browser.js", 42 | "default": "./index.js" 43 | }, 44 | "default": "./index.js" 45 | }, 46 | "./edge": { 47 | "types": "./edge.d.ts", 48 | "require": "./edge.js", 49 | "import": "./edge.js", 50 | "default": "./edge.js" 51 | }, 52 | "./react-native": { 53 | "types": "./react-native.d.ts", 54 | "require": "./react-native.js", 55 | "import": "./react-native.js", 56 | "default": "./react-native.js" 57 | }, 58 | "./extension": { 59 | "types": "./extension.d.ts", 60 | "require": "./extension.js", 61 | "import": "./extension.js", 62 | "default": "./extension.js" 63 | }, 64 | "./index-browser": { 65 | "types": "./index.d.ts", 66 | "require": "./index-browser.js", 67 | "import": "./index-browser.js", 68 | "default": "./index-browser.js" 69 | }, 70 | "./index": { 71 | "types": "./index.d.ts", 72 | "require": "./index.js", 73 | "import": "./index.js", 74 | "default": "./index.js" 75 | }, 76 | "./wasm": { 77 | "types": "./wasm.d.ts", 78 | "require": "./wasm.js", 79 | "import": "./wasm.mjs", 80 | "default": "./wasm.mjs" 81 | }, 82 | "./runtime/client": { 83 | "types": "./runtime/client.d.ts", 84 | "require": "./runtime/client.js", 85 | "import": "./runtime/client.mjs", 86 | "default": "./runtime/client.mjs" 87 | }, 88 | "./runtime/library": { 89 | "types": "./runtime/library.d.ts", 90 | "require": "./runtime/library.js", 91 | "import": "./runtime/library.mjs", 92 | "default": "./runtime/library.mjs" 93 | }, 94 | "./runtime/binary": { 95 | "types": "./runtime/binary.d.ts", 96 | "require": "./runtime/binary.js", 97 | "import": "./runtime/binary.mjs", 98 | "default": "./runtime/binary.mjs" 99 | }, 100 | "./runtime/wasm": { 101 | "types": "./runtime/wasm.d.ts", 102 | "require": "./runtime/wasm.js", 103 | "import": "./runtime/wasm.mjs", 104 | "default": "./runtime/wasm.mjs" 105 | }, 106 | "./runtime/edge": { 107 | "types": "./runtime/edge.d.ts", 108 | "require": "./runtime/edge.js", 109 | "import": "./runtime/edge-esm.js", 110 | "default": "./runtime/edge-esm.js" 111 | }, 112 | "./runtime/react-native": { 113 | "types": "./runtime/react-native.d.ts", 114 | "require": "./runtime/react-native.js", 115 | "import": "./runtime/react-native.js", 116 | "default": "./runtime/react-native.js" 117 | }, 118 | "./generator-build": { 119 | "require": "./generator-build/index.js", 120 | "import": "./generator-build/index.js", 121 | "default": "./generator-build/index.js" 122 | }, 123 | "./sql": { 124 | "require": { 125 | "types": "./sql.d.ts", 126 | "node": "./sql.js", 127 | "default": "./sql.js" 128 | }, 129 | "import": { 130 | "types": "./sql.d.ts", 131 | "node": "./sql.mjs", 132 | "default": "./sql.mjs" 133 | }, 134 | "default": "./sql.js" 135 | }, 136 | "./*": "./*" 137 | }, 138 | "version": "6.6.0", 139 | "sideEffects": false 140 | } -------------------------------------------------------------------------------- /backend/src/generated/prisma/query_engine-windows.dll.node: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aestheticsuraj234/chai-or-leetcode/0db1a64e0e1c4cd636cfb6d2f8e7f02e30796777/backend/src/generated/prisma/query_engine-windows.dll.node -------------------------------------------------------------------------------- /backend/src/generated/prisma/query_engine-windows.dll.node.tmp17532: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aestheticsuraj234/chai-or-leetcode/0db1a64e0e1c4cd636cfb6d2f8e7f02e30796777/backend/src/generated/prisma/query_engine-windows.dll.node.tmp17532 -------------------------------------------------------------------------------- /backend/src/generated/prisma/query_engine-windows.dll.node.tmp20964: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aestheticsuraj234/chai-or-leetcode/0db1a64e0e1c4cd636cfb6d2f8e7f02e30796777/backend/src/generated/prisma/query_engine-windows.dll.node.tmp20964 -------------------------------------------------------------------------------- /backend/src/generated/prisma/schema.prisma: -------------------------------------------------------------------------------- 1 | generator client { 2 | provider = "prisma-client-js" 3 | output = "../src/generated/prisma" 4 | } 5 | 6 | datasource db { 7 | provider = "postgresql" 8 | url = env("DATABASE_URL") 9 | } 10 | 11 | enum UserRole { 12 | ADMIN 13 | USER 14 | } 15 | 16 | enum Difficulty { 17 | EASY 18 | MEDIUM 19 | HARD 20 | } 21 | 22 | model User { 23 | id String @id @default(uuid()) 24 | name String? @unique 25 | email String @unique 26 | image String? 27 | role UserRole @default(USER) 28 | password String 29 | createdAt DateTime @default(now()) 30 | updatedAt DateTime @updatedAt 31 | 32 | // Relationships 33 | problems Problem[] // Problems created by this user 34 | submissions Submission[] // Submissions made by this user 35 | solvedProblems ProblemSolved[] // Problems solved by this user 36 | playlists Playlist[] // Playlists created by this user 37 | 38 | @@index([role]) 39 | } 40 | 41 | model TokenBlacklist { 42 | id String @id @default(uuid()) 43 | token String @unique 44 | expiresAt DateTime 45 | createdAt DateTime @default(now()) 46 | } 47 | 48 | model Problem { 49 | id String @id @default(uuid()) 50 | title String 51 | description String 52 | difficulty Difficulty // EASY, MEDIUM, HARD 53 | tags String[] 54 | userId String // Creator of the problem 55 | examples Json // Language-specific examples 56 | constraints String 57 | hints String? 58 | editorial String? 59 | 60 | testCases Json // Universal test cases (input/output pairs) 61 | codeSnippets Json // Language-specific starter code snippets 62 | referenceSolutions Json // Correct solutions for each supported language 63 | 64 | createdAt DateTime @default(now()) 65 | updatedAt DateTime @updatedAt 66 | 67 | // Relationships 68 | user User @relation(fields: [userId], references: [id], onDelete: Cascade) 69 | submissions Submission[] // Submissions for this problem 70 | solvedBy ProblemSolved[] // Users who have solved this problem 71 | problemsPlaylists ProblemInPlaylist[] // Playlists that include this problem 72 | 73 | @@index([difficulty]) 74 | } 75 | 76 | model Submission { 77 | id String @id @default(uuid()) 78 | userId String // User who submitted the code 79 | problemId String // Problem for which the code was submitted 80 | sourceCode Json // Submitted code 81 | language String // Programming language used 82 | stdin String? // Input provided for execution 83 | stdout String? // Output of the execution 84 | stderr String? // Error messages (if any) 85 | compileOutput String? // Compilation errors (if any) 86 | status String // Execution status (e.g., Accepted, Wrong Answer, etc.) 87 | memory String? // Memory usage 88 | time String? // Execution time 89 | createdAt DateTime @default(now()) 90 | 91 | // Relationships 92 | user User @relation(fields: [userId], references: [id], onDelete: Cascade) 93 | problem Problem @relation(fields: [problemId], references: [id], onDelete: Cascade) 94 | testCases TestCaseResult[] 95 | 96 | @@index([status]) 97 | } 98 | 99 | model TestCaseResult { 100 | id String @id @default(uuid()) 101 | submissionId String 102 | testCase Int 103 | passed Boolean 104 | stdout String? 105 | expected String 106 | stderr String? 107 | compileOutput String? 108 | status String 109 | memory String? 110 | time String? 111 | 112 | createdAt DateTime @default(now()) 113 | 114 | submission Submission @relation(fields: [submissionId], references: [id], onDelete: Cascade) 115 | 116 | @@index([submissionId]) 117 | } 118 | 119 | model ProblemSolved { 120 | id String @id @default(uuid()) 121 | userId String // User who solved the problem 122 | problemId String // Problem that was solved 123 | createdAt DateTime @default(now()) 124 | 125 | // Relationships 126 | user User @relation(fields: [userId], references: [id], onDelete: Cascade) 127 | problem Problem @relation(fields: [problemId], references: [id], onDelete: Cascade) 128 | 129 | @@unique([userId, problemId]) // Ensure a user cannot solve the same problem multiple times 130 | } 131 | 132 | model Playlist { 133 | id String @id @default(uuid()) 134 | name String 135 | description String? 136 | userId String // User who created the playlist 137 | createdAt DateTime @default(now()) 138 | updatedAt DateTime @updatedAt 139 | 140 | problems ProblemInPlaylist[] // Problems in this playlist 141 | 142 | user User @relation(fields: [userId], references: [id], onDelete: Cascade) 143 | 144 | @@unique([name, userId]) // Ensure unique playlist names per user 145 | } 146 | 147 | model ProblemInPlaylist { 148 | id String @id @default(uuid()) 149 | playlistId String 150 | problemId String 151 | createdAt DateTime @default(now()) 152 | 153 | playlist Playlist @relation(fields: [playlistId], references: [id], onDelete: Cascade) 154 | problem Problem @relation(fields: [problemId], references: [id], onDelete: Cascade) 155 | 156 | @@unique([playlistId, problemId]) // Ensure no duplicate problems in the same playlist 157 | } 158 | -------------------------------------------------------------------------------- /backend/src/generated/prisma/wasm.d.ts: -------------------------------------------------------------------------------- 1 | export * from "./index" -------------------------------------------------------------------------------- /backend/src/generated/prisma/wasm.js: -------------------------------------------------------------------------------- 1 | 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | 4 | const { 5 | Decimal, 6 | objectEnumValues, 7 | makeStrictEnum, 8 | Public, 9 | getRuntime, 10 | skip 11 | } = require('./runtime/index-browser.js') 12 | 13 | 14 | const Prisma = {} 15 | 16 | exports.Prisma = Prisma 17 | exports.$Enums = {} 18 | 19 | /** 20 | * Prisma Client JS version: 6.6.0 21 | * Query Engine version: f676762280b54cd07c770017ed3711ddde35f37a 22 | */ 23 | Prisma.prismaVersion = { 24 | client: "6.6.0", 25 | engine: "f676762280b54cd07c770017ed3711ddde35f37a" 26 | } 27 | 28 | Prisma.PrismaClientKnownRequestError = () => { 29 | const runtimeName = getRuntime().prettyName; 30 | throw new Error(`PrismaClientKnownRequestError is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}). 31 | In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`, 32 | )}; 33 | Prisma.PrismaClientUnknownRequestError = () => { 34 | const runtimeName = getRuntime().prettyName; 35 | throw new Error(`PrismaClientUnknownRequestError is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}). 36 | In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`, 37 | )} 38 | Prisma.PrismaClientRustPanicError = () => { 39 | const runtimeName = getRuntime().prettyName; 40 | throw new Error(`PrismaClientRustPanicError is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}). 41 | In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`, 42 | )} 43 | Prisma.PrismaClientInitializationError = () => { 44 | const runtimeName = getRuntime().prettyName; 45 | throw new Error(`PrismaClientInitializationError is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}). 46 | In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`, 47 | )} 48 | Prisma.PrismaClientValidationError = () => { 49 | const runtimeName = getRuntime().prettyName; 50 | throw new Error(`PrismaClientValidationError is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}). 51 | In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`, 52 | )} 53 | Prisma.Decimal = Decimal 54 | 55 | /** 56 | * Re-export of sql-template-tag 57 | */ 58 | Prisma.sql = () => { 59 | const runtimeName = getRuntime().prettyName; 60 | throw new Error(`sqltag is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}). 61 | In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`, 62 | )} 63 | Prisma.empty = () => { 64 | const runtimeName = getRuntime().prettyName; 65 | throw new Error(`empty is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}). 66 | In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`, 67 | )} 68 | Prisma.join = () => { 69 | const runtimeName = getRuntime().prettyName; 70 | throw new Error(`join is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}). 71 | In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`, 72 | )} 73 | Prisma.raw = () => { 74 | const runtimeName = getRuntime().prettyName; 75 | throw new Error(`raw is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}). 76 | In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`, 77 | )} 78 | Prisma.validator = Public.validator 79 | 80 | /** 81 | * Extensions 82 | */ 83 | Prisma.getExtensionContext = () => { 84 | const runtimeName = getRuntime().prettyName; 85 | throw new Error(`Extensions.getExtensionContext is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}). 86 | In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`, 87 | )} 88 | Prisma.defineExtension = () => { 89 | const runtimeName = getRuntime().prettyName; 90 | throw new Error(`Extensions.defineExtension is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}). 91 | In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`, 92 | )} 93 | 94 | /** 95 | * Shorthand utilities for JSON filtering 96 | */ 97 | Prisma.DbNull = objectEnumValues.instances.DbNull 98 | Prisma.JsonNull = objectEnumValues.instances.JsonNull 99 | Prisma.AnyNull = objectEnumValues.instances.AnyNull 100 | 101 | Prisma.NullTypes = { 102 | DbNull: objectEnumValues.classes.DbNull, 103 | JsonNull: objectEnumValues.classes.JsonNull, 104 | AnyNull: objectEnumValues.classes.AnyNull 105 | } 106 | 107 | 108 | 109 | /** 110 | * Enums 111 | */ 112 | 113 | exports.Prisma.TransactionIsolationLevel = makeStrictEnum({ 114 | ReadUncommitted: 'ReadUncommitted', 115 | ReadCommitted: 'ReadCommitted', 116 | RepeatableRead: 'RepeatableRead', 117 | Serializable: 'Serializable' 118 | }); 119 | 120 | exports.Prisma.UserScalarFieldEnum = { 121 | id: 'id', 122 | name: 'name', 123 | email: 'email', 124 | image: 'image', 125 | role: 'role', 126 | password: 'password', 127 | createdAt: 'createdAt', 128 | updatedAt: 'updatedAt' 129 | }; 130 | 131 | exports.Prisma.TokenBlacklistScalarFieldEnum = { 132 | id: 'id', 133 | token: 'token', 134 | expiresAt: 'expiresAt', 135 | createdAt: 'createdAt' 136 | }; 137 | 138 | exports.Prisma.ProblemScalarFieldEnum = { 139 | id: 'id', 140 | title: 'title', 141 | description: 'description', 142 | difficulty: 'difficulty', 143 | tags: 'tags', 144 | userId: 'userId', 145 | examples: 'examples', 146 | constraints: 'constraints', 147 | hints: 'hints', 148 | editorial: 'editorial', 149 | testCases: 'testCases', 150 | codeSnippets: 'codeSnippets', 151 | referenceSolutions: 'referenceSolutions', 152 | createdAt: 'createdAt', 153 | updatedAt: 'updatedAt' 154 | }; 155 | 156 | exports.Prisma.SubmissionScalarFieldEnum = { 157 | id: 'id', 158 | userId: 'userId', 159 | problemId: 'problemId', 160 | sourceCode: 'sourceCode', 161 | language: 'language', 162 | stdin: 'stdin', 163 | stdout: 'stdout', 164 | stderr: 'stderr', 165 | compileOutput: 'compileOutput', 166 | status: 'status', 167 | memory: 'memory', 168 | time: 'time', 169 | createdAt: 'createdAt' 170 | }; 171 | 172 | exports.Prisma.TestCaseResultScalarFieldEnum = { 173 | id: 'id', 174 | submissionId: 'submissionId', 175 | testCase: 'testCase', 176 | passed: 'passed', 177 | stdout: 'stdout', 178 | expected: 'expected', 179 | stderr: 'stderr', 180 | compileOutput: 'compileOutput', 181 | status: 'status', 182 | memory: 'memory', 183 | time: 'time', 184 | createdAt: 'createdAt' 185 | }; 186 | 187 | exports.Prisma.ProblemSolvedScalarFieldEnum = { 188 | id: 'id', 189 | userId: 'userId', 190 | problemId: 'problemId', 191 | createdAt: 'createdAt' 192 | }; 193 | 194 | exports.Prisma.PlaylistScalarFieldEnum = { 195 | id: 'id', 196 | name: 'name', 197 | description: 'description', 198 | userId: 'userId', 199 | createdAt: 'createdAt', 200 | updatedAt: 'updatedAt' 201 | }; 202 | 203 | exports.Prisma.ProblemInPlaylistScalarFieldEnum = { 204 | id: 'id', 205 | playlistId: 'playlistId', 206 | problemId: 'problemId', 207 | createdAt: 'createdAt' 208 | }; 209 | 210 | exports.Prisma.SortOrder = { 211 | asc: 'asc', 212 | desc: 'desc' 213 | }; 214 | 215 | exports.Prisma.JsonNullValueInput = { 216 | JsonNull: Prisma.JsonNull 217 | }; 218 | 219 | exports.Prisma.QueryMode = { 220 | default: 'default', 221 | insensitive: 'insensitive' 222 | }; 223 | 224 | exports.Prisma.NullsOrder = { 225 | first: 'first', 226 | last: 'last' 227 | }; 228 | 229 | exports.Prisma.JsonNullValueFilter = { 230 | DbNull: Prisma.DbNull, 231 | JsonNull: Prisma.JsonNull, 232 | AnyNull: Prisma.AnyNull 233 | }; 234 | exports.UserRole = exports.$Enums.UserRole = { 235 | ADMIN: 'ADMIN', 236 | USER: 'USER' 237 | }; 238 | 239 | exports.Difficulty = exports.$Enums.Difficulty = { 240 | EASY: 'EASY', 241 | MEDIUM: 'MEDIUM', 242 | HARD: 'HARD' 243 | }; 244 | 245 | exports.Prisma.ModelName = { 246 | User: 'User', 247 | TokenBlacklist: 'TokenBlacklist', 248 | Problem: 'Problem', 249 | Submission: 'Submission', 250 | TestCaseResult: 'TestCaseResult', 251 | ProblemSolved: 'ProblemSolved', 252 | Playlist: 'Playlist', 253 | ProblemInPlaylist: 'ProblemInPlaylist' 254 | }; 255 | 256 | /** 257 | * This is a stub Prisma Client that will error at runtime if called. 258 | */ 259 | class PrismaClient { 260 | constructor() { 261 | return new Proxy(this, { 262 | get(target, prop) { 263 | let message 264 | const runtime = getRuntime() 265 | if (runtime.isEdge) { 266 | message = `PrismaClient is not configured to run in ${runtime.prettyName}. In order to run Prisma Client on edge runtime, either: 267 | - Use Prisma Accelerate: https://pris.ly/d/accelerate 268 | - Use Driver Adapters: https://pris.ly/d/driver-adapters 269 | `; 270 | } else { 271 | message = 'PrismaClient is unable to run in this browser environment, or has been bundled for the browser (running in `' + runtime.prettyName + '`).' 272 | } 273 | 274 | message += ` 275 | If this is unexpected, please open an issue: https://pris.ly/prisma-prisma-bug-report` 276 | 277 | throw new Error(message) 278 | } 279 | }) 280 | } 281 | } 282 | 283 | exports.PrismaClient = PrismaClient 284 | 285 | Object.assign(exports, Prisma) 286 | -------------------------------------------------------------------------------- /backend/src/index.js: -------------------------------------------------------------------------------- 1 | import express from "express"; 2 | import cors from "cors"; 3 | import dotenv from "dotenv"; 4 | import morgan from "morgan"; 5 | import cookieParser from "cookie-parser"; 6 | 7 | import authRoutes from "./routes/auth.routes.js"; 8 | import problemRoutes from "./routes/problems.routes.js"; 9 | import executeCodeRoutes from "./routes/executeCode.routes.js" 10 | import playlistRoutes from "./routes/playlist.routes.js" 11 | import submissionRoutes from "./routes/submission.routes.js" 12 | dotenv.config(); 13 | 14 | const app = express(); 15 | 16 | // Middlewares 17 | app.use( 18 | cors({ 19 | origin: "http://localhost:5173", 20 | credentials: true, 21 | }) 22 | ); 23 | app.use(morgan("dev")); 24 | app.use(express.json()); 25 | app.use(cookieParser()); 26 | 27 | 28 | app.use("/api/v1/auth" , authRoutes); 29 | app.use("/api/v1/problems" , problemRoutes); 30 | app.use("/api/v1/execute-code" , executeCodeRoutes) 31 | app.use("/api/v1/submissions" , submissionRoutes) 32 | app.use("/api/v1/playlist" , playlistRoutes) 33 | 34 | // Start the server 35 | app.listen(process.env.PORT, () => { 36 | console.log(`Server is running on port http://localhost:${process.env.PORT}`); 37 | }); -------------------------------------------------------------------------------- /backend/src/libs/db.js: -------------------------------------------------------------------------------- 1 | import { PrismaClient } from '../generated/prisma/index.js'; 2 | 3 | const globalForPrisma = globalThis; 4 | 5 | export const db = 6 | globalForPrisma.prisma || 7 | new PrismaClient({ 8 | log: ['query', 'info', 'warn', 'error'], // Optional: Enable logging for debugging 9 | }); 10 | 11 | if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = db; -------------------------------------------------------------------------------- /backend/src/libs/problem.libs.js: -------------------------------------------------------------------------------- 1 | 2 | import axios from "axios"; 3 | 4 | export function getJudge0LanguageId(language) { 5 | const languageMap = { 6 | "PYTHON": 71, 7 | "JAVASCRIPT": 63, 8 | "JAVA": 62, 9 | "CPP": 54, 10 | "GO": 60, 11 | }; 12 | return languageMap[language.toUpperCase()]; 13 | } 14 | 15 | export function getLanguageName(languageId) { 16 | const LANGUAGE_NAMES = { 17 | 74: "TypeScript", 18 | 63: "JavaScript", 19 | 71: "Python", 20 | 62: "Java", 21 | }; 22 | return LANGUAGE_NAMES[languageId] || "Unknown"; 23 | } 24 | 25 | // Poll Judge0 for result until it's no longer "In Queue" or "Processing" 26 | export async function getJudge0Result(token) { 27 | let result; 28 | while (true) { 29 | const response = await axios.get(`${process.env.JUDGE0_API_URL}/submissions/${token}`); 30 | result = response.data; 31 | if (result.status.id !== 1 && result.status.id !== 2) break; 32 | await new Promise((resolve) => setTimeout(resolve, 1000)); 33 | } 34 | return result; 35 | } 36 | 37 | 38 | export const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); 39 | 40 | // Utility: split into chunks of max 20 for Judge0 batch 41 | export function chunkArray(arr, size = 20) { 42 | const result = []; 43 | for (let i = 0; i < arr.length; i += size) { 44 | result.push(arr.slice(i, i + size)); 45 | } 46 | return result; 47 | } 48 | 49 | // Submit batch of submissions to Judge0 50 | export async function submitBatch(submissions) { 51 | const { data } = await axios.post( 52 | `${process.env.JUDGE0_API_URL}/submissions/batch?base64_encoded=false`, 53 | { submissions } 54 | ); 55 | console.log('Batch submission response:', data); 56 | return data; // returns [{ token } , { token }] 57 | } 58 | 59 | // Poll all tokens until they are done 60 | export async function pollBatchResults(tokens) { 61 | while (true) { 62 | const { data } = await axios.get( 63 | `${process.env.JUDGE0_API_URL}/submissions/batch`, 64 | { 65 | params: { 66 | tokens: tokens.join(","), 67 | base64_encoded: false, 68 | }, 69 | } 70 | ); 71 | 72 | const results = data.submissions; 73 | 74 | const isAllDone = results.every( 75 | (r) => r.status.id !== 1 && r.status.id !== 2 76 | ); 77 | if (isAllDone) return results; 78 | 79 | await sleep(1000); // Wait before polling again 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /backend/src/middlewares/auth.middleware.js: -------------------------------------------------------------------------------- 1 | import jwt from 'jsonwebtoken'; 2 | import { db } from '../libs/db.js'; 3 | 4 | export const authenticate = async (req, res, next) => { 5 | try { 6 | const token = req.cookies.jwt; // Assuming the cookie name is 'jwt' 7 | 8 | if (!token) { 9 | return res.status(401).json({ message: "Unauthorized - No Token Provided" }); 10 | } 11 | 12 | let decoded; 13 | try { 14 | decoded = jwt.verify(token, process.env.JWT_SECRET); 15 | } catch (err) { 16 | return res.status(401).json({ message: "Unauthorized - Invalid Token" }); 17 | } 18 | 19 | const user = await db.user.findUnique({ 20 | where: { id: decoded.id }, 21 | select: { 22 | id: true, 23 | image: true, 24 | name: true, 25 | email: true, 26 | role: true, 27 | }, 28 | }); 29 | 30 | if (!user) { 31 | return res.status(404).json({ message: "User not found" }); 32 | } 33 | 34 | req.user = user; 35 | next(); 36 | } catch (error) { 37 | console.error("Error in authenticate middleware:", error.message); 38 | res.status(500).json({ message: "Internal Server Error" }); 39 | } 40 | }; 41 | 42 | export const checkAdmin = async (req, res, next) => { 43 | try { 44 | const userId = req.user.id; 45 | 46 | const user = await db.user.findUnique({ 47 | where: { id: userId }, 48 | select: { role: true }, 49 | }); 50 | 51 | if (!user || user.role !== 'ADMIN') { 52 | return res.status(403).json({ error: 'Access denied. User is not an admin.' }); 53 | } 54 | 55 | next(); 56 | } catch (error) { 57 | console.error("Error in checkAdmin middleware:", error.message); 58 | res.status(500).json({ message: "Internal Server Error" }); 59 | } 60 | }; 61 | -------------------------------------------------------------------------------- /backend/src/routes/auth.routes.js: -------------------------------------------------------------------------------- 1 | import express from "express" 2 | import {register , login, getSubmissions, getUserPlaylists, logout, checkAuth} from "../controllers/auth.controller.js" 3 | import { authenticate } from "../middlewares/auth.middleware.js" 4 | 5 | const router = express.Router() 6 | 7 | 8 | router.get("/check" , authenticate , checkAuth); 9 | 10 | router.post("/login",login) 11 | 12 | 13 | router.post("/register",register) 14 | 15 | router.post("/logout" , logout) 16 | 17 | 18 | router.get("/get-submissions",authenticate , getSubmissions) 19 | 20 | router.get("/get-playlists" , authenticate , getUserPlaylists) 21 | 22 | export default router; -------------------------------------------------------------------------------- /backend/src/routes/executeCode.routes.js: -------------------------------------------------------------------------------- 1 | import express from "express"; 2 | import { authenticate } from "../middlewares/auth.middleware.js"; 3 | import { executeCode } from "../controllers/executeCode.controller.js"; 4 | 5 | 6 | const router = express.Router(); 7 | 8 | 9 | router.post("/" , authenticate ,executeCode) 10 | 11 | 12 | export default router; -------------------------------------------------------------------------------- /backend/src/routes/playlist.routes.js: -------------------------------------------------------------------------------- 1 | import express from "express"; 2 | import { authenticate } from "../middlewares/auth.middleware.js"; 3 | import { addProblemToPlaylist, createPlayList, deletePlayList, getPlayAllListDetails, getPlayListDetails, removeProblemFromPlaylist } from "../controllers/playlist.controller.js"; 4 | 5 | const router = express.Router(); 6 | 7 | router.get("/" , authenticate , getPlayAllListDetails) 8 | 9 | router.get("/:playlistId" , authenticate , getPlayListDetails) 10 | 11 | router.post("/create-playlist" ,authenticate , createPlayList) 12 | 13 | 14 | 15 | router.post('/:playlistId/add-problem' , authenticate , addProblemToPlaylist) 16 | 17 | router.delete("/:playlistId" , authenticate , deletePlayList) 18 | 19 | router.delete("/:playlistId/remove-problem" , authenticate , removeProblemFromPlaylist) 20 | 21 | 22 | export default router; -------------------------------------------------------------------------------- /backend/src/routes/problems.routes.js: -------------------------------------------------------------------------------- 1 | import express from "express"; 2 | import { authenticate, checkAdmin } from "../middlewares/auth.middleware.js"; 3 | import { 4 | createProblem, 5 | getAllProblems, 6 | getProblemById, 7 | updateProblem, 8 | deleteProblem, 9 | getAllProblemsSolvedByUser, 10 | } from "../controllers/problem.controller.js"; 11 | 12 | const router = express.Router(); 13 | 14 | // Create a new problem (Admin only) 15 | router.post("/create-problem", authenticate, checkAdmin, createProblem); 16 | 17 | // Get all problems (Public) 18 | router.get("/get-all-problems", authenticate , getAllProblems); 19 | 20 | // Get a problem by ID (Public) 21 | router.get("/get-problem/:id", getProblemById); 22 | 23 | // Update a problem by ID (Admin only) put is used TO UPDATE ALL FIELDS AND PATCH IS USED TO UPDATE SPECIFIC FIELDS ONLY 24 | router.put("/update-problem/:id", authenticate, checkAdmin, updateProblem); 25 | 26 | // Delete a problem by ID (Admin only) 27 | router.delete("/delete-problem/:id", authenticate, checkAdmin, deleteProblem); 28 | 29 | router.get("/get-solved-problem" , authenticate , getAllProblemsSolvedByUser) 30 | 31 | export default router; -------------------------------------------------------------------------------- /backend/src/routes/submission.routes.js: -------------------------------------------------------------------------------- 1 | import express from "express"; 2 | import { authenticate } from "../middlewares/auth.middleware.js"; 3 | import { getAllSubmissions, getAllTheSubmissionsForProblem, getSubmissionsForProblem } from "../controllers/submission.controller.js"; 4 | 5 | const router = express.Router(); 6 | 7 | 8 | 9 | router.get("/get-all-submissions" , authenticate , getAllSubmissions) 10 | 11 | router.get("/get-submissions/:problemId" , authenticate , getSubmissionsForProblem) 12 | 13 | router.get("/get-submissions-count/:problemId" , authenticate , getAllTheSubmissionsForProblem) 14 | 15 | 16 | 17 | 18 | export default router; -------------------------------------------------------------------------------- /frontend/.env: -------------------------------------------------------------------------------- 1 | VITE_BACKEND_API_BASEURL= http://localhost:8080/api/v1 -------------------------------------------------------------------------------- /frontend/.env.local: -------------------------------------------------------------------------------- 1 | VITE_BACKEND_API_BASEURL= http://localhost:8080/api/v1 -------------------------------------------------------------------------------- /frontend/.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.toptal.com/developers/gitignore/api/node 2 | # Edit at https://www.toptal.com/developers/gitignore?templates=node 3 | 4 | ### Node ### 5 | # Logs 6 | logs 7 | *.log 8 | npm-debug.log* 9 | yarn-debug.log* 10 | yarn-error.log* 11 | lerna-debug.log* 12 | .pnpm-debug.log* 13 | 14 | # Diagnostic reports (https://nodejs.org/api/report.html) 15 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 16 | 17 | # Runtime data 18 | pids 19 | *.pid 20 | *.seed 21 | *.pid.lock 22 | 23 | # Directory for instrumented libs generated by jscoverage/JSCover 24 | lib-cov 25 | 26 | # Coverage directory used by tools like istanbul 27 | coverage 28 | *.lcov 29 | 30 | # nyc test coverage 31 | .nyc_output 32 | 33 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 34 | .grunt 35 | 36 | # Bower dependency directory (https://bower.io/) 37 | bower_components 38 | 39 | # node-waf configuration 40 | .lock-wscript 41 | 42 | # Compiled binary addons (https://nodejs.org/api/addons.html) 43 | build/Release 44 | 45 | # Dependency directories 46 | node_modules/ 47 | jspm_packages/ 48 | 49 | # Snowpack dependency directory (https://snowpack.dev/) 50 | web_modules/ 51 | 52 | # TypeScript cache 53 | *.tsbuildinfo 54 | 55 | # Optional npm cache directory 56 | .npm 57 | 58 | # Optional eslint cache 59 | .eslintcache 60 | 61 | # Optional stylelint cache 62 | .stylelintcache 63 | 64 | # Microbundle cache 65 | .rpt2_cache/ 66 | .rts2_cache_cjs/ 67 | .rts2_cache_es/ 68 | .rts2_cache_umd/ 69 | 70 | # Optional REPL history 71 | .node_repl_history 72 | 73 | # Output of 'npm pack' 74 | *.tgz 75 | 76 | # Yarn Integrity file 77 | .yarn-integrity 78 | 79 | # dotenv environment variable files 80 | .env 81 | .env.development.local 82 | .env.test.local 83 | .env.production.local 84 | 85 | 86 | # parcel-bundler cache (https://parceljs.org/) 87 | .cache 88 | .parcel-cache 89 | 90 | # Next.js build output 91 | .next 92 | out 93 | 94 | # Nuxt.js build / generate output 95 | .nuxt 96 | dist 97 | 98 | # Gatsby files 99 | .cache/ 100 | # Comment in the public line in if your project uses Gatsby and not Next.js 101 | # https://nextjs.org/blog/next-9-1#public-directory-support 102 | # public 103 | 104 | # vuepress build output 105 | .vuepress/dist 106 | 107 | # vuepress v2.x temp and cache directory 108 | .temp 109 | 110 | # Docusaurus cache and generated files 111 | .docusaurus 112 | 113 | # Serverless directories 114 | .serverless/ 115 | 116 | # FuseBox cache 117 | .fusebox/ 118 | 119 | # DynamoDB Local files 120 | .dynamodb/ 121 | 122 | # TernJS port file 123 | .tern-port 124 | 125 | # Stores VSCode versions used for testing VSCode extensions 126 | .vscode-test 127 | 128 | # yarn v2 129 | .yarn/cache 130 | .yarn/unplugged 131 | .yarn/build-state.yml 132 | .yarn/install-state.gz 133 | .pnp.* 134 | 135 | ### Node Patch ### 136 | # Serverless Webpack directories 137 | .webpack/ 138 | 139 | # Optional stylelint cache 140 | 141 | # SvelteKit build / generate output 142 | .svelte-kit 143 | 144 | # End of https://www.toptal.com/developers/gitignore/api/node -------------------------------------------------------------------------------- /frontend/README.md: -------------------------------------------------------------------------------- 1 | # React + Vite 2 | 3 | This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. 4 | 5 | Currently, two official plugins are available: 6 | 7 | - [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh 8 | - [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh 9 | 10 | ## Expanding the ESLint configuration 11 | 12 | If you are developing a production application, we recommend using TypeScript and enable type-aware lint rules. Check out the [TS template](https://github.com/vitejs/vite/tree/main/packages/create-vite/template-react-ts) to integrate TypeScript and [`typescript-eslint`](https://typescript-eslint.io) in your project. 13 | -------------------------------------------------------------------------------- /frontend/eslint.config.js: -------------------------------------------------------------------------------- 1 | import js from '@eslint/js' 2 | import globals from 'globals' 3 | import reactHooks from 'eslint-plugin-react-hooks' 4 | import reactRefresh from 'eslint-plugin-react-refresh' 5 | 6 | export default [ 7 | { ignores: ['dist'] }, 8 | { 9 | files: ['**/*.{js,jsx}'], 10 | languageOptions: { 11 | ecmaVersion: 2020, 12 | globals: globals.browser, 13 | parserOptions: { 14 | ecmaVersion: 'latest', 15 | ecmaFeatures: { jsx: true }, 16 | sourceType: 'module', 17 | }, 18 | }, 19 | plugins: { 20 | 'react-hooks': reactHooks, 21 | 'react-refresh': reactRefresh, 22 | }, 23 | rules: { 24 | ...js.configs.recommended.rules, 25 | ...reactHooks.configs.recommended.rules, 26 | 'no-unused-vars': ['error', { varsIgnorePattern: '^[A-Z_]' }], 27 | 'react-refresh/only-export-components': [ 28 | 'warn', 29 | { allowConstantExport: true }, 30 | ], 31 | }, 32 | }, 33 | ] 34 | -------------------------------------------------------------------------------- /frontend/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | 7 |
86 | 111 | {codeSnippets[activeIndex]} 112 |113 | 114 | {/* Blinking cursor */} 115 | 116 |
123 | {subtitle}
129 |Create your first playlist to organize problems!
63 |{playlist.description}
105 | 106 | {/* Expanded Problems List */} 107 | {expandedPlaylist === playlist.id && ( 108 |Problem | 121 |Difficulty | 122 |Tags | 123 |Action | 124 |
---|---|---|---|
{item.problem.title} | 130 |{getDifficultyBadge(item.problem.difficulty)} | 131 |
132 |
133 | {item.problem.tags && item.problem.tags.map((tag, idx) => (
134 |
140 |
135 |
138 | ))}
139 | |
141 |
142 |
146 | |
150 |
53 | {problem.description} 54 |
55 | 56 | 63 |Start solving problems to see them listed here!
56 |Problem | 70 |Difficulty | 71 |Tags | 72 |Actions | 73 |
---|---|---|---|
{problem.title} | 79 |{getDifficultyBadge(problem.difficulty)} | 80 |
81 |
82 | {problem.tags && problem.tags.map((tag, index) => (
83 |
89 |
84 |
87 | ))}
88 | |
90 |
91 |
92 |
96 |
100 | |
101 |
134 | No problems found matching your criteria. 135 |
136 |Status | 80 |Expected Output | 81 |Your Output | 82 |Memory | 83 |Time | 84 |
---|---|---|---|---|
90 | {testCase.passed ? (
91 |
92 |
95 | ) : (
96 |
97 |
100 | )}
101 | |
102 | {testCase.expected} | 103 |{testCase.stdout || 'null'} | 104 |{testCase.memory} | 105 |{testCase.time} | 106 |
31 | A Platform Inspired by Leetcode which helps you to prepare for coding interviews and helps you to improve your coding skills by solving coding problems 32 |
33 | 34 | { 35 | problems.length > 0 ?37 | No problems found 38 |
39 | ) 40 | } 41 |
57 | Sign in to your account
60 |142 | Don't have an account?{" "} 143 | 144 | Create account 145 | 146 |
147 |
59 | Sign in to your account
62 |168 | Already have an account?{" "} 169 | 170 | Sign in 171 | 172 |
173 |