├── .gitignore
├── README.md
├── api
├── app.js
├── index.js
├── modules
│ └── index.js
├── package.json
└── routes
│ └── index.js
├── judge
├── app.js
├── config.js
├── index.js
├── modules
│ ├── commands.js
│ ├── evaluateCode.js
│ ├── getData.js
│ └── runCode.js
├── package.json
└── routes
│ └── index.js
├── online-compiler
├── .eslintrc.json
├── README.md
├── components
│ ├── Login
│ │ ├── header
│ │ │ └── Header.js
│ │ └── signin
│ │ │ └── SigninComponent.js
│ ├── Problem
│ │ ├── description
│ │ │ └── DescriptionComponent.js
│ │ ├── hints
│ │ │ └── HintsComponent.js
│ │ ├── ide
│ │ │ ├── IdeComponent.js
│ │ │ └── modules.js
│ │ └── submission
│ │ │ └── SubmissionComponent.js
│ ├── loading
│ │ └── LoadingComponents.js
│ └── styles
│ │ ├── Description.module.css
│ │ ├── Header.module.css
│ │ ├── Hints.module.css
│ │ ├── Ide.module.css
│ │ ├── Signin.module.css
│ │ ├── Statement.module.css
│ │ ├── Submission.module.css
│ │ └── loading.module.css
├── modules
│ └── getData.js
├── next.config.js
├── package.json
├── pages
│ ├── [pid].js
│ ├── _app.js
│ └── index.js
├── public
│ ├── description.svg
│ ├── favicon.ico
│ └── vercel.svg
└── styles
│ ├── Home.module.css
│ ├── globals.css
│ └── pid.module.css
└── pictures
├── login.PNG
├── problem.png
├── problemList.PNG
└── screenshot.png
/.gitignore:
--------------------------------------------------------------------------------
1 | # dependencies
2 | online-compiler/node_modules
3 | online-compiler/.pnp
4 | online-compiler/.pnp.js
5 |
6 | # testing
7 | online-compiler/coverage
8 |
9 | # next.js
10 | online-compiler/.next/
11 | online-compiler/out/
12 |
13 | # production
14 | online-compiler/build
15 |
16 | # misc
17 | online-compiler/.DS_Store
18 | online-compiler/*.pem
19 |
20 | # debug
21 | online-compiler/npm-debug.log*
22 | online-compiler/yarn-debug.log*
23 | online-compiler/yarn-error.log*
24 | online-compiler/.pnpm-debug.log*
25 |
26 | # local env files
27 | online-compiler/.env.local
28 | online-compiler/.env.development.local
29 | online-compiler/.env.test.local
30 | online-compiler/.env.production.local
31 | online-compiler/config.js
32 |
33 | # vercel
34 | online-compiler/.vercel
35 | online-compiler/yarn.lock
36 |
37 | # judge
38 | judge/.env
39 | judge/package-lock.json
40 | judge/yarn.lock
41 | judge/node_modules
42 |
43 | # api
44 | api/modules/online-compiler.json
45 | api/package-lock.json
46 | api/yarn.lock
47 | api/node_modules
48 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Leetcode Clone
2 |
3 | ## Description
4 |
5 | Online Judge Compiler is Leetcode tiny version. The app is divided in **Frontend ide**, **judge api** and **problem api**.
6 |
7 | ## Setup
8 |
9 | > api
10 |
11 | Default port: 5050
12 |
13 | The api is using firebase store as database, to store test cases and evaluator of each problem.
14 |
15 | Base structure
16 |
17 | ```javascript
18 | {
19 | "problems": {
20 | "name_problem_1": {
21 | "extension_language": [ /* ONLY C++ and Javascript are available (as cpp and js) */
22 | "simple test case",
23 | "evaluator code",
24 | "correct answer",
25 | "complete test case"
26 | ],
27 | }
28 | },
29 | "statements": {
30 | "name_problem_1": {
31 | "title": "Name Problem 1",
32 | "hints": [],
33 | "input": "simple test case",
34 | "statement": "html description problem",
35 | "templates": [
36 | "solution file empty for cpp",
37 | "solution file empty for js"
38 | ],
39 | }
40 | }
41 | }
42 | ```
43 |
44 | Include Service Account Key in /api/modules as "online-compiler.json"
45 |
46 | > judge
47 |
48 | Default port: 3080
49 |
50 | Judge api needs docker installed
51 |
52 | Create .env file with the following keys
53 |
54 | ```sh
55 | PROBLEM_API='your_api_url'
56 | JUDGE_PATH='your_path_to_store_the_code_of_users'
57 | ```
58 |
59 | > Client
60 |
61 | Export Firebase SDK app in /online-compiler/config.js
62 |
63 | Default port: 3000
64 |
65 | ## Install & Run
66 |
67 | Valid for each application
68 |
69 | ```sh
70 | yarn
71 | yarn dev
72 | ```
73 |
74 | ## Preview
75 |
76 | 
77 | 
78 | 
79 | 
80 |
--------------------------------------------------------------------------------
/api/app.js:
--------------------------------------------------------------------------------
1 | import express from 'express';
2 | import cors from 'cors';
3 | import helmet from 'helmet';
4 | import routes from './routes/index.js';
5 | import firestore from './modules/index.js';
6 |
7 | const app = express();
8 |
9 | app.use(cors());
10 | app.use(helmet());
11 | app.use(express.json());
12 | app.use(express.urlencoded({ extended: true }));
13 | app.use('/', routes);
14 |
15 | (function () {
16 | const database = firestore();
17 | app.set('firestore', database);
18 | })();
19 |
20 | export default app;
21 |
--------------------------------------------------------------------------------
/api/index.js:
--------------------------------------------------------------------------------
1 | import app from './app.js';
2 |
3 | const PORT = process.env.PORT || 5050;
4 |
5 | app.listen(PORT, () => console.log('[api] Running on port', PORT));
6 |
--------------------------------------------------------------------------------
/api/modules/index.js:
--------------------------------------------------------------------------------
1 | import { initializeApp, cert } from 'firebase-admin/app';
2 | import { getFirestore } from 'firebase-admin/firestore';
3 | import serviceAcc from './online-compiler.json';
4 |
5 | export default function firestore() {
6 | initializeApp({
7 | credential: cert(serviceAcc),
8 | });
9 |
10 | const db = getFirestore();
11 |
12 | return {
13 | async getProblem (id) {
14 | const docRef = await db.collection('problems').doc(id).get();
15 | return docRef._fieldsProto;
16 | },
17 |
18 | async getStatement (id) {
19 | const docRef = await db.collection('statements').doc(id).get();
20 | return docRef._fieldsProto;
21 | },
22 |
23 | async getProblemList () {
24 | const docRef = await db.collection('statements').get();
25 | return docRef;
26 | },
27 | };
28 | }
29 |
--------------------------------------------------------------------------------
/api/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "api",
3 | "version": "1.0.0",
4 | "description": "api problems for judge",
5 | "type": "module",
6 | "main": "index.js",
7 | "scripts": {
8 | "dev": "node --experimental-json-modules index.js"
9 | },
10 | "license": "MIT",
11 | "dependencies": {
12 | "cors": "^2.8.5",
13 | "express": "^4.17.3",
14 | "firebase-admin": "^10.0.2",
15 | "helmet": "^5.0.2"
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/api/routes/index.js:
--------------------------------------------------------------------------------
1 | import express from 'express';
2 |
3 | const app = express.Router();
4 |
5 | app.get('/problems', async (req, res) => {
6 | const firestore = req.app.get('firestore');
7 |
8 | try {
9 | const problemList = await firestore.getProblemList();
10 | const statements = [];
11 |
12 | problemList.forEach((doc) => statements.push({ id: doc.id, title: doc.data().title }));
13 |
14 | res.json(statements);
15 | } catch (error) {
16 | res.sendStatus(404);
17 | }
18 | });
19 |
20 | app.get('/problem/:id', async (req, res) => {
21 | const { id } = req.params;
22 | const firestore = req.app.get('firestore');
23 |
24 | try {
25 | const statement = await firestore.getStatement(id);
26 |
27 | res.json(statement);
28 | } catch (error) {
29 | res.sendStatus(404);
30 | }
31 | });
32 |
33 | app.get('/evaluator/:id/:language/:mode', async (req, res) => {
34 | const { id, language, mode } = req.params;
35 | const firestore = req.app.get('firestore');
36 |
37 | try {
38 | const data = await firestore.getProblem(id);
39 | const evaluators = data[language].arrayValue.values;
40 |
41 | const evaluator = {
42 | evaluator: evaluators[1].stringValue.split('_n').join('\n'),
43 | correct: evaluators[2].stringValue.split('_n').join('\n'),
44 | input: mode === 'run' ? evaluators[0].stringValue : data.cpp.arrayValue.values[3].stringValue,
45 | };
46 |
47 | res.json(evaluator);
48 | } catch (error) {
49 | res.sendStatus(404);
50 | }
51 | });
52 |
53 | export default app;
54 |
--------------------------------------------------------------------------------
/judge/app.js:
--------------------------------------------------------------------------------
1 | import express from 'express';
2 | import cors from 'cors';
3 | import helmet from 'helmet';
4 | import runCodeRoute from './routes/index.js';
5 |
6 | const app = express();
7 |
8 | app.use(cors());
9 | app.use(helmet());
10 | app.use(express.json());
11 | app.use(express.urlencoded({ extended: true }));
12 | app.use('/', runCodeRoute);
13 |
14 | export default app;
15 |
--------------------------------------------------------------------------------
/judge/config.js:
--------------------------------------------------------------------------------
1 | import 'dotenv/config';
2 |
3 | const vars = {
4 | problemApi: process.env.PROBLEM_API,
5 | judgePath: process.env.JUDGE_PATH,
6 | };
7 |
8 | export default vars;
9 |
--------------------------------------------------------------------------------
/judge/index.js:
--------------------------------------------------------------------------------
1 | import app from './app.js';
2 |
3 | const PORT = process.env.PORT || 3080;
4 | app.listen(PORT, () => console.log('[server] Running on port:', PORT));
5 |
--------------------------------------------------------------------------------
/judge/modules/commands.js:
--------------------------------------------------------------------------------
1 | export default function commands(path, id) {
2 | return {
3 | cpp: `docker run --rm -m 64M --memory-swap 64M --name ${id} -v ${path}:/code -w /code cpp /bin/sh -c "g++ -Wall main.cpp -o a && ./a >&1 | tee"`,
4 | js: `docker run --rm -m 64M --memory-swap 64M --name ${id} -v ${path}:/code -w /code node:current-alpine3.15 /bin/sh -c "node main.js"`,
5 | };
6 | }
7 |
--------------------------------------------------------------------------------
/judge/modules/evaluateCode.js:
--------------------------------------------------------------------------------
1 | import { createFolder, createFiles, runCode, deleteFolder } from './runCode.js';
2 | import commands from './commands.js';
3 | import config from '../config.js';
4 |
5 | const { judgePath } = config;
6 |
7 | export default async function evaluateCode(id, code, language, { evaluator, correct, input }) {
8 | const fullPath = `${judgePath}\\${id}`;
9 |
10 | try {
11 | const cmd = commands(fullPath, id)[language];
12 |
13 | await createFolder(judgePath, id);
14 | await createFiles(
15 | fullPath,
16 | { name: `\\input.txt`, string: input },
17 | { name: `\\main.${language}`, string: evaluator },
18 | { name: `\\Correct.${language}`, string: correct },
19 | { name: `\\Solution.${language}`, string: code },
20 | );
21 | const output = await runCode(cmd, 15000, id);
22 | await deleteFolder(fullPath);
23 |
24 | return output;
25 | } catch (error) {
26 | await deleteFolder(fullPath);
27 | if (typeof error === 'string')
28 | return error;
29 |
30 | return 'Out of Memory';
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/judge/modules/getData.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 | import config from '../config.js';
3 |
4 | const { problemApi } = config;
5 |
6 | export default async function getEvaluator(id, language, mode /* run | submit */) {
7 | return axios.get(`${problemApi}/evaluator/${id}/${language}/${mode}`);
8 | }
9 |
--------------------------------------------------------------------------------
/judge/modules/runCode.js:
--------------------------------------------------------------------------------
1 | import { writeFile } from 'fs';
2 | import { exec } from 'child_process';
3 |
4 | export const createFolder = (path, name) => {
5 | return new Promise((resolve, reject) => {
6 | exec(`cd ${path} && mkdir ${name}`, (err, _, __) => {
7 | if (err) return reject(err);
8 | resolve();
9 | });
10 | });
11 | };
12 |
13 | export const createFiles = (path, input, evaluator, correct, main) => {
14 | return new Promise((resolve, reject) => {
15 | const writeSolutionFile = () => {
16 | writeFile(path.concat(main.name), main.string, (err) => {
17 | if (err) return reject(err);
18 | resolve();
19 | });
20 | };
21 |
22 | const writeCorrectFile = () => {
23 | writeFile(path.concat(correct.name), correct.string, (err) => {
24 | if (err) return reject(err);
25 | writeSolutionFile();
26 | });
27 | };
28 |
29 | const writeEvaluatorFile = () => {
30 | writeFile(path.concat(evaluator.name), evaluator.string, (err) => {
31 | if (err) return reject(err);
32 | writeCorrectFile();
33 | });
34 | };
35 |
36 | const writeInputFile = () => {
37 | writeFile(path.concat(input.name), input.string, (err) => {
38 | if (err) return reject(err);
39 | writeEvaluatorFile();
40 | });
41 | };
42 |
43 | writeInputFile();
44 | });
45 | };
46 |
47 | export const runCode = (cmd, timeout, name) => {
48 | return new Promise((resolve, reject) => {
49 | exec(cmd, { timeout, killSignal: 'SIGKILL' }, (err, stdout, stderr) => {
50 | if (stderr) {
51 | if (stderr.startsWith('docker: Error response from daemon: OCI runtime'))
52 | reject('Out of Memory 64MB');
53 | else
54 | reject(stderr);
55 | }
56 |
57 | if (err) {
58 | if (err.killed) {
59 | exec(`docker kill ${name}`);
60 | resolve(`${stdout} \nTimeout after ${timeout}ms`)
61 | } else
62 | reject(err);
63 | } else {
64 | resolve(stdout);
65 | }
66 | });
67 | });
68 | };
69 |
70 | export const deleteFolder = (path) => {
71 | return new Promise((resolve, reject) => {
72 | exec(`rm -rf ${path}`, (err, _, __) => {
73 | if (err) return reject(err);
74 | resolve();
75 | });
76 | });
77 | };
78 |
--------------------------------------------------------------------------------
/judge/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "judge",
3 | "version": "1.0.0",
4 | "description": "jugde for online compiler",
5 | "main": "index.js",
6 | "type": "module",
7 | "scripts": {
8 | "dev": "node --experimental-json-modules index.js"
9 | },
10 | "license": "MIT",
11 | "private": true,
12 | "dependencies": {
13 | "axios": "^0.26.0",
14 | "cors": "^2.8.5",
15 | "dotenv": "^16.0.0",
16 | "express": "^4.17.3",
17 | "firebase-admin": "^10.0.2",
18 | "helmet": "^5.0.2"
19 | },
20 | "devDependencies": {
21 | "nodemon": "^2.0.15"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/judge/routes/index.js:
--------------------------------------------------------------------------------
1 | import express from 'express';
2 | import getEvaluator from '../modules/getData.js';
3 | import evaluateCode from '../modules/evaluateCode.js';
4 |
5 | const app = express.Router();
6 |
7 | app.post('/', async (req, res) => {
8 | const { id, code, language, problem } = req.body;
9 |
10 | if (!id || !code || !language) { return res.sendStatus(400); }
11 |
12 | try {
13 | const getData = await getEvaluator(problem, language, 'run');
14 | const { evaluator, correct, input } = getData.data;
15 | const output = await evaluateCode(id, code, language, { evaluator, correct, input });
16 |
17 | res.json(output);
18 | } catch (error) {
19 | res.send(error);
20 | }
21 | });
22 |
23 | app.post('/judge', async (req, res) => {
24 | const { id, code, language, problem } = req.body;
25 |
26 | if (!id || !code || !language) { return res.sendStatus(400); }
27 |
28 | try {
29 | const getData = await getEvaluator(problem, language, 'submit');
30 | const { evaluator, correct, input } = getData.data;
31 | const output = await evaluateCode(id, code, language, { evaluator, correct, input });
32 |
33 | if (output.includes('correct:true'))
34 | res.send('Accepted');
35 | else if (output.includes('correct:false'))
36 | res.send('Wrong Answer');
37 | else if (output.includes('Timeout after'))
38 | res.send('Time Limited Exceded');
39 | else
40 | res.send(output);
41 | } catch (error) {
42 | await deleteFolder(fullPath);
43 | if (typeof error === 'string')
44 | return res.send(error);
45 |
46 | res.send('Out of Memory');
47 | }
48 | });
49 |
50 | export default app;
51 |
--------------------------------------------------------------------------------
/online-compiler/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "next/core-web-vitals"
3 | }
4 |
--------------------------------------------------------------------------------
/online-compiler/README.md:
--------------------------------------------------------------------------------
1 | This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
2 |
3 | ## Getting Started
4 |
5 | First, run the development server:
6 |
7 | ```bash
8 | npm run dev
9 | # or
10 | yarn dev
11 | ```
12 |
13 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
14 |
15 | You can start editing the page by modifying `pages/index.js`. The page auto-updates as you edit the file.
16 |
17 | [API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.js`.
18 |
19 | The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages.
20 |
21 | ## Learn More
22 |
23 | To learn more about Next.js, take a look at the following resources:
24 |
25 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
26 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
27 |
28 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
29 |
30 | ## Deploy on Vercel
31 |
32 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
33 |
34 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
35 |
--------------------------------------------------------------------------------
/online-compiler/components/Login/header/Header.js:
--------------------------------------------------------------------------------
1 | import { signOut } from "firebase/auth";
2 | import styles from '../../styles/Header.module.css';
3 |
4 | export default function Header ({ auth, children }) {
5 | const handleClick = (e) => {
6 | e.preventDefault();
7 | const userLogout = confirm('Are you sure?');
8 |
9 | if (userLogout) {
10 | signOut(auth);
11 | }
12 | };
13 |
14 | return (
15 |
16 |
Logout
21 | {children}
22 |
23 | );
24 | }
25 |
--------------------------------------------------------------------------------
/online-compiler/components/Login/signin/SigninComponent.js:
--------------------------------------------------------------------------------
1 | import { GoogleAuthProvider, getAuth, signInWithPopup } from 'firebase/auth';
2 | import { useState } from 'react';
3 | import { firebaseApp } from '../../../config';
4 | import styles from '../../styles/Signin.module.css';
5 |
6 | export default function Signin() {
7 | const [loading, setLoading] = useState(false);
8 |
9 | const handleClick = async () => {
10 | const auth = getAuth(firebaseApp());
11 | const googleProvider = new GoogleAuthProvider();
12 | try {
13 | setLoading(true);
14 | await signInWithPopup(auth, googleProvider);
15 | } catch (error) {
16 | console.error(error);
17 | } finally {
18 | setLoading(false);
19 | }
20 | };
21 |
22 | return (
23 |
24 |
29 |
30 | );
31 | }
32 |
--------------------------------------------------------------------------------
/online-compiler/components/Problem/description/DescriptionComponent.js:
--------------------------------------------------------------------------------
1 | import Image from 'next/image';
2 | import styles from '../../styles/Description.module.css';
3 |
4 | export default function DescriptionComponent({ children, title }) {
5 | return (
6 |
7 |
8 |
⬅ |
9 |
10 |
{title}
11 |
12 |
13 |
14 | {children}
15 |
16 |
17 | );
18 | }
19 |
--------------------------------------------------------------------------------
/online-compiler/components/Problem/hints/HintsComponent.js:
--------------------------------------------------------------------------------
1 | import { useState } from 'react';
2 | import styles from '../../styles/Hints.module.css';
3 |
4 | export default function HintsComponent({ hints = [] }) {
5 | const [open, toggleOpen] = useState(new Array(hints.length).fill(false));
6 |
7 | const handleToggle = (index) => {
8 | const copy = [...open];
9 | copy[index] = !copy[index];
10 |
11 | toggleOpen(copy);
12 | };
13 |
14 | return (
15 |
16 | {hints.map((hint, index) => (
17 | handleToggle(index)}>
18 |
19 | 💡 {open[index] ? 'Hide' : 'Show'} hint #{index + 1}
20 |
21 | {hint}
22 |
23 | ))}
24 |
25 | );
26 | }
27 |
--------------------------------------------------------------------------------
/online-compiler/components/Problem/ide/IdeComponent.js:
--------------------------------------------------------------------------------
1 | import { useState, useEffect } from 'react';
2 | import Editor from '@monaco-editor/react';
3 | import SubmissionComponent from '../submission/SubmissionComponent';
4 | import styles from '../../styles/Ide.module.css';
5 | import { runCode, submitCode } from './modules';
6 |
7 | export default function IdeComponent({ templates, input, problemId, userId }) {
8 | // editor
9 | const [language, setLanguage] = useState('cpp');
10 | const [template, setTemplate] = useState('// some code here');
11 | const [theme, setTheme] = useState('vs-light');
12 | const [themeButton, setThemeButton] = useState('🌞');
13 | const [code, setCode] = useState('');
14 | const [extension, setExtension] = useState('cpp');
15 |
16 | // submissions
17 | const [loading, setLoading] = useState(false);
18 | const [submission, setSubmission] = useState('');
19 | const [result, setResults] = useState('');
20 | const [typeSubmission, setTypeSubmission] = useState('');
21 |
22 | useEffect(() => {
23 | setTemplate(templates[0] || '// some code here');
24 | setCode(templates[0] || '// some code here');
25 | }, [templates, input, problemId, userId]);
26 |
27 | const handleChangeTheme = (e) => {
28 | e.preventDefault();
29 |
30 | if (themeButton === '🌞') {
31 | setTheme('vs-dark');
32 | setThemeButton('🌙');
33 | return;
34 | }
35 | setTheme('vs-light');
36 | setThemeButton('🌞');
37 | };
38 |
39 | const handleChangeLanguage = (e) => {
40 | const lang = e.target.value;
41 | if (lang === 'js') {
42 | setExtension('js');
43 | setLanguage('javascript');
44 | setTemplate(templates[1]);
45 | setCode(templates[1]);
46 | } else if (lang === 'cpp') {
47 | setExtension('cpp');
48 | setLanguage('cpp');
49 | setTemplate(templates[0]);
50 | setCode(templates[0]);
51 | }
52 | };
53 |
54 | const handleRunCode = async () => {
55 | try {
56 | setLoading(true);
57 | await runCode(
58 | code,
59 | extension,
60 | problemId,
61 | setSubmission,
62 | setResults,
63 | setTypeSubmission,
64 | userId,
65 | );
66 | } finally {
67 | setLoading(false);
68 | }
69 | };
70 |
71 | const handleSubmitCode = async () => {
72 | try {
73 | setLoading(true);
74 | await submitCode(
75 | code,
76 | extension,
77 | problemId,
78 | setSubmission,
79 | setResults,
80 | setTypeSubmission,
81 | userId,
82 | );
83 | } finally {
84 | setLoading(false);
85 | }
86 | };
87 |
88 | return (
89 |
90 |
91 |
92 |
101 |
102 |
{themeButton}
103 |
104 |
105 |
setCode(value)}
112 | />
113 |
114 |
115 |
116 |
122 |
128 |
129 |
130 |
131 |
132 |
133 | {submission.length > 1 ? (
134 |
140 | ) : null}
141 |
142 | );
143 | }
144 |
--------------------------------------------------------------------------------
/online-compiler/components/Problem/ide/modules.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 |
3 | const uri = 'http://localhost:3080';
4 |
5 | export async function submitCode(code, language, problem, setSubmission, setResults, setTypeSubmission, userId) {
6 | setTypeSubmission('submit');
7 | setSubmission('Pending');
8 | try {
9 | const output = await axios.post(`${uri}/judge`, {
10 | id: userId,
11 | code,
12 | language,
13 | problem,
14 | });
15 |
16 | const outputData = output.data;
17 | if (outputData === 'Time Limited Exceded' || outputData === 'Accepted' || outputData === 'Wrong Answer' || outputData === 'Out of Memory') {
18 | setSubmission(outputData);
19 | setResults(null);
20 | } else {
21 | setSubmission('Runtime Error');
22 | setResults(outputData);
23 | }
24 | } catch (err) {
25 | setSubmission('Server Error');
26 | setResults(null);
27 | }
28 | }
29 |
30 | export async function runCode(code, language, problem, setSubmission, setResults, setTypeSubmission, userId) {
31 | setTypeSubmission('run');
32 | setSubmission('Pending');
33 | try {
34 | const output = await axios.post(`${uri}`, {
35 | id: userId,
36 | code,
37 | language,
38 | problem,
39 | });
40 |
41 | let outputData = output.data;
42 |
43 | if (output.data.includes('Timeout after')) {
44 | setSubmission('Time Limited Exceded');
45 | } else {
46 | if (output.data.startsWith('solution:')) {
47 | if (output.data.includes('correct:true')) {
48 | setSubmission('Accepted');
49 | outputData = outputData
50 | .replace('solution:', '')
51 | .replace('correct:true', '');
52 | } else if (output.data.includes('correct:false')) {
53 | setSubmission('Wrong Answer');
54 | outputData = outputData
55 | .replace('solution:', '')
56 | .replace('correct:false', '');
57 | }
58 | } else {
59 | if (output.data === 'Out of Memory') {
60 | setSubmission('Out of Memory');
61 | } else {
62 | setSubmission('Runtime Error');
63 | }
64 | }
65 | }
66 |
67 | setResults(outputData.replace('solution:', ''));
68 | } catch (error) {
69 | console.log(error);
70 | setSubmission('Server Error');
71 | setResults(null);
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/online-compiler/components/Problem/submission/SubmissionComponent.js:
--------------------------------------------------------------------------------
1 | import styles from '../../styles/Submission.module.css';
2 |
3 | export default function SubmissionComponent ({ status, input, output, type }) {
4 | // status -> Pending | TimeLimitedExceded | RuntimeError | WrongAnswer | Accepted | Out of Memory
5 | const style = status.split(' ').join('');
6 | const typeSubmission = type[0].toUpperCase().concat(type.substring(1));
7 |
8 | return (
9 |
10 |
{typeSubmission} Code Status: {status}
11 |
12 | {status !== 'Pending' && !!output ? (
13 |
14 | {type === 'run' ? (
15 |
16 |
Input
17 |
{input.split('\n').map((str,i) =>
{str}
)}
18 |
19 | ) : null}
20 |
21 | {output ? (
22 |
23 |
Your Answer
24 |
{output.split('\n').map((str,i) =>
{str}
)}
25 |
26 | ) : null}
27 |
28 | ) : null}
29 |
30 | );
31 | }
32 |
--------------------------------------------------------------------------------
/online-compiler/components/loading/LoadingComponents.js:
--------------------------------------------------------------------------------
1 | import styles from '../styles/loading.module.css';
2 |
3 | export default function Loading() {
4 | return (
5 |
8 | );
9 | }
--------------------------------------------------------------------------------
/online-compiler/components/styles/Description.module.css:
--------------------------------------------------------------------------------
1 | .title {
2 | padding-top: 2%;
3 | margin-top: 0;
4 | margin-left: 10px;
5 | }
6 |
7 | .problem {
8 | padding: 15px;
9 | border-radius: 10px;
10 | background-color: white;
11 | }
12 |
13 | .title_container {
14 | display: flex;
15 | flex-direction: row;
16 | align-items: center;
17 | }
18 |
--------------------------------------------------------------------------------
/online-compiler/components/styles/Header.module.css:
--------------------------------------------------------------------------------
1 | .button {
2 | float: right;
3 | margin: 20px 20px;
4 | font-weight: bold;
5 | }
--------------------------------------------------------------------------------
/online-compiler/components/styles/Hints.module.css:
--------------------------------------------------------------------------------
1 | .details {
2 | margin-top: 10px;
3 | margin-bottom: 10px;
4 | padding: 10px;
5 | background-color: white;
6 | border: 1px solid #cccccc;
7 | border-radius: 3px;
8 | }
9 |
10 | .details:hover {
11 | cursor: pointer;
12 | border: 1px solid #10009c;
13 | border-radius: 3px;
14 | }
15 |
16 | .details:hover > .summary {
17 | color: #10009c;
18 | }
19 |
--------------------------------------------------------------------------------
/online-compiler/components/styles/Ide.module.css:
--------------------------------------------------------------------------------
1 | .config {
2 | margin-top: 10px;
3 | margin-bottom: 10px;
4 | display: flex;
5 | justify-content: space-between;
6 | }
7 |
8 | .line {
9 | border: 1px solid #cccccc;
10 | }
11 |
12 | .select {
13 | padding: 10px;
14 | border: 1px solid #cccccc;
15 | border-radius: 3px;
16 | font-weight: bold;
17 | }
18 |
19 | .select:hover {
20 | cursor: pointer;
21 | }
22 |
23 | .theme {
24 | padding: 10px;
25 | background-color: white;
26 | border: 1px solid #cccccc;
27 | border-radius: 3px;
28 | }
29 |
30 | .theme:hover {
31 | background-color: #cccccc;
32 | }
33 |
34 | .compile {
35 | margin-top: 10px;
36 | display: flex;
37 | justify-content: flex-end;
38 | }
39 |
40 | .buttons {
41 | margin-bottom: 10px;
42 | }
43 |
44 | .run_button {
45 | border: 1px solid #cccccc;
46 | padding: 10px;
47 | border-radius: 100px;
48 | background-color: white;
49 | margin-right: 10px;
50 | font-size: 14px;
51 | font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, Courier, monospace;
52 | }
53 |
54 | .run_button:hover {
55 | cursor: pointer;
56 | background-color: #cccccc;
57 | }
58 |
59 | .submit_button {
60 | border: 1px solid #10009c;
61 | padding: 10px;
62 | border-radius: 100px;
63 | background-color: #10009c;
64 | color: white;
65 | font-size: 14px;
66 | font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, Courier, monospace;
67 | }
68 |
69 | .submit_button:hover {
70 | cursor: pointer;
71 | background-color: #3c2fb3;
72 | }
73 |
--------------------------------------------------------------------------------
/online-compiler/components/styles/Signin.module.css:
--------------------------------------------------------------------------------
1 | .container {
2 | min-height: 100vh;
3 | display: flex;
4 | justify-content: center;
5 | align-items: center;
6 | }
7 |
8 | .button {
9 | border: none;
10 | padding: 15px 30px;
11 | color: white;
12 | background: tomato;
13 | cursor: pointer;
14 | border-radius: 8px;
15 | font-weight: bold;
16 | font-size: 18px;
17 | }
18 |
--------------------------------------------------------------------------------
/online-compiler/components/styles/Statement.module.css:
--------------------------------------------------------------------------------
1 | .code {
2 | padding: 10px;
3 | margin-top: 10px;
4 | background-color: #F5F5F5;
5 | border: 1px solid #cccccc;
6 | border-radius: 3px;
7 | font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, Courier, monospace;
8 | font-size: 14px;
9 | }
10 |
11 | .sub {
12 | font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, Courier, monospace;
13 | color: #C7254E;
14 | background-color: #F9F2F4;
15 | border-radius: 3px;
16 | }
17 |
--------------------------------------------------------------------------------
/online-compiler/components/styles/Submission.module.css:
--------------------------------------------------------------------------------
1 | .container {
2 | margin-top: 20px;
3 | background-color: white;
4 | padding: 15px;
5 | border-radius: 10px;
6 | }
7 |
8 | .Accepted { color: green; }
9 | .Pending { color: blue; }
10 | .TimeLimitedExceded, .RuntimeError, .WrongAnswer, .ServerError, .OutofMemory { color: red; }
11 |
12 | .code {
13 | padding: 10px;
14 | margin-top: 10px;
15 | background-color: #F5F5F5;
16 | border: 1px solid #cccccc;
17 | border-radius: 3px;
18 | font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, Courier, monospace;
19 | font-size: 14px;
20 | }
21 |
22 | .results div {
23 | margin-top: 15px;
24 | }
25 |
--------------------------------------------------------------------------------
/online-compiler/components/styles/loading.module.css:
--------------------------------------------------------------------------------
1 | .loading {
2 | display: inline-block;
3 | position: relative;
4 | width: 80px;
5 | height: 80px;
6 | }
7 | .loading div {
8 | display: inline-block;
9 | position: absolute;
10 | left: 8px;
11 | width: 16px;
12 | background: grey;
13 | animation: loading 1.2s cubic-bezier(0, 0.5, 0.5, 1) infinite;
14 | }
15 | .loading div:nth-child(1) {
16 | left: 8px;
17 | animation-delay: -0.24s;
18 | }
19 | .loading div:nth-child(2) {
20 | left: 32px;
21 | animation-delay: -0.12s;
22 | }
23 | .loading div:nth-child(3) {
24 | left: 56px;
25 | animation-delay: 0;
26 | }
27 | @keyframes loading {
28 | 0% {
29 | top: 8px;
30 | height: 64px;
31 | }
32 | 50%, 100% {
33 | top: 24px;
34 | height: 32px;
35 | }
36 | }
--------------------------------------------------------------------------------
/online-compiler/modules/getData.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 |
3 | export const getProblemList = async (setStatements, setLoading) => {
4 | try {
5 | const res = await axios.get('http://localhost:5050/problems');
6 | setStatements([...res.data]);
7 | } catch (error) {
8 | console.error(error);
9 | } finally {
10 | setLoading(false);
11 | }
12 | };
13 |
14 | export const getProblem = async (setLoading, setStatement, pid) => {
15 | if (!pid) return;
16 |
17 | try {
18 | const res = await axios.get(`http://localhost:5050/problem/${pid}`);
19 | setStatement({
20 | title: res.data.title.stringValue,
21 | statement: res.data.statement.stringValue,
22 | hints: res.data.hints.arrayValue.values.map((value) => value.stringValue),
23 | templates: res.data.templates.arrayValue.values.map(
24 | (value) => value.stringValue.split('_n').join('\n'),
25 | ),
26 | input: res.data.input.stringValue.split('_n').join('\n'),
27 | });
28 | } catch (error) {
29 | console.error(error);
30 | } finally {
31 | setLoading(false);
32 | }
33 | };
34 |
--------------------------------------------------------------------------------
/online-compiler/next.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('next').NextConfig} */
2 | const nextConfig = {
3 | reactStrictMode: true,
4 | }
5 |
6 | module.exports = nextConfig
7 |
--------------------------------------------------------------------------------
/online-compiler/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "online-compiler",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev",
7 | "build": "next build",
8 | "start": "next start",
9 | "lint": "next lint"
10 | },
11 | "dependencies": {
12 | "@monaco-editor/react": "^4.3.1",
13 | "axios": "^0.26.0",
14 | "firebase": "^9.6.7",
15 | "next": "12.1.0",
16 | "react": "17.0.2",
17 | "react-dom": "17.0.2"
18 | },
19 | "devDependencies": {
20 | "eslint": "8.9.0",
21 | "eslint-config-next": "12.1.0"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/online-compiler/pages/[pid].js:
--------------------------------------------------------------------------------
1 | import { useRouter } from 'next/router';
2 | import { useEffect, useState } from 'react';
3 | import { getProblem } from '../modules/getData';
4 | import styles from '../styles/pid.module.css'
5 | import DescriptionComponent from '../components/Problem/description/DescriptionComponent';
6 | import HintsComponent from '../components/Problem/hints/HintsComponent';
7 | import IdeComponent from '../components/Problem/ide/IdeComponent';
8 | import LoadingComponent from '../components/loading/LoadingComponents';
9 |
10 | export default function Problem({ userId }) {
11 | const router = useRouter();
12 | const { pid } = router.query;
13 |
14 | const [loading, setLoading] = useState(true);
15 | const [statement, setStatement] = useState({});
16 |
17 | useEffect(() => {
18 | getProblem(setLoading, setStatement, pid);
19 | }, [pid]);
20 |
21 | return (
22 |
23 | {!!statement.title && !loading ? (
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
37 |
38 | ) : (
39 |
40 |
41 |
42 | )}
43 |
44 | );
45 | }
46 |
--------------------------------------------------------------------------------
/online-compiler/pages/_app.js:
--------------------------------------------------------------------------------
1 | import { getAuth, onAuthStateChanged } from 'firebase/auth';
2 | import { useEffect, useState } from 'react';
3 | import { firebaseApp } from '../config';
4 | import '../styles/globals.css'
5 | import Signin from '../components/Login/signin/SigninComponent';
6 | import Header from '../components/Login/header/Header';
7 | import Loading from '../components/loading/LoadingComponents';
8 |
9 | function MyApp({ Component, pageProps }) {
10 | const [loading, setLoading] = useState(true);
11 | const [user, setUser] = useState(null);
12 | const [authApp, setAuthApp] = useState(null);
13 |
14 | useEffect(() => {
15 | firebaseApp();
16 | const auth = getAuth();
17 | setAuthApp(auth);
18 |
19 | onAuthStateChanged(auth, (user) => {
20 | setUser(user);
21 | setLoading(false);
22 | });
23 | }, []);
24 |
25 | const signinOrHome = user ? (
26 |
29 | ) : ;
30 |
31 | return (
32 |
33 | {loading ? (
34 |
35 |
36 |
37 | ) : signinOrHome}
38 |
39 | );
40 | }
41 |
42 | export default MyApp;
43 |
--------------------------------------------------------------------------------
/online-compiler/pages/index.js:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from 'react';
2 | import styles from '../styles/Home.module.css'
3 | import LoadingComponent from '../components/loading/LoadingComponents';
4 | import { getProblemList } from '../modules/getData';
5 |
6 | export default function Home() {
7 | const [loading, setLoading] = useState(true);
8 | const [statements, setStatements] = useState([]);
9 |
10 | useEffect(() => {
11 | getProblemList(setStatements, setLoading);
12 | }, []);
13 |
14 | return (
15 |
16 |
17 | Problems
18 |
19 | {loading ? (
20 |
21 |
22 |
23 | ) : (
24 |
25 | {statements.map((statement) => {
26 | return (
27 |
30 | );
31 | })}
32 |
33 | )}
34 |
35 | )
36 | }
37 |
--------------------------------------------------------------------------------
/online-compiler/public/description.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/online-compiler/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/juaniviola/leetcode_clone/c49f63ac750d2fdbb79c4a430b07e096d84ce91c/online-compiler/public/favicon.ico
--------------------------------------------------------------------------------
/online-compiler/public/vercel.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/online-compiler/styles/Home.module.css:
--------------------------------------------------------------------------------
1 | .container {
2 | min-height: 100vh;
3 | height: 100%;
4 | background-color: #F5F5F5;
5 | padding: 0 5%;
6 | padding-bottom: 2%;
7 | }
8 |
9 | .loading {
10 | text-align: center;
11 | }
12 |
13 | .title {
14 | font-weight: bold;
15 | font-size: 24px;
16 | padding-top: 50px;
17 | margin-bottom: 30px;
18 | }
19 |
20 | .problem {
21 | padding: 10px;
22 | border: 1px solid black;
23 | background-color: white;
24 | }
25 |
26 | .problem:first-of-type {
27 | border-top-left-radius: 5px;
28 | border-top-right-radius: 5px;
29 | }
30 |
31 | .problem:last-of-type {
32 | border-bottom-left-radius: 5px;
33 | border-bottom-right-radius: 5px;
34 | }
35 |
--------------------------------------------------------------------------------
/online-compiler/styles/globals.css:
--------------------------------------------------------------------------------
1 | html,
2 | body {
3 | padding: 0;
4 | margin: 0;
5 | font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
6 | Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
7 | }
8 |
9 | body {
10 | min-height: 100vh;
11 | }
12 |
13 | a {
14 | color: inherit;
15 | text-decoration: none;
16 | }
17 |
18 | * {
19 | box-sizing: border-box;
20 | }
21 |
--------------------------------------------------------------------------------
/online-compiler/styles/pid.module.css:
--------------------------------------------------------------------------------
1 | .container {
2 | min-height: 100vh;
3 | background-color: #F5F5F5;
4 | padding: 0 5%;
5 | padding-bottom: 2%;
6 | }
7 |
8 | .loading {
9 | display: flex;
10 | justify-content: center;
11 | align-items: center;
12 | }
13 |
--------------------------------------------------------------------------------
/pictures/login.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/juaniviola/leetcode_clone/c49f63ac750d2fdbb79c4a430b07e096d84ce91c/pictures/login.PNG
--------------------------------------------------------------------------------
/pictures/problem.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/juaniviola/leetcode_clone/c49f63ac750d2fdbb79c4a430b07e096d84ce91c/pictures/problem.png
--------------------------------------------------------------------------------
/pictures/problemList.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/juaniviola/leetcode_clone/c49f63ac750d2fdbb79c4a430b07e096d84ce91c/pictures/problemList.PNG
--------------------------------------------------------------------------------
/pictures/screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/juaniviola/leetcode_clone/c49f63ac750d2fdbb79c4a430b07e096d84ce91c/pictures/screenshot.png
--------------------------------------------------------------------------------