├── Procfile
├── .gitignore
├── Images
├── Blogs.PNG
├── contest.PNG
├── homepage.PNG
├── profile.PNG
└── championship.PNG
├── gif
└── codencollab.gif
├── src
├── db
│ └── mongoose.js
├── Function
│ ├── getKey.js
│ ├── Oauth2.js
│ └── compilerFunc.js
├── models
│ ├── Reply.js
│ ├── Comment.js
│ ├── Blogs.js
│ └── User.js
├── middleware
│ └── Auth.js
├── socketFunc
│ ├── chat.js
│ ├── Compile.js
│ ├── problem.js
│ ├── Contest-Join.js
│ └── userJoin.js
├── index.js
├── routers
│ ├── OauthRouter.js
│ ├── UserRouter.js
│ ├── ReplyRouter.js
│ ├── BlogRouter.js
│ └── CommentRouter.js
└── utils
│ ├── Users.js
│ └── Contest.js
├── package.json
├── .github
├── workflows
│ └── node.js.yml
└── pull_request_template.md
├── CONTRIBUTING.md
├── CODE_OF_CONDUCT.md
├── README.md
└── LICENSE
/Procfile:
--------------------------------------------------------------------------------
1 | web: node src/index.js
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | Configs
3 | nodemon.json
4 | .env
--------------------------------------------------------------------------------
/Images/Blogs.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/atharmohammad/Code-N-Collab-Server/HEAD/Images/Blogs.PNG
--------------------------------------------------------------------------------
/Images/contest.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/atharmohammad/Code-N-Collab-Server/HEAD/Images/contest.PNG
--------------------------------------------------------------------------------
/Images/homepage.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/atharmohammad/Code-N-Collab-Server/HEAD/Images/homepage.PNG
--------------------------------------------------------------------------------
/Images/profile.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/atharmohammad/Code-N-Collab-Server/HEAD/Images/profile.PNG
--------------------------------------------------------------------------------
/gif/codencollab.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/atharmohammad/Code-N-Collab-Server/HEAD/gif/codencollab.gif
--------------------------------------------------------------------------------
/Images/championship.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/atharmohammad/Code-N-Collab-Server/HEAD/Images/championship.PNG
--------------------------------------------------------------------------------
/src/db/mongoose.js:
--------------------------------------------------------------------------------
1 | const mongoose = require("mongoose");
2 | mongoose.connect(
3 | process.env.MONGO_DB_URL,
4 | { useNewUrlParser: true, useCreateIndex: true },
5 | (e, r) => {
6 | if (e) {
7 | return console.log("error", e);
8 | }
9 | console.log("connected");
10 | }
11 | );
12 |
--------------------------------------------------------------------------------
/src/Function/getKey.js:
--------------------------------------------------------------------------------
1 | let idx = -1;
2 |
3 | const keys = [
4 | [process.env.COMPILE_CLIENT_ID1, process.env.COMPILE_CLIENT_SECRET1],
5 | [process.env.COMPILE_CLIENT_ID2, process.env.COMPILE_CLIENT_SECRET2],
6 | [process.env.COMPILE_CLIENT_ID3, process.env.COMPILE_CLIENT_SECRET3],
7 | [process.env.COMPILE_CLIENT_ID4, process.env.COMPILE_CLIENT_SECRET4],
8 | ];
9 |
10 | const sz = keys.length;
11 |
12 | const AwesomeKey = () => {
13 | idx++;
14 | if (idx == sz) idx = 0;
15 |
16 | return keys[idx];
17 | };
18 |
19 | module.exports = {
20 | AwesomeKey,
21 | };
22 |
--------------------------------------------------------------------------------
/src/models/Reply.js:
--------------------------------------------------------------------------------
1 | const mongoose = require("mongoose");
2 |
3 | const replySchema = new mongoose.Schema(
4 | {
5 | Body: {
6 | type: String,
7 | },
8 | User: {
9 | type: mongoose.Schema.Types.ObjectId,
10 | required: true,
11 | ref: "User",
12 | },
13 | Comment: {
14 | type: mongoose.Schema.Types.ObjectId,
15 | required: true,
16 | ref: "Comment",
17 | },
18 | Likes: [
19 | {
20 | type: mongoose.Schema.Types.ObjectId,
21 | ref: "User",
22 | required: true,
23 | },
24 | ],
25 | Deleted: {
26 | type: Boolean,
27 | default: false,
28 | },
29 | },
30 | {
31 | timestamps: true,
32 | }
33 | );
34 |
35 | const table = mongoose.model("Reply", replySchema);
36 |
37 | module.exports = table;
38 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "codencollab-server",
3 | "version": "1.0.0",
4 | "description": "Code-n-Collab Server",
5 | "main": "index.js",
6 | "scripts": {
7 | "start": "node src/index.js",
8 | "dev": "nodemon src/index.js"
9 | },
10 | "author": "Mohd Athar / Adnan Shamsi",
11 | "license": "ISC",
12 | "dependencies": {
13 | "axios": "^0.21.1",
14 | "bcryptjs": "^2.4.3",
15 | "cors": "^2.8.5",
16 | "dotenv": "^10.0.0",
17 | "express": "^4.17.1",
18 | "googleapis": "^74.2.0",
19 | "jsonwebtoken": "^8.5.1",
20 | "mongoose": "^5.12.12",
21 | "puppeteer": "^9.0.0",
22 | "request": "^2.88.2",
23 | "request-promise": "^4.2.6",
24 | "socket.io": "^4.0.1",
25 | "validator": "^13.6.0"
26 | },
27 | "devDependencies": {
28 | "nodemon": "^2.0.7"
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/middleware/Auth.js:
--------------------------------------------------------------------------------
1 | const jwt = require("jsonwebtoken");
2 | const Users = require("../models/User");
3 | const mongoose = require("mongoose");
4 |
5 | const auth = async (req, res, next) => {
6 | try {
7 | const token = req.header("Authorization").split(" ")[1];
8 | const decodedKey = jwt.verify(token, process.env.JWT_SECRET);
9 |
10 | const user = await Users.findOne({
11 | _id: decodedKey._id,
12 | token: token,
13 | Deleted: false,
14 | });
15 | if (!user) throw new Error();
16 |
17 | req.token = token;
18 | req.user = user;
19 | req.Admin = false;
20 |
21 | if(req.user.SuperUser){
22 | req.Admin = true;
23 | }
24 |
25 | next();
26 | } catch (e) {
27 | res.status(401).send({ error: "Please Authenticate !" });
28 | }
29 | };
30 |
31 | module.exports = auth;
32 |
--------------------------------------------------------------------------------
/src/socketFunc/chat.js:
--------------------------------------------------------------------------------
1 | const { getUser } = require("../utils/Users");
2 |
3 | module.exports = function (io) {
4 | try {
5 | io.on("connection", (socket) => {
6 | socket.on("clientMsg", ({ message }) => {
7 | try {
8 | const user = getUser(socket.id);
9 | if (!user) {
10 | return;
11 | }
12 | const data = { text: message, user: user.username };
13 |
14 | io.to(user.room).emit("serverMsg", data);
15 | } catch (e) {
16 | console.log(e);
17 | }
18 | });
19 | socket.on("Contest-Msg", ({ message, room, name }) => {
20 | try {
21 | const data = { text: message, user: name };
22 | io.to(room).emit("serverMsg", data);
23 | } catch (e) {
24 | console.log(e);
25 | }
26 | });
27 | });
28 | } catch (e) {
29 | console.log(e);
30 | }
31 | };
32 |
--------------------------------------------------------------------------------
/.github/workflows/node.js.yml:
--------------------------------------------------------------------------------
1 | # This workflow will do a clean install of node dependencies, cache/restore them, build the source code and run tests across different versions of node
2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
3 |
4 | name: Node.js CI
5 |
6 | on:
7 | push:
8 | branches: [ master ]
9 | pull_request:
10 | branches: [ master ]
11 |
12 | jobs:
13 | build:
14 |
15 | runs-on: ubuntu-latest
16 |
17 | strategy:
18 | matrix:
19 | node-version: [12.x, 14.x, 16.x]
20 | # See supported Node.js release schedule at https://nodejs.org/en/about/releases/
21 |
22 | steps:
23 | - uses: actions/checkout@v2
24 | - name: Use Node.js ${{ matrix.node-version }}
25 | uses: actions/setup-node@v2
26 | with:
27 | node-version: ${{ matrix.node-version }}
28 | cache: 'npm'
29 | - run: npm ci
30 | - run: npm run build --if-present
31 | - run: npm test
32 |
--------------------------------------------------------------------------------
/src/models/Comment.js:
--------------------------------------------------------------------------------
1 | const mongoose = require("mongoose");
2 |
3 | const commentSchema = new mongoose.Schema(
4 | {
5 | Body: {
6 | type: String,
7 | required: true,
8 | },
9 | Blog: {
10 | type: mongoose.Schema.Types.ObjectId,
11 | required: true,
12 | ref: "Blog",
13 | },
14 | User: {
15 | type: mongoose.Schema.Types.ObjectId,
16 | required: true,
17 | ref: "User",
18 | },
19 | Deleted: {
20 | type: Boolean,
21 | default: false,
22 | },
23 | Likes: [
24 | {
25 | type: mongoose.Schema.Types.ObjectId,
26 | ref: "User",
27 | required: true,
28 | },
29 | ],
30 | Replies: [
31 | {
32 | type: mongoose.Schema.Types.ObjectId,
33 | ref: "Reply",
34 | required: true,
35 | },
36 | ],
37 | },
38 | {
39 | timestamps: true,
40 | }
41 | );
42 |
43 | const table = mongoose.model("Comment", commentSchema);
44 |
45 | module.exports = table;
46 |
--------------------------------------------------------------------------------
/src/models/Blogs.js:
--------------------------------------------------------------------------------
1 | const mongoose = require("mongoose");
2 | const validator = require("validator");
3 | const Comment = require("./Comment");
4 | const User = require("./User");
5 |
6 | const blogSchema = new mongoose.Schema(
7 | {
8 | Body: {
9 | type: String,
10 | required: true,
11 | },
12 | User: {
13 | type: mongoose.Schema.Types.ObjectId,
14 | required: true,
15 | ref: "User",
16 | },
17 | Deleted: {
18 | type: Boolean,
19 | default: false,
20 | },
21 | LikesLength:{
22 | type:Number,
23 | default:0
24 | },
25 | Likes: [
26 | {
27 | type: mongoose.Schema.Types.ObjectId,
28 | ref: "User",
29 | required: true,
30 | },
31 | ],
32 | Comments: [
33 | {
34 | type: mongoose.Schema.Types.ObjectId,
35 | ref: "Comment",
36 | required: true,
37 | },
38 | ],
39 | },
40 | {
41 | timestamps: true,
42 | }
43 | );
44 |
45 | const table = mongoose.model("Blog", blogSchema);
46 |
47 | module.exports = table;
48 |
--------------------------------------------------------------------------------
/src/Function/Oauth2.js:
--------------------------------------------------------------------------------
1 | const axios = require("axios");
2 |
3 | function getAuthUrl() {
4 | //defining the access scopes for google oauth
5 | const scopes = [
6 | "https://www.googleapis.com/auth/userinfo.profile",
7 | "https://www.googleapis.com/auth/userinfo.email",
8 | ];
9 |
10 | //Generating the authentication url
11 | return oauth2Client.generateAuthUrl({
12 | access_type: "offline",
13 | prompt: "consent",
14 | scope: scopes,
15 | });
16 | }
17 |
18 | //Fetching the google user using the token from the authentication url
19 | async function getGoogleUser(token) {
20 | try {
21 | const googleUser = await axios.get(
22 | `https://www.googleapis.com/oauth2/v1/userinfo?alt=json&access_token=${token.access_token}`,
23 | {
24 | headers: {
25 | Authorization: `Bearer ${token.id_token}`,
26 | },
27 | }
28 | );
29 |
30 | return googleUser.data;
31 | } catch (e) {
32 | throw new Error("Problem getting user");
33 | }
34 | }
35 |
36 | module.exports = {
37 | getAuthUrl: getAuthUrl,
38 | getGoogleUser: getGoogleUser,
39 | };
40 |
--------------------------------------------------------------------------------
/src/socketFunc/Compile.js:
--------------------------------------------------------------------------------
1 | const { getUser } = require("../utils/Users");
2 | const { compilerFunc } = require("../Function/compilerFunc");
3 |
4 | module.exports = function (io) {
5 | try {
6 | io.on("connection", (socket) => {
7 | socket.on("Compile_ON", ({ language, code, input, reason }) => {
8 | try {
9 | const sids = io.of("/").adapter.sids;
10 | const room = [...sids.get(socket.id)][1]; //Get all t
11 |
12 | if (!room) {
13 | return;
14 | }
15 |
16 | if (reason === "code-editor")
17 | socket.broadcast.to(room).emit("Compile_ON");
18 |
19 | compilerFunc(language, code, input)
20 | .then((res) => {
21 | if (reason === "code-editor")
22 | io.to(room).emit("COMPILE_OFF", res.data);
23 | else io.to(socket.id).emit("COMPILE_OFF", res.data);
24 | })
25 | .catch((e) => {
26 | if (reason === "code-editor")
27 | io.to(room).emit("COMPILE_OFF", e.data);
28 | else io.to(socket.id).emit("COMPILE_OFF", res.data);
29 | });
30 | } catch (e) {
31 | console.log(e);
32 | }
33 | });
34 | });
35 | } catch (e) {
36 | console.log(e);
37 | }
38 | };
39 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | const express = require("express");
2 | const http = require("http");
3 | const socketIo = require("socket.io");
4 | const port = process.env.PORT || 8080;
5 | const cors = require("cors");
6 | require("./db/mongoose");
7 | const userRouter = require("./routers/UserRouter");
8 | const blogRouter = require("./routers/BlogRouter");
9 | const replyRouter = require("./routers/ReplyRouter");
10 | const commentRouter = require("./routers/CommentRouter");
11 | const oAuthRouter = require("./routers/OauthRouter");
12 | //const KEY = require("../config");
13 | const app = express();
14 | //Initialising the server instance
15 | const server = http.createServer(app);
16 |
17 | //Applying the middlewares
18 | app.use(
19 | cors({
20 | "origin": process.env.CORS_ORIGIN,
21 | "methods": "GET,HEAD,PUT,PATCH,POST,DELETE",
22 | "preflightContinue": false,
23 | "optionsSuccessStatus": 200
24 | })
25 | );
26 |
27 | app.use(express.json());
28 | //Server waking route
29 | app.get('/',(req,res)=>{
30 | return res.status(200).send({ title: 'Waking Call..' });
31 | })
32 | app.use("/user", userRouter);
33 | app.use("/Oauth", oAuthRouter);
34 | app.use("/blogs", blogRouter);
35 | app.use("/reply", replyRouter);
36 | app.use("/comment", commentRouter);
37 |
38 | //Initialising a socketio instance
39 | const io = socketIo(server, {
40 | cors: {
41 | origin: "*",
42 | },
43 | });
44 |
45 | require("./socketFunc/userJoin")(io);
46 | require("./socketFunc/chat")(io);
47 | require("./socketFunc/Compile")(io);
48 | require("./socketFunc/problem")(io);
49 | require("./socketFunc/Contest-Join")(io);
50 |
51 | server.listen(port, () => console.log(`Listening on port ${port}`));
52 |
--------------------------------------------------------------------------------
/.github/pull_request_template.md:
--------------------------------------------------------------------------------
1 | **Description**
2 |
3 |
4 |
5 | **Related Issue**
6 | - issue goes here
7 |
8 | Fixes # [ISSUE]
9 |
12 |
13 | - [ ] Code
14 | - [ ] User Interface
15 | - [ ] New Feature
16 | - [ ] Documentation
17 | - [ ] Testing
18 |
19 |
20 | **Code/Quality Assurance Only**
21 |
22 | - Bug fix (non-breaking change which fixes an issue)
23 | - This change requires a documentation update (software upgrade on readme file)
24 |
25 | **How Has This Been Tested?**
26 |
27 |
28 |
29 |
30 | **Additional Info (OPTIONAL)**
31 |
32 |
33 |
34 |
35 | **Checklist**
36 |
37 |
41 |
42 | - [ ] My code follows the code style of this project.
43 | - [ ] My UI is responsive
44 | - [ ] My change requires a change to the documentation.
45 | - [ ] I have updated the documentation accordingly.
46 | - [ ] All new and existing tests passed.
47 | - [ ] My changes generate no new warnings
48 | - [ ] The title of my pull request is a short description of the requested changes.
49 |
50 |
51 |
52 |
53 | **Screenshots**
54 | Original | Updated
55 | :------------------------:|---------------------:
56 | ** original screenshot ** | ** updated screenshot **
57 |
--------------------------------------------------------------------------------
/src/routers/OauthRouter.js:
--------------------------------------------------------------------------------
1 | const express = require("express");
2 | const router = new express.Router();
3 | const { google } = require("googleapis");
4 | const { getAuthUrl, getGoogleUser } = require("../Function/Oauth2");
5 | const User = require("../models/User");
6 |
7 | //Initialising the oauth client as a global variable
8 |
9 | global.oauth2Client = new google.auth.OAuth2(
10 | process.env.GOOGLE_CLIENT_ID,
11 | process.env.GOOGLE_CLIENT_SECRET,
12 | process.env.redirect_URI
13 | );
14 |
15 | router.get("/googleOauth", async (req, res) => {
16 | try {
17 | const authUrl = getAuthUrl();
18 | res.status(200).send(authUrl);
19 | } catch (e) {
20 | console.log(e);
21 | res.status(400).send();
22 | }
23 | });
24 |
25 | router.post("/authenticated", async (req, res) => {
26 | const code = req.body.code;
27 | console.log(req.body);
28 | if (!code) {
29 | return res
30 | .status(400)
31 | .send({ error: "There is a problem ! Please try again later" });
32 | }
33 |
34 | try {
35 | const { tokens } = await oauth2Client.getToken(code);
36 | const user = await getGoogleUser(tokens);
37 | const isUser = await User.findOne({ Email: user.email, Verified: true });
38 | if (isUser) {
39 | if (isUser.Deleted) {
40 | isUser.Deleted = false;
41 | await isUser.save();
42 | }
43 | const token = await isUser.generateToken();
44 | return res.status(200).send({ user: isUser, token: token, Way: "login" });
45 | }
46 | const newUser = new User({
47 | Name: user.name,
48 | Email: user.email,
49 | Verified: user.verified_email,
50 | });
51 |
52 | const _Guser = await newUser.save();
53 | const token = await _Guser.generateToken();
54 |
55 | return res.status(200).send({ user: _Guser, token: token, Way: "signup" });
56 | } catch (e) {
57 | res.status(400).send();
58 | }
59 | });
60 |
61 | module.exports = router;
62 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing Guidelines
2 |
3 | * Create every pull request on develop branch
4 | * Follow our [Commit Message Style Guide]() when you commit your changes.
5 | * Please consider raising an issue before submitting a pull request (PR) to solve a problem that is not present in our [issue tracker](https://github.com/atharmohammad/
6 | Code-N-Collab-Server/issues). This allows maintainers to first validate the issue you are trying to solve and also reference the PR to a specific issue.
7 | * When submitting a PR, please follow [this template]() (which will probably be already filled up once you create the PR).
8 | * When submitting a PR with changes to user interface (e.g.: new screen, ...), please add screenshots to the PR description.
9 | * When you are finished with your work, please squash your commits otherwise we will squash them on your PR (this can help us maintain a clear commit history).
10 | * Issues labeled as “good first issues” are meant for contributors who have not contributed to the project yet. Please choose other issues to contribute to, if you have already contributed to these type of issues.
11 |
12 | ## General Guidelines
13 |
14 | * If you’re just getting started work on an issue labeled “First Timers Only” in any project.
15 | * choose an open issue from the issue list, claim it in the comments, and a maintainer will assign it to you.
16 | * After approval you must make continuous notes on your progress in the issue while working. If there is not at least one comment every 3 days, the maintainer can reassign the issue.
17 | * Create a branch specific to the issue you're working on, so that you send a PR from that branch instead of the base branch on your fork.
18 | * If you’d like to create a new issue, please go through our issue list first (open as well as closed) and make sure the issues you are reporting do not replicate the existing issues.
19 | * Have a short description on what has gone wrong (like a root cause analysis and description of the fix), if that information is not already present in the issue.
20 | * If you have issues on multiple pages, report them separately. Do not combine them into a single issue.
21 |
--------------------------------------------------------------------------------
/src/utils/Users.js:
--------------------------------------------------------------------------------
1 | //Maintaining a list of passwords for room and users
2 |
3 | const passwordList = [];
4 | const users = [];
5 |
6 | //Adding users in a room
7 | const addUser = ({ id, username, room, password }) => {
8 | username = username.trim().toLowerCase();
9 | room = room.trim().toLowerCase();
10 | password = password.trim();
11 |
12 | if (!username || !room) {
13 | return {
14 | error: "Username and room are required",
15 | };
16 | }
17 |
18 | const existingUser = users.find((user) => {
19 | return user.room === room && user.username === username;
20 | });
21 |
22 | //No two user can have same name
23 | if (existingUser) {
24 | return {
25 | error: "Username is in use! Choose some other name",
26 | };
27 | }
28 |
29 | const roomPassword = passwordList.find((roomPassword) => {
30 | return roomPassword.room === room;
31 | });
32 |
33 | //Validating the room password
34 |
35 | if (roomPassword) {
36 | if (roomPassword.password !== password)
37 | return { error: "Password did not match !" };
38 | } else {
39 | const p = { room, password };
40 | passwordList.push(p);
41 | }
42 |
43 | //Updating the users list
44 | const user = { id, username, room };
45 | users.push(user);
46 | return { user };
47 | };
48 |
49 | //removing the user by filtering the user id
50 | const removeUser = (id) => {
51 | const index = users.findIndex((user) => user.id === id);
52 |
53 | if (index !== -1) {
54 | return users.splice(index, 1)[0];
55 | } else {
56 | return null;
57 | }
58 | };
59 |
60 | //returns a user
61 | const getUser = (id) => {
62 | return users.find((user) => user.id === id);
63 | };
64 |
65 | //Returning a list of users
66 | const getUsersInRoom = (room) => {
67 | return users.filter((user) => user.room === room);
68 | };
69 |
70 | //removing the password from the list
71 | const removePassword = (room) => {
72 | const index = passwordList.findIndex(
73 | (roomPassword) => roomPassword.room === room
74 | );
75 |
76 | if (index !== -1) {
77 | return passwordList.splice(index, 1)[0];
78 | }
79 | };
80 |
81 | module.exports = {
82 | addUser,
83 | removeUser,
84 | getUser,
85 | getUsersInRoom,
86 | removePassword,
87 | };
88 |
--------------------------------------------------------------------------------
/src/routers/UserRouter.js:
--------------------------------------------------------------------------------
1 | const express = require("express");
2 | const router = new express.Router();
3 | const User = require("../models/User");
4 | const auth = require("../middleware/Auth");
5 | const jwt = require("jsonwebtoken");
6 |
7 | router.get("/Me", auth, async (req, res, next) => {
8 | try {
9 | const user = req.user;
10 | if (!user) res.status(404).send();
11 |
12 | res.status(200).send(user);
13 | } catch (e) {
14 | res.status(400).send();
15 | }
16 | });
17 |
18 | router.get("/userProfile/:id", async (req, res) => {
19 | const id = req.params.id;
20 | console.log(id);
21 | try {
22 | const user = await User.findOne({ _id: id, Deleted: false });
23 | if (!user) {
24 | return res.status(404).send({ error: "User not found !" });
25 | }
26 |
27 | return res.status(200).send(user);
28 | } catch (e) {
29 | res.status(400).send();
30 | }
31 | });
32 |
33 | router.patch("/updateProfile", auth, async (req, res) => {
34 | const updates = Object.keys(req.body);
35 | const requiredUpdate = [
36 | "Name",
37 | "CodeforcesHandle",
38 | "Designation",
39 | "Password",
40 | "Avatar",
41 | "Moto",
42 | "Country",
43 | "Linkedin",
44 | "Github",
45 | "Codeforces",
46 | "Codechef",
47 | "AtCoder",
48 | "Institution",
49 | ];
50 | const allUpdates = updates.every((update) => requiredUpdate.includes(update));
51 | if (!allUpdates) {
52 | console.log(updates);
53 | return res.status(400).send({ error: "Invalid updates!" });
54 | }
55 | try {
56 | updates.forEach((update) => (req.user[update] = req.body[update]));
57 | await req.user.save();
58 |
59 | res.status(200).send(req.user);
60 | } catch (e) {
61 | res.status(400).send(e);
62 | }
63 | });
64 |
65 | router.delete("/deleteUser", auth, async (req, res) => {
66 | try {
67 | req.user.Deleted = true;
68 | await req.user.save();
69 | res.status(200).send();
70 | } catch (e) {
71 | res
72 | .status(400)
73 | .send({ error: "there is some error , user cannot be deleted !" });
74 | }
75 | });
76 |
77 | router.get("/logout", auth, async (req, res) => {
78 | try {
79 | req.user.token = null;
80 | await req.user.save();
81 | res.status(200).send();
82 | } catch (e) {
83 | res.status(400).send();
84 | }
85 | });
86 |
87 | module.exports = router;
88 |
--------------------------------------------------------------------------------
/src/Function/compilerFunc.js:
--------------------------------------------------------------------------------
1 | const axios = require("axios");
2 | const { AwesomeKey } = require("./getKey");
3 |
4 | const languageMapper = (lang_mode) => {
5 | switch (lang_mode) {
6 | case "cpp":
7 | return { language: "cpp14", versionIndex: "3" };
8 | case "python":
9 | return { language: "python3", versionIndex: "3" };
10 | case "java":
11 | return { language: "java", versionIndex: "3" };
12 | case "kotlin":
13 | return { language: "kotlin", versionIndex: "2" };
14 | case "sql":
15 | return { language: "sql", versionIndex: "3" };
16 | case "go":
17 | return { language: "go", versionIndex: "3" };
18 | case "scala":
19 | return { language: "scala", versionIndex: "3" };
20 | case "shell":
21 | return { language: "bash", versionIndex: "3" };
22 | case "pascal":
23 | return { language: "pascal", versionIndex: "2" };
24 | case "csharp":
25 | return { language: "csharp", versionIndex: "3" };
26 | case "php":
27 | return { language: "php", versionIndex: "3" };
28 | case "perl":
29 | return { language: "perl", versionIndex: "3" };
30 | case "ruby":
31 | return { language: "ruby", versionIndex: "3" };
32 | case "swift":
33 | return { language: "swift", versionIndex: "3" };
34 | case "lua":
35 | return { language: "lua", versionIndex: "2" };
36 | case "rust":
37 | return { language: "rust", versionIndex: "3" };
38 | case "r":
39 | return { language: "r", versionIndex: "3" };
40 | case "nodejs":
41 | return { language: "nodejs", versionIndex: "3" };
42 | }
43 |
44 | return {};
45 | };
46 |
47 | const compilerFunc = async (lang, code, input) => {
48 | const { language, versionIndex } = languageMapper(lang);
49 |
50 | const url = "https://api.jdoodle.com/v1/execute";
51 | const [clientId,clientSecret] = AwesomeKey();
52 |
53 | const sendData = {
54 | clientId,
55 | clientSecret,
56 | script: code,
57 | stdin: input,
58 | language,
59 | versionIndex,
60 | };
61 |
62 | let response = {};
63 | try {
64 | response = await axios({
65 | method: "post", //you can set what request you want to be
66 | url,
67 | data: sendData,
68 | });
69 | } catch (e) {
70 | response = e;
71 | return {
72 | data: {
73 | e:
74 | "Error:404\nOops Something went wrong\n😢😞🙁",
75 | },
76 | };
77 | }
78 | return response;
79 | };
80 |
81 | module.exports = {
82 | compilerFunc,
83 | };
84 |
--------------------------------------------------------------------------------
/src/socketFunc/problem.js:
--------------------------------------------------------------------------------
1 | const { getUser } = require("../utils/Users");
2 | const puppeteer = require("puppeteer");
3 |
4 | //Site parameters
5 | const params = {
6 | codeforces: {
7 | querySelector: `#pageContent > div.problemindexholder > div.ttypography`,
8 | wait: 0,
9 | },
10 | codechef: {
11 | querySelector:
12 | "#content-regions > section.content-area.small-8.columns.pl0",
13 | wait: 0,
14 | },
15 | geeksforgeeks: {
16 | querySelector: "#problems > div.problem-statement",
17 | wait: 500,
18 | },
19 | atcoder: {
20 | querySelector: "#task-statement > span > span.lang-en",
21 | wait: 500,
22 | },
23 | cses: {
24 | querySelector: "body > div.skeleton > div.content-wrapper > div.content",
25 | wait: 2500,
26 | },
27 | codedrills: { querySelector: ".py-5", wait: 5000 },
28 | };
29 |
30 | module.exports = function (io) {
31 | try {
32 | io.on("connection", (socket) => {
33 | socket.on("codeforces-problem", async (link) => {
34 | try {
35 | let problem = "";
36 | if (link == null || link == undefined) {
37 | //console.log("link not defined");
38 | } else if (link.includes("codeforces.com")) {
39 | problem = await chromiumFetch("codeforces", link);
40 | } else if (link.includes("codechef.com")) {
41 | problem = await chromiumFetch("codechef", link);
42 | } else if (link.includes("geeksforgeeks.org")) {
43 | problem = await chromiumFetch("geeksforgeeks", link);
44 | } else if (link.includes("atcoder.jp")) {
45 | problem = await chromiumFetch("atcoder", link);
46 | } else if (link.includes("cses.fi")) {
47 | problem = await chromiumFetch("cses", link);
48 | } else if (link.includes("codedrills.io")) {
49 | problem = await chormiumFetch("codedrills", link);
50 | } else {
51 | problem = "";
52 | }
53 |
54 | const user = getUser(socket.id);
55 | if (!user) {
56 | return;
57 | }
58 | if (!problem) {
59 | problem = `
60 | Please input correct url of the problem !
And make sure Url is from following websites only: geeksforgeeks , codeforces , codechef , atcoder,cses, codeDrills
61 |
`;
62 | }
63 | io.to(user.room).emit("problem", problem);
64 | } catch (e) {
65 | console.log(e);
66 | }
67 | });
68 | });
69 | } catch (e) {
70 | console.log(e);
71 | }
72 | };
73 | //Scrapping the problem from the selector after loading the page
74 | async function chromiumFetch(site, URL) {
75 | try {
76 | const browser = await puppeteer.launch({
77 | headless: true,
78 | args: ["--no-sandbox", "--disable-setuid-sandbox"],
79 | });
80 | console.log(site);
81 | const page = await browser.newPage();
82 | await page.goto(URL);
83 | let text = "";
84 | await page.waitForTimeout(params[site].wait);
85 | text = await page.evaluate((q) => {
86 | return document.querySelector(q).outerHTML;
87 | }, params[site].querySelector);
88 |
89 | await browser.close();
90 |
91 | return text;
92 | } catch (e) {
93 | console.log(e);
94 | }
95 |
96 | return "Error: Try again :( ";
97 | }
98 |
--------------------------------------------------------------------------------
/src/models/User.js:
--------------------------------------------------------------------------------
1 | const mongoose = require("mongoose");
2 | const validator = require("validator");
3 | const bcrypt = require("bcryptjs");
4 | const jwt = require("jsonwebtoken");
5 |
6 | const userSchema = new mongoose.Schema(
7 | {
8 | Name: {
9 | type: String,
10 | trim: true,
11 | required: true,
12 | },
13 | Email: {
14 | type: String,
15 | trim: true,
16 | unique: true,
17 | validate: (value) => {
18 | if (!validator.isEmail(value)) throw new Error("Email is not valid !");
19 | },
20 | },
21 | Password: {
22 | type: String,
23 | trim: true,
24 | },
25 | CodeforcesHandle: {
26 | type: String,
27 | trim: true,
28 | default: null,
29 | },
30 | Designation: {
31 | type: String,
32 | trim: true,
33 | },
34 | Deleted: {
35 | type: Boolean,
36 | default: false,
37 | },
38 | Blogs: [
39 | {
40 | type: mongoose.Schema.Types.ObjectId,
41 | required: true,
42 | ref: "Blog",
43 | },
44 | ],
45 | token: {
46 | type: String,
47 | },
48 | Avatar: {
49 | type: Number,
50 | default: 1,
51 | },
52 | Verified: {
53 | type: Boolean,
54 | default: false,
55 | },
56 | Moto: {
57 | type: String,
58 | default: "",
59 | },
60 | Institution: {
61 | type: String,
62 | default: null,
63 | },
64 | Country: {
65 | type: String,
66 | default: "",
67 | },
68 | Linkedin: {
69 | type: String,
70 | default: null,
71 | },
72 | Github: {
73 | type: String,
74 | default: null,
75 | },
76 | Codeforces: {
77 | type: String,
78 | default: null,
79 | },
80 | Codechef: {
81 | type: String,
82 | default: null,
83 | },
84 | AtCoder: {
85 | type: String,
86 | default: null,
87 | },
88 | SuperUser: {
89 | type: Boolean,
90 | default: false,
91 | },
92 | },
93 | {
94 | timestamps: true,
95 | }
96 | );
97 |
98 | userSchema.methods.toJSON = function () {
99 | const user = this;
100 | const userObject = user.toObject();
101 |
102 | delete userObject.Password;
103 | delete userObject.token;
104 | delete userObject.Blogs;
105 | delete userObject.Verified;
106 | delete userObject.Deleted;
107 |
108 | return userObject;
109 | };
110 |
111 | userSchema.methods.generateToken = async function () {
112 | const user = this;
113 | const token = jwt.sign({ _id: user._id.toString() }, process.env.JWT_SECRET);
114 | user.token = token;
115 | await user.save();
116 | return token;
117 | };
118 |
119 | userSchema.statics.findByCredentials = async function (email, password) {
120 | const user = await this.model("User").findOne({
121 | Email: email,
122 | Deleted: false,
123 | });
124 | if (!user) throw new Error("User does not exists");
125 |
126 | const isMatch = await bcrypt.compare(password, user.Password);
127 |
128 | if (!isMatch) throw new Error("Password is not correct");
129 |
130 | return user;
131 | };
132 |
133 | userSchema.pre("save", async function (next) {
134 | const user = this;
135 | if (user.isModified("Password")) {
136 | user.Password = await bcrypt.hash(user.Password, 8); //Hash it 8 times
137 | }
138 | next();
139 | });
140 |
141 | const table = mongoose.model("User", userSchema);
142 |
143 | module.exports = table;
144 |
--------------------------------------------------------------------------------
/src/routers/ReplyRouter.js:
--------------------------------------------------------------------------------
1 | const express = require("express");
2 | const router = new express.Router();
3 |
4 | const Reply = require("../models/Reply");
5 | const Comments = require("../models/Comment");
6 |
7 | const auth = require("../middleware/Auth");
8 |
9 | router.post("/newReply/:id", auth, async (req, res) => {
10 | try {
11 | if(!req.body.Body || !req.body.Body.trim()){
12 | throw new Error('Reply cant be empty!')
13 | }
14 | const reply = new Reply({
15 | Body: req.body.Body,
16 | User: req.user._id,
17 | Comment: req.params.id,
18 | });
19 | const comment = await Comments.findOne({
20 | _id: req.params.id,
21 | Deleted: false,
22 | });
23 | if (!comment) res.status(404).send();
24 |
25 | const newReply = await reply.save();
26 | comment.Replies.push(newReply._id);
27 | await comment.save();
28 | res.status(200).send(newReply);
29 | } catch (e) {
30 | res.status(400).send();
31 | }
32 | });
33 |
34 | router.get("/getReply/:id", async (req, res) => {
35 | try {
36 | const comment = await Comments.findOne({
37 | _id: req.params.id,
38 | Deleted: false,
39 | })
40 | .populate({
41 | path: "Replies",
42 | model: "Reply",
43 | select: ["User", "Body", "Likes", "Comment","createdAt"],
44 | match: { Deleted: false },
45 | populate: {
46 | path: "User",
47 | select: ["Name", "Designation", "Avatar", "Institution","SuperUser"],
48 | },
49 | })
50 | .exec();
51 |
52 | if (!comment) {
53 | return res.status(400).send();
54 | }
55 |
56 | res.status(200).send(comment);
57 | } catch (e) {
58 | console.log(e);
59 | res.status(400).send();
60 | }
61 | });
62 |
63 | router.post("/like/:id", auth, async (req, res) => {
64 | try {
65 | const reply = await Reply.findOne({ _id: req.params.id, Deleted: false });
66 | if (!reply) res.status(404).send();
67 |
68 | const like = reply.Likes.find((curr) => {
69 | return curr.toString().trim() == req.user._id.toString().trim();
70 | });
71 |
72 | if (like) {
73 | const likeArray = reply.Likes.filter(
74 | (curr) => curr.toString().trim() != req.user._id.toString().trim()
75 | );
76 | reply.Likes = likeArray;
77 | } else {
78 | reply.Likes.push(req.user._id);
79 | }
80 | const newReply = await reply.save();
81 | res.status(200).send(newReply);
82 | } catch (e) {
83 | res.status(400).send();
84 | }
85 | });
86 |
87 | router.delete("/deleteReply/:id", auth, async (req, res) => {
88 | const id = req.params.id;
89 |
90 | try {
91 | const reply = await Reply.findOne({ _id: id, Deleted: false });
92 | if (!reply) return res.status(404).send();
93 |
94 | if (!req.Admin && reply.User.toString().trim() !== req.user._id.toString().trim()) {
95 | return res.status(401).send();
96 | }
97 |
98 | reply.Deleted = true;
99 | await reply.save();
100 | res.status(200).send();
101 | } catch (e) {
102 | res.status(400).send();
103 | }
104 | });
105 |
106 | router.patch("/updateReply/:id", auth, async (req, res) => {
107 | const id = req.params.id;
108 |
109 | try {
110 | const reply = await Reply.findOne({ _id: id, Deleted: false });
111 |
112 | if (!reply) {
113 | return res.status(404).send();
114 | }
115 |
116 | if (reply.User.toString().trim() !== req.user._id.toString().trim()) {
117 | return res.status(401).send();
118 | }
119 | if(!req.body.Body || !req.body.Body.trim()){
120 | throw new Error('Reply cant be empty!')
121 | }
122 | reply.Body = req.body.Body;
123 | await reply.save();
124 | res.status(200).send(reply);
125 | } catch (e) {
126 | res.status(400).send();
127 | }
128 | });
129 |
130 | module.exports = router;
131 |
--------------------------------------------------------------------------------
/src/routers/BlogRouter.js:
--------------------------------------------------------------------------------
1 | const express = require("express");
2 | const router = new express.Router();
3 | const Blog = require("../models/Blogs");
4 | const auth = require("../middleware/Auth");
5 | const User = require("../models/User");
6 |
7 | router.get("/Allblogs", async (req, res) => {
8 | try {
9 | let sort;
10 | // /Allblogs?sortBy=popularity&skip=5
11 | if (req.query.sortBy === "popularity") {
12 | sort = { LikesLength: "desc" };
13 | }else if(req.query.sortBy === "oldest-first"){
14 | sort = { createdAt: 1 };
15 | }
16 | else {
17 | sort = { createdAt: "desc" };
18 | }
19 |
20 | const blogs = await Blog.find({ Deleted: false })
21 | .limit(10)
22 | .skip(parseInt(req.query.skip))
23 | .sort(sort)
24 | .populate({
25 | path: "User",
26 | select: ["Name", "Designation", "Avatar", "Institution", "SuperUser"],
27 | match: { Deleted: false },
28 | })
29 | .exec();
30 | res.status(200).send(blogs);
31 | } catch (e) {
32 | res.status(400).send(e);
33 | }
34 | });
35 |
36 | router.post("/write", auth, async (req, res) => {
37 | try {
38 | if(!req.body.Body || !req.body.Body.trim()){
39 | throw new Error('Blog cant be empty!')
40 | }
41 | const blog = new Blog({ Body: req.body.Body, User: req.user._id });
42 | const newBlog = await blog.save();
43 | const user = await User.findOne({ _id: req.user._id });
44 | user.Blogs.push(newBlog._id);
45 | await user.save();
46 | res.status(200).send(newBlog);
47 | } catch (e) {
48 | res.status(400).send(e);
49 | }
50 | });
51 |
52 | router.delete("/delete/:id", auth, async (req, res) => {
53 | try {
54 | const _id = req.params.id;
55 | const blog = await Blog.findOne({ _id: _id, Deleted: false });
56 | if (!blog) return res.status(404).send();
57 |
58 | if (
59 | !req.Admin &&
60 | blog.User.toString().trim() != req.user._id.toString().trim()
61 | ) {
62 | return res.status(401).send();
63 | }
64 |
65 | blog.Deleted = true;
66 | await blog.save();
67 |
68 | res.status(200).send();
69 | } catch (e) {
70 | return res.status(400).send(e);
71 | }
72 | });
73 |
74 | router.get("/currentBlog/:id", async (req, res) => {
75 | try {
76 | const id = req.params.id;
77 | const blog = await Blog.findOne({ _id: id, Deleted: false }).populate({
78 | path: "User",
79 | select: ["Name", "Designation", "Avatar", "Institution","SuperUser"],
80 | match: { Deleted: false },
81 | });
82 | if (!blog) res.status(404).send();
83 |
84 | res.status(200).send(blog);
85 | } catch (e) {
86 | return res.status(400).send(e);
87 | }
88 | });
89 |
90 | router.patch("/currBlog/:id", auth, async (req, res) => {
91 | try {
92 | const id = req.params.id;
93 | const blog = await Blog.findOne({ _id: id, Deleted: false });
94 | if (!blog) res.status(404).send();
95 |
96 | if(!req.body.Body || !req.body.Body.trim()){
97 | throw new Error('Blog cant be empty!')
98 | }
99 |
100 | blog.Body = req.body.Body;
101 | await blog.save();
102 | res.status(200).send();
103 | } catch (e) {
104 | res.status(400).send(e);
105 | }
106 | });
107 |
108 | router.post("/like/:id", auth, async (req, res) => {
109 | try {
110 | const blog = await Blog.findOne({
111 | _id: req.params.id,
112 | Deleted: false,
113 | });
114 |
115 | if (!blog) {
116 | return res.status(404).send();
117 | }
118 |
119 | const like = blog.Likes.find((curr) => {
120 | return curr.toString().trim() == req.user._id.toString().trim();
121 | });
122 |
123 | if (like) {
124 | const likeArray = blog.Likes.filter(
125 | (curr) => curr.toString().trim() != req.user._id.toString().trim()
126 | );
127 | blog.Likes = likeArray;
128 | } else {
129 | blog.Likes.push(req.user._id);
130 | }
131 |
132 | blog.LikesLength = blog.Likes.length;
133 |
134 | await blog.save();
135 | res.status(200).send(blog);
136 | } catch (e) {
137 | console.log(e);
138 | res.status(400).send();
139 | }
140 | });
141 |
142 | module.exports = router;
143 |
--------------------------------------------------------------------------------
/src/routers/CommentRouter.js:
--------------------------------------------------------------------------------
1 | const express = require("express");
2 | const router = new express.Router();
3 | const Comments = require("../models/Comment");
4 | const Blogs = require("../models/Blogs");
5 | const auth = require("../middleware/Auth");
6 |
7 | router.get("/getComments/:id", async (req, res) => {
8 | const id = req.params.id;
9 | try {
10 | const blogs = await Blogs.findOne({ _id: id, Deleted: false })
11 | .populate({
12 | path: "Comments",
13 | model: "Comment",
14 | select: ["User", "Body", "Likes", "Replies","createdAt"],
15 | match: { Deleted: false },
16 | populate: {
17 | path: "User",
18 | select: ["Name", "Designation", "Avatar", "Institution","SuperUser"],
19 | },
20 | })
21 | .exec();
22 |
23 | if (!blogs) {
24 | res.status(404).send();
25 | } else {
26 | res.status(200).send(blogs);
27 | }
28 | } catch (e) {
29 | console.log(e);
30 | res.status(404).send();
31 | }
32 | });
33 |
34 | router.post("/createComment/:id", auth, async (req, res) => {
35 | try {
36 | const blog = await Blogs.findOne({ _id: req.params.id });
37 |
38 | if (!blog) {
39 | return res.status(404).send();
40 | }
41 | if(!req.body.Body || !req.body.Body.trim()){
42 | throw new Error('Comment cant be empty!')
43 | }
44 | const comment = new Comments({
45 | Body: req.body.Body,
46 | User: req.user._id,
47 | Blog: req.params.id,
48 | });
49 |
50 | const newComment = await comment.save();
51 | blog.Comments.push(newComment._id);
52 | await blog.save();
53 | res.status(200).send();
54 | } catch (e) {
55 | res.status(400).send(e);
56 | }
57 | });
58 |
59 | router.post("/like/:id", auth, async (req, res) => {
60 | try {
61 | const comment = await Comments.findOne({
62 | _id: req.params.id,
63 | Deleted: false,
64 | });
65 | if (!comment) throw new Error();
66 |
67 | const like = comment.Likes.find((curr) => {
68 | return curr.toString().trim() == req.user._id.toString().trim();
69 | });
70 | if (like) {
71 | const likeArray = comment.Likes.filter(
72 | (curr) => curr.toString().trim() != req.user._id.toString().trim()
73 | );
74 | comment.Likes = likeArray;
75 | } else {
76 | comment.Likes.push(req.user._id);
77 | }
78 |
79 | await comment.save();
80 | res.status(200).send(comment);
81 | } catch (e) {
82 | res.status(400).send(e);
83 | }
84 | });
85 |
86 | router.patch("/updateComment/:id", auth, async (req, res) => {
87 | const id = req.params.id;
88 | try {
89 | const comment = await Comments.findOne({ _id: id, Deleted: false });
90 | if (!comment) {
91 | return res.status(404).send();
92 | }
93 |
94 | if (comment.User.toString().trim() !== req.user._id.toString().trim()) {
95 | return res.status(401).send();
96 | }
97 |
98 | if(!req.body.Body || !req.body.Body.trim()){
99 | throw new Error('Comment cant be empty!')
100 | }
101 |
102 | comment.Body = req.body.Body;
103 | await comment.save();
104 | res.status(200).send(comment.Body);
105 | } catch (e) {
106 | res.status(400).send(e);
107 | }
108 | });
109 |
110 | router.delete("/deleteComment/:id", auth, async (req, res) => {
111 | const id = req.params.id;
112 |
113 | try {
114 | const comment = await Comments.findOne({ _id: id, Deleted: false });
115 | if (!comment) return res.status(404).send();
116 |
117 | if (!req.Admin && comment.User.toString().trim() !== req.user._id.toString().trim()) {
118 | return res.status(401).send();
119 | }
120 | const blog = await Blogs.findOne({
121 | _id: comment.Blog.toString().trim(),
122 | Deleted: false,
123 | });
124 | const newCommentArray = blog.Comments.filter(
125 | (comment) => comment.toString().trim() !== id.toString().trim()
126 | );
127 | blog.Comments = newCommentArray;
128 | await blog.save();
129 | comment.Deleted = true;
130 | await comment.save();
131 | res.status(200).send();
132 | } catch (e) {
133 | res.status(400).send();
134 | }
135 | });
136 |
137 | module.exports = router;
138 |
--------------------------------------------------------------------------------
/src/socketFunc/Contest-Join.js:
--------------------------------------------------------------------------------
1 | const axios = require("axios");
2 | const {
3 | checkContest,
4 | removeContestUser,
5 | startContest,
6 | getTeamMembers,
7 | createURL,
8 | updateContest,
9 | getContestLength,
10 | deleteContests,
11 | } = require("../utils/Contest");
12 |
13 | let DeleteIntervalOn = false;
14 |
15 | module.exports = function (io) {
16 | try {
17 | io.on("connection", (socket) => {
18 | socket.on("Contest-Join", (user, callback) => {
19 | try {
20 | if (!user || !user.Name || !user.Name.trim()) {
21 | return callback({
22 | error: "Update Codeforces Handle",
23 | contest: null,
24 | });
25 | }
26 | if (!user.RoomId || !user.RoomId.trim()) {
27 | return callback({ error: "Invalid room Name", contest: null });
28 | }
29 |
30 | const obj = checkContest(user.RoomId, user.Name, socket.id);
31 | console.log(obj.contest);
32 | if (obj.error) {
33 | return callback({ error: obj.error, contest: obj.contest });
34 | } else {
35 | if (!DeleteIntervalOn) {
36 | console.log("Starting Interval");
37 | DeleteIntervalOn = true;
38 | const interval = setInterval(() => {
39 | console.log("deleting data.....");
40 | deleteContests();
41 | if (getContestLength() == 0) {
42 | console.log("Stopping Interval");
43 | DeleteIntervalOn = false;
44 | clearInterval(interval);
45 | }
46 | }, 24 * 60 * 60 * 1000);
47 | }
48 | socket.join(user.RoomId);
49 | console.log("contest-joined");
50 | callback({ error: obj.error, contest: obj.contest });
51 | const teamMembers = getTeamMembers(obj.contest.UsersId);
52 | io.in(user.RoomId).emit("peopleInRoom", {
53 | teamMembers,
54 | userJoin: user.Name.trim().toLowerCase(),
55 | });
56 | }
57 | } catch (e) {
58 | console.log(e);
59 | }
60 | });
61 | socket.on(
62 | "Start-Contest",
63 | ({ room, problemTags, minRating, maxRating, maxDuration }) => {
64 | try {
65 | socket.to(room).emit("Contest-Starting");
66 | problemTags = problemTags.map((tag) => tag.label);
67 | const URL = createURL(problemTags);
68 | const problems = axios
69 | .get(URL)
70 | .then((res) => {
71 | console.log(res);
72 | const problemArray = res.data.result.problems.slice(0);
73 | const contest = startContest({
74 | room,
75 | problemTags,
76 | minRating,
77 | maxRating,
78 | problemArray,
79 | maxDuration,
80 | });
81 | const teamMembers = getTeamMembers(contest.UsersId);
82 | io.to(room).emit("Update", contest); //First update then send memebers
83 | })
84 | .catch((e) => console.log(e));
85 | } catch (e) {
86 | console.log(e);
87 | }
88 | }
89 | );
90 | socket.on("Contest-Update", async ({ roomId }) => {
91 | try {
92 | const contest = await updateContest(roomId);
93 | console.log(contest);
94 | io.to(roomId).emit("Update", contest);
95 | } catch (e) {
96 | console.log(e);
97 | }
98 | });
99 | socket.on("Leave-Contest", (user) => {
100 | try {
101 | console.log("contest-Left");
102 | const contest = removeContestUser({
103 | roomId: user.roomId,
104 | name: user.name,
105 | });
106 | console.log(contest);
107 | const teamMembers = getTeamMembers(contest.UsersId);
108 | console.log(teamMembers);
109 | io.to(user.room).emit("peopleInRoom", {
110 | teamMembers,
111 | userLeft: user.name.trim().toLowerCase(),
112 | });
113 | socket.leave(user.room);
114 | } catch (e) {
115 | console.log(e);
116 | }
117 | });
118 | });
119 | } catch (e) {
120 | console.log(e);
121 | }
122 | };
123 |
--------------------------------------------------------------------------------
/src/socketFunc/userJoin.js:
--------------------------------------------------------------------------------
1 | const axios = require("axios");
2 | const {
3 | addUser,
4 | removeUser,
5 | getUser,
6 | getUsersInRoom,
7 | removePassword,
8 | } = require("../utils/Users");
9 |
10 | module.exports = function (io) {
11 | try {
12 | io.on("connection", (socket) => {
13 | socket.on("join", ({ username, room, password }, callback) => {
14 | try {
15 | const { error, user } = addUser({
16 | id: socket.id,
17 | username,
18 | room,
19 | password,
20 | });
21 |
22 | if (error) {
23 | return callback({ error });
24 | }
25 | try {
26 | socket.join(user.room);
27 |
28 | console.log("A new user joined", user.room, user.username);
29 | } catch (e) {
30 | return console.log("cant join");
31 | }
32 |
33 | //To get data for newly connected client from the room
34 | const socketsInstances = async () => {
35 | try {
36 | const clients = await io.in(user.room).fetchSockets();
37 | const teamMembers = getUsersInRoom(user.room);
38 | io.to(user.room).emit("peopleInRoom", {
39 | teamMembers,
40 | userJoin: user.username,
41 | });
42 | //counts how many users are active in room
43 | let res = "";
44 | if (clients.length > 1) {
45 | //make functions for getting data
46 | let askedCnt = 0;
47 |
48 | for (const client of clients) {
49 | if (askedCnt == 5) break;
50 | if (client.id === socket.id) continue;
51 |
52 | askedCnt++;
53 | io.to(client.id).emit("sendInitialIO", { id: socket.id });
54 | }
55 | }
56 | } catch (e) {}
57 | };
58 |
59 | socketsInstances();
60 | return callback({ user });
61 | } catch (e) {
62 | console.log(e);
63 | }
64 | });
65 |
66 | socket.on("takeInitialIO", (data) => {
67 | try {
68 | if (data.reason === "code-editor") {
69 | console.log("takeInitialIO", data.inputText, data.outputText);
70 | io.to(data.id).emit("IO_recieved", {
71 | inputText: data.inputText,
72 | outputText: data.outputText,
73 | });
74 | }
75 | } catch (e) {
76 | console.log(e);
77 | }
78 | });
79 | //real-time updates on the change in IO
80 | socket.on("changeIO", (data) => {
81 | try {
82 | if (data.reason === "code-editor") {
83 | const sids = io.of("/").adapter.sids; //sids gives map of every user socketid to its room
84 | const room = [...sids.get(socket.id)][1];
85 | if (!room) return;
86 | socket.broadcast.to(room).emit("IO_recieved", data);
87 | }
88 | } catch (e) {
89 | console.log(e);
90 | }
91 | });
92 | //Disconnecting the user and updating , notifying other people in the room about the user
93 | socket.on("disconnect", () => {
94 | try {
95 | const user = removeUser(socket.id);
96 |
97 | if (!user) return;
98 |
99 | console.log("disconnecting", user);
100 |
101 | if (user) {
102 | try {
103 | const socketsInstances = async () => {
104 | const clients = await io.in(user.room).fetchSockets();
105 | const teamMembers = getUsersInRoom(user.room);
106 | if (clients.length) {
107 | io.to(user.room).emit("peopleInRoom", {
108 | teamMembers,
109 | userLeft: user.username,
110 | });
111 | }
112 |
113 | if (clients.length == 0) {
114 | removePassword(user.room);
115 | }
116 | socket.leave(user.room);
117 | console.log("Disconnected");
118 | };
119 | socketsInstances();
120 | } catch (e) {
121 | console.log(e);
122 | }
123 | }
124 | } catch (e) {
125 | console.log(e);
126 | }
127 | });
128 | });
129 | } catch (e) {
130 | console.log(e);
131 | }
132 | };
133 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | ## Our Pledge
2 |
3 | We as members, contributors, and leaders pledge to make participation in our
4 | community a harassment-free experience for everyone, regardless of age, body
5 | size, visible or invisible disability, ethnicity, sex characteristics, gender
6 | identity and expression, level of experience, education, socio-economic status,
7 | nationality, personal appearance, race, religion, or sexual identity
8 | and orientation.
9 |
10 | We pledge to act and interact in ways that contribute to an open, welcoming,
11 | diverse, inclusive, and healthy community.
12 |
13 | ## Our Standards
14 |
15 | Examples of behavior that contributes to a positive environment for our
16 | community include:
17 |
18 | * Demonstrating empathy and kindness toward other people
19 | * Being respectful of differing opinions, viewpoints, and experiences
20 | * Giving and gracefully accepting constructive feedback
21 | * Accepting responsibility and apologizing to those affected by our mistakes,
22 | and learning from the experience
23 | * Focusing on what is best not just for us as individuals, but for the
24 | overall community
25 |
26 | Examples of unacceptable behavior include:
27 |
28 | * The use of sexualized language or imagery, and sexual attention or
29 | advances of any kind
30 | * Trolling, insulting or derogatory comments, and personal or political attacks
31 | * Public or private harassment
32 | * Publishing others' private information, such as a physical or email
33 | address, without their explicit permission
34 | * Other conduct which could reasonably be considered inappropriate in a
35 | professional setting
36 |
37 | ## Enforcement Responsibilities
38 |
39 | Community leaders are responsible for clarifying and enforcing our standards of
40 | acceptable behavior and will take appropriate and fair corrective action in
41 | response to any behavior that they deem inappropriate, threatening, offensive,
42 | or harmful.
43 |
44 | Community leaders have the right and responsibility to remove, edit, or reject
45 | comments, commits, code, wiki edits, issues, and other contributions that are
46 | not aligned to this Code of Conduct, and will communicate reasons for moderation
47 | decisions when appropriate.
48 |
49 | ## Scope
50 |
51 | This Code of Conduct applies within all community spaces, and also applies when
52 | an individual is officially representing the community in public spaces.
53 | Examples of representing our community include using an official e-mail address,
54 | posting via an official social media account, or acting as an appointed
55 | representative at an online or offline event.
56 |
57 | ## Enforcement
58 |
59 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
60 | reported to the community leaders responsible for enforcement at
61 | codencollab@gmail.com.
62 | All complaints will be reviewed and investigated promptly and fairly.
63 |
64 | All community leaders are obligated to respect the privacy and security of the
65 | reporter of any incident.
66 |
67 | ## Enforcement Guidelines
68 |
69 | Community leaders will follow these Community Impact Guidelines in determining
70 | the consequences for any action they deem in violation of this Code of Conduct:
71 |
72 | ### 1. Correction
73 |
74 | **Community Impact**: Use of inappropriate language or other behavior deemed
75 | unprofessional or unwelcome in the community.
76 |
77 | **Consequence**: A private, written warning from community leaders, providing
78 | clarity around the nature of the violation and an explanation of why the
79 | behavior was inappropriate. A public apology may be requested.
80 |
81 | ### 2. Warning
82 |
83 | **Community Impact**: A violation through a single incident or series
84 | of actions.
85 |
86 | **Consequence**: A warning with consequences for continued behavior. No
87 | interaction with the people involved, including unsolicited interaction with
88 | those enforcing the Code of Conduct, for a specified period of time. This
89 | includes avoiding interactions in community spaces as well as external channels
90 | like social media. Violating these terms may lead to a temporary or
91 | permanent ban.
92 |
93 | ### 3. Temporary Ban
94 |
95 | **Community Impact**: A serious violation of community standards, including
96 | sustained inappropriate behavior.
97 |
98 | **Consequence**: A temporary ban from any sort of interaction or public
99 | communication with the community for a specified period of time. No public or
100 | private interaction with the people involved, including unsolicited interaction
101 | with those enforcing the Code of Conduct, is allowed during this period.
102 | Violating these terms may lead to a permanent ban.
103 |
104 | ### 4. Permanent Ban
105 |
106 | **Community Impact**: Demonstrating a pattern of violation of community
107 | standards, including sustained inappropriate behavior, harassment of an
108 | individual, or aggression toward or disparagement of classes of individuals.
109 |
110 | **Consequence**: A permanent ban from any sort of public interaction within
111 | the community.
112 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
7 |
8 |
9 |
10 | [![Contributors][contributors-shield]][contributors-url]
11 | [![Forks][forks-shield]][forks-url]
12 | [![Stargazers][stars-shield]][stars-url]
13 | [![MIT License][license-shield]][license-url]
14 | [![Issues][issues-shield]][issues-url]
15 | [![LinkedIn][linkedin-shield]][linkedin-url]
16 |
17 |
18 | Table of Contents
19 |
20 | -
21 | About
22 |
25 |
26 | -
27 | Getting Started
28 |
31 |
34 |
35 | - Contributing
36 | - Contact
37 | - Resources
38 | - License
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 | |
48 |
49 |
50 | |
51 |
52 |
53 |
54 |
55 | |
56 |
57 |
58 | |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 | |
69 |
70 |
71 |
72 |
73 |
74 | # 🔖 About
75 | Code-N-Collab server is backend for Code-N-Collab , It make Code-N-Collab real-time collaborative using sockets and also serves as backend for blogs
76 |
77 | ### Website
78 | [Code-N-Collab](https://code-n-collab.netlify.app/)
79 |
80 | ## 🚀 Features
81 | It provides users with :-
82 | - Real-time code editor : Users can collaborate with their team on issues and solve CP problems using a real-time code-editor(like Google Docs)
83 |
84 | - Lockout Championship : For Cp lovers, they can compete in a lockout championships with their friends to and challenge your friends for championship,filter problems with preferred difficulty and improve you CP skills
85 |
86 | - Blogs : To find new people and share your knowledge , platform provides users blogs to write learn and share
87 |
88 | ## 🔥 Getting Started
89 |
90 | ### Prerequisites
91 |
92 | - Reactjs
93 | - Nodejs
94 | - Google Cloud Platform
95 |
96 | #### Setup your free GCP account for google Oauth
97 | - https://support.google.com/cloud/answer/6158849?hl=en
98 |
99 | - Setup API&Credentials for Web Application
100 |
101 | - Provide a Redirect URI in the Credentials(The redirect_URI will be used to redirect to the page after login with google,in below environment variable example we have setup redirect_URI=http://localhost:3000/homepage/ , using port 3000 you can use any port but make sure to add /homepage after that to redirect to correct route)
102 |
103 | - copy and
104 |
105 | #### Setup your MONGO_DB atlas
106 | - Atlas Docs
107 | - get the from your cluster , you have to use it in env vars
108 |
109 | #### Setup Environment variables
110 | - you can declare your env vars using dotenv like below :
111 |
112 | ```
113 | CORS_ORIGIN=*
114 | GOOGLE_CLIENT_ID=
115 | GOOGLE_CLIENT_SECRET=
116 | redirect_URI=http://localhost:3000/homepage/
117 | BaseURI=http://localhost:8080/
118 | MONGO_DB_URL=
119 | COMPILE_CLIENT_ID1=
120 | COMPILE_CLIENT_SECRET1=
121 | COMPILE_CLIENT_ID2=
122 | COMPILE_CLIENT_SECRET2=
123 | COMPILE_CLIENT_ID3=
124 | COMPILE_CLIENT_SECRET3=
125 | COMPILE_CLIENT_ID4=
126 | COMPILE_CLIENT_SECRET4=
127 |
128 | ```
129 | - or you can declare your env vars in nodemon.json if you are using nodemon for development like below:
130 | ```
131 | {
132 | "env":{
133 | "CORS_ORIGIN":"*",
134 | "GOOGLE_CLIENT_ID": "",
135 | "GOOGLE_CLIENT_SECRET": "",
136 | "redirect_URI" : "http://localhost:3000/homepage/",
137 | "BaseURI":"http://localhost:8080/",
138 | "MONGO_DB_URL":"",
139 | "COMPILE_CLIENT_ID1":"",
140 | "COMPILE_CLIENT_SECRET1":"",
141 | "COMPILE_CLIENT_ID2":"",
142 | "COMPILE_CLIENT_SECRET2":"",
143 | "COMPILE_CLIENT_ID3":"",
144 | "COMPILE_CLIENT_SECRET3":"",
145 | "COMPILE_CLIENT_ID4":"",
146 | "COMPILE_CLIENT_SECRET4":""
147 | }
148 | }
149 |
150 | ```
151 |
152 | ### Installation
153 |
154 | ```
155 | $ git clone https://github.com//Code-N-Collab-Server.git
156 | $ cd Code-N-Collab-Server
157 | $ git remote add upstream https://github.com/atharmohammad/Code-N-Collab-Server.git
158 | $ npm install
159 |
160 | $ npm start // if you are using dotenv
161 |
162 | //or
163 |
164 | $ npm run dev // if you are using nodemon.json
165 |
166 | ```
167 |
168 | ## 💁 Contribution guidelines
169 |
170 | we encourage organizations and individuals to contribute requirements, documentation, issues, new templates, and code.
171 | For code contributions, read :
172 |
173 | - The Code of Conduct
174 | - The Contribution Guidelines
175 |
176 | ## 📲 Contact
177 |
178 | Mohd Athar - mohd.rule123@gmail.com
179 |
180 | Adnan Shamsi - adnanshamsi023@gmail.com
181 |
182 | ## 📚 Resources
183 | - Socket.Io Documentation
184 | - Convergence Docs
185 | - Atlas Docs
186 | - React-Codemirror Editor Docs
187 | - Mongoose Docs
188 |
189 | ## License
190 | Apache License 2.0
191 |
192 | [contributors-shield]: https://img.shields.io/github/contributors/atharmohammad/Code-N-Collab-Server.svg?style=for-the-badge
193 | [contributors-url]: https://github.com/atharmohammad/Code-N-Collab-Server/graphs/contributors
194 | [forks-shield]: https://img.shields.io/github/forks/atharmohammad/Code-N-Collab-Server.svg?style=for-the-badge
195 | [forks-url]: https://github.com/atharmohammad/Code-N-Collab-Server/network/members
196 | [stars-shield]: https://img.shields.io/github/stars/atharmohammad/Code-N-Collab-Server.svg?style=for-the-badge
197 | [stars-url]: https://github.com/atharmohammad/Code-N-Collab-Server/stargazers
198 | [issues-shield]: https://img.shields.io/github/issues/atharmohammad/Code-N-Collab-Server.svg?style=for-the-badge
199 | [issues-url]: https://github.com/atharmohammad/Code-N-Collab-Server/issues
200 | [license-shield]: https://img.shields.io/github/license/atharmohammad/Code-N-Collab-Server.svg?style=for-the-badge
201 | [license-url]: https://github.com/atharmohammad/Code-N-Collab-Server/blob/master/LICENSE
202 | [linkedin-shield]: https://img.shields.io/badge/-LinkedIn-black.svg?style=for-the-badge&logo=linkedin&colorB=555
203 | [linkedin-url]: https://www.linkedin.com/in/athar-mohammad-34068a157/
204 |
205 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/src/utils/Contest.js:
--------------------------------------------------------------------------------
1 | const axios = require("axios");
2 |
3 | //Storing all the contest data
4 | let contests = [];
5 |
6 | //checking the contest existence and its validation
7 | const checkContest = (roomId, name, socketid) => {
8 | try{
9 | const contest = contests.find((con, index) => con.Id === roomId);
10 | let user = undefined;
11 | let usersCnt = 0;
12 |
13 | if (contest) {
14 | user = contest.UsersId.find((user, index) => user === name);
15 | usersCnt = contest.UsersId.length;
16 | }
17 |
18 | if (!contest) {
19 | return createContest(roomId, name, socketid);
20 | } else if (contest.Started && !user) {
21 | return { error: "Contest has Already Started !", contest: null };
22 | } else if (usersCnt == 4 && !user) {
23 | return { error: "Room is Full", contest: null };
24 | } else if (usersCnt < 4 || user) {
25 | return joinContest(roomId, name, socketid, contest);
26 | } else {
27 | return {
28 | error: "There might be an error! Please Try again !",
29 | contest: null,
30 | };
31 | }
32 | }catch(e){
33 | return {
34 | error: "There might be an error! Please Try again !",
35 | contest: null,
36 | };
37 | }
38 | };
39 |
40 | //Creating a contest
41 | const createContest = (roomId, name, socketid) => {
42 | try{
43 | //initialising the User object
44 | const user = {
45 | Name: name,
46 | Score: 0,
47 | SocketId: socketid,
48 | };
49 |
50 | //initialising the contest object
51 | const lockout = {
52 | Id: roomId,
53 | Users: [user],
54 | UsersId: [name],
55 | EndTime: null,
56 | Problems: [],
57 | Started: false,
58 | problemTags: [],
59 | minRating: 0,
60 | maxRating: 0,
61 | StartTime: new Date().getTime(),
62 | userToProblem: new Map(),
63 | };
64 |
65 | //Inserting the contest in the array
66 | contests.push(lockout);
67 | console.log("contest-created!");
68 | return { error: null, contest: lockout };
69 | }catch(e){
70 | return {
71 | error: "There might be an error! Please Try again !",
72 | contest: null,
73 | };
74 | }
75 |
76 | };
77 |
78 | //If contest already exist and user is valid then join the contest
79 | const joinContest = (roomId, name, socketid, co) => {
80 | try{
81 | //Finding the user , contest with in the array
82 | const contest = contests.find((con, index) => con.Id === roomId);
83 | const user = contest.Users.find((user, i) => user.Name === name);
84 | const userId = contest.UsersId.find((user, i) => user === name);
85 |
86 | //If no contest exist return
87 | if (!contest) {
88 | return;
89 | }
90 |
91 | //If both user and userId is available means the user hadn't exit the contest
92 | if (user && userId) {
93 | user.SocketId = socketid;
94 | return { error: null, contest: contest };
95 | } else if (user) {
96 | //If user joins the contest again after exiting
97 | user.SocketId = socketid;
98 | contest.UsersId.push(name);
99 | return { error: null, contest: contest };
100 | }
101 |
102 | //If new user joined the contest before starting
103 | const newUser = {
104 | Name: name,
105 | Score: 0,
106 | SocketId: socketid,
107 | };
108 |
109 | contest.UsersId.push(name);
110 | contest.Users.push(newUser);
111 |
112 | console.log("Joined the existing contest");
113 | return { error: null, contest: contest };
114 |
115 | }catch(e){
116 | return {
117 | error: "There might be an error! Please Try again !",
118 | contest: null,
119 | };
120 | }
121 | };
122 |
123 | //Removing the contest user on its exit
124 |
125 | const removeContestUser = ({ roomId, name }) => {
126 | //Finding the contest
127 | try {
128 | const contest = contests.find((con, index) => con.Id === roomId);
129 |
130 | if (!contest) {
131 | return;
132 | }
133 |
134 | //Filtering the userId for removal , user name will remain to show on leaderboard
135 | const UserIds = contest.UsersId;
136 | const users = UserIds.filter((id, i) => id !== name);
137 | contest.UsersId = users;
138 | console.log(contest, "removed user");
139 | return contest;
140 | } catch (e) {
141 | return {
142 | error: "There might be an error! Please Try again !",
143 | contest: null,
144 | };
145 | }
146 | };
147 |
148 | //starting the contest and fetching problems
149 | const startContest = ({
150 | room,
151 | problemTags,
152 | minRating,
153 | maxRating,
154 | maxDuration,
155 | problemArray,
156 | }) => {
157 | try{
158 | ////setting up problems////
159 | shuffle(problemArray);
160 |
161 | const problems = [];
162 | const problemLink = "https://codeforces.com/problemset/problem/";
163 | let problemCount = 0;
164 |
165 | if (!maxDuration || maxDuration < 10 || maxDuration > 120) {
166 | maxDuration = 30;
167 | }
168 |
169 | //Selecting the problems after filtering
170 | problemArray.every((problem, i) => {
171 | if (
172 | problem.rating >= parseInt(minRating) &&
173 | problem.rating <= parseInt(maxRating)
174 | ) {
175 | const link = problemLink + problem.contestId + "/" + problem.index + "/";
176 | problems.push({
177 | key: i,
178 | link: link,
179 | name: problem.name,
180 | points: problem.rating,
181 | solved: false,
182 | author: null,
183 | });
184 | problemCount++;
185 | if (problemCount == 5) return false;
186 |
187 | return true;
188 | }
189 | return true;
190 | });
191 | const curr_time = new Date();
192 | ////********/////////
193 | /////Setting up the contest////
194 | const contest = contests.find((cont, ind) => cont.Id === room);
195 | if (!contest) {
196 | return null;
197 | }
198 | contest.Started = true; //Starting the contest
199 | contest.EndTime = curr_time.getTime() + maxDuration * 60 * 1000;
200 | contest.Problems = problems;
201 | contest.problemTags = problemTags;
202 | contest.minRating = minRating;
203 | contest.maxRating = maxRating;
204 | /////***///////////
205 |
206 | console.log(contest);
207 | return contest;
208 | }catch(e){
209 | return {
210 | error: "There might be an error! Please Try again !",
211 | contest: null,
212 | };
213 | }
214 |
215 | };
216 |
217 | //To return a list of team members in a contest
218 | const getTeamMembers = (userIds) => {
219 | try{
220 | const newId = [];
221 | userIds.forEach((id) => {
222 | newId.push({ username: id });
223 | });
224 | return newId;
225 | }catch(e){
226 | return {
227 | error: "There might be an error! Please Try again !",
228 | contest: null,
229 | };
230 | }
231 |
232 | };
233 |
234 | //Creating the url to fetch problems with certaing filtering
235 | const createURL = (problemTags) => {
236 | try{
237 | const tags = problemTags.join(";");
238 | const URL = `https://codeforces.com/api/problemset.problems?tags=${tags}`;
239 | return URL;
240 | }catch(e){
241 | return {
242 | error: "There might be an error! Please Try again !",
243 | contest: null,
244 | };
245 | }
246 |
247 | };
248 |
249 | //Updating the leaderboard and problems in the contest
250 | const updateContest = async (roomId) => {
251 | try{
252 | const contest = contests.find((con, index) => con.Id === roomId);
253 | if (!contest) {
254 | return;
255 | }
256 |
257 | const unsolvedProblems = [];
258 |
259 | contest.Problems.forEach((prob, i) => {
260 | if (!contest.userToProblem.has(prob.name)) {
261 | unsolvedProblems.push(prob.name);
262 | }
263 | });
264 |
265 | console.log(unsolvedProblems);
266 |
267 | //Checking the problems solved by each user
268 | const promise = await contest.UsersId.map(async (user, i) => {
269 | const URL = `https://codeforces.com/api/user.status?handle=${user}&from=1&count=10`;
270 | try {
271 | const res = await axios.get(URL);
272 | unsolvedProblems.map((prob, j) => {
273 | checkIfProblemSolved(user, prob, roomId, res.data.result);
274 | });
275 | console.log("fullfilled");
276 | } catch (e) {
277 | console.log("rejected");
278 | }
279 | return;
280 | });
281 |
282 | //awaiting all the promises to return
283 | try {
284 | await Promise.all(promise);
285 | updateScores(roomId);
286 | } catch (e) {
287 | console.log("no such user");
288 | }
289 | return contest;
290 | }catch(e){
291 | return {
292 | error: "There might be an error! Please Try again !",
293 | contest: null,
294 | };
295 | }
296 | };
297 |
298 | //Validation and checking the problem solving before updating
299 | const checkIfProblemSolved = (user, unsolvedProblem, roomId, arr) => {
300 | try{
301 | const contest = contests.find((con, index) => con.Id === roomId);
302 |
303 | if (!contest) {
304 | return;
305 | }
306 |
307 | arr.every((prob, i) => {
308 | if (check(contest, unsolvedProblem, prob)) {
309 | if (
310 | contest.userToProblem.has(prob.problem.name) &&
311 | contest.userToProblem.get(prob.problem.name).time >
312 | prob.creationTimeSeconds
313 | ) {
314 | contest.userToProblem.set(prob.problem.name, {
315 | time: prob.creationTimeSeconds,
316 | author: user,
317 | points: prob.problem.rating,
318 | });
319 | } else if (!contest.userToProblem.has(prob.problem.name)) {
320 | contest.userToProblem.set(prob.problem.name, {
321 | time: prob.creationTimeSeconds,
322 | author: user,
323 | points: prob.problem.rating,
324 | });
325 | } else {
326 | console.log("error");
327 | }
328 | }
329 | });
330 | }catch(e){
331 | return {
332 | error: "There might be an error! Please Try again !",
333 | contest: null,
334 | };
335 | }
336 |
337 | };
338 |
339 | //Updating the scores for the leader board
340 | const updateScores = (roomId) => {
341 | try{
342 | const contest = contests.find((con, index) => con.Id === roomId);
343 |
344 | if (!contest) {
345 | return;
346 | }
347 |
348 | const score = new Map();
349 | contest.userToProblem.forEach((values, keys) => {
350 | if (score.has(values.author)) {
351 | const prevScore = score.get(values.author).points;
352 | score.set(values.author, { points: prevScore + values.points });
353 | } else {
354 | score.set(values.author, { points: values.points });
355 | }
356 | });
357 |
358 | contest.Users.forEach((user, i) => {
359 | if (score.has(user.Name)) {
360 | contest.Users[i].Score = score.get(user.Name).points;
361 | }
362 | });
363 |
364 | contest.Users.sort((a, b) => b.Score - a.Score);
365 |
366 | contest.Problems.map((problem, i) => {
367 | if (contest.userToProblem.has(problem.name)) {
368 | problem.solved = true;
369 | problem.author = contest.userToProblem.get(problem.name).author;
370 | }
371 | });
372 | }catch(e){
373 | return {
374 | error: "There might be an error! Please Try again !",
375 | contest: null,
376 | };
377 | }
378 |
379 | };
380 |
381 | //Checking the time when the problem was solved
382 | const check = (contest, unsolvedProblem, prob) => {
383 |
384 | try{
385 | const cftime = prob.creationTimeSeconds * 1000;
386 |
387 | return (
388 | prob.problem.name.trim().toLowerCase() ==
389 | unsolvedProblem.trim().toLowerCase() &&
390 | prob.verdict == "OK" &&
391 | cftime >= contest.StartTime &&
392 | cftime <= contest.EndTime
393 | );
394 | }catch(e){
395 | return {
396 | error: "There might be an error! Please Try again !",
397 | contest: null,
398 | };
399 | }
400 |
401 | };
402 |
403 | //Shuffling the problems array to pick random problems
404 | function shuffle(array) {
405 | for (var i = array.length - 1; i > 0; i--) {
406 | var j = Math.floor(Math.random() * (i + 1));
407 | var temp = array[i];
408 | array[i] = array[j];
409 | array[j] = temp;
410 | }
411 | }
412 |
413 | //Return the contest with specific roomId
414 | const getContest = (roomId) => {
415 | return contests.find((con, index) => con.Id === roomId);
416 | };
417 |
418 | const getContestLength = () => {
419 | return contests.length;
420 | };
421 |
422 |
423 | //Removes the contest if exceeds the 24hrs of time
424 | const deleteContests = () => {
425 | const curr_time = new Date().getTime();
426 | const deleteBeforeTime = curr_time - 24 * 60 * 60 * 1000;
427 | let temp = [];
428 | for (let i = 0; i < contests.length; i++) {
429 | if (contest[i].StartTime > deleteBeforeTime) temp.push(contests[i]);
430 | }
431 | contests = temp;
432 | };
433 |
434 | module.exports = {
435 | checkContest,
436 | removeContestUser,
437 | startContest,
438 | getTeamMembers,
439 | createURL,
440 | updateContest,
441 | getContest,
442 | getContestLength,
443 | deleteContests,
444 | };
445 |
--------------------------------------------------------------------------------