diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 460ae61..6e3dc27 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,3 +13,8 @@ jobs: steps: - uses: actions/checkout@v2 - run: cd client && yarn --frozen-lockfile && yarn lint + lint_server: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - run: cd server && yarn --frozen-lockfile && yarn lint diff --git a/.husky/pre-commit b/.husky/pre-commit index ea2e0e0..2a952ae 100755 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -3,3 +3,6 @@ cd client npx lint-staged + +cd ../server +yarn lint \ No newline at end of file diff --git a/server/.eslintignore b/server/.eslintignore new file mode 100644 index 0000000..3881e74 --- /dev/null +++ b/server/.eslintignore @@ -0,0 +1,3 @@ +node_modules/ +dist/ +.idea diff --git a/server/.eslintrc.json b/server/.eslintrc.json new file mode 100644 index 0000000..09213d7 --- /dev/null +++ b/server/.eslintrc.json @@ -0,0 +1,25 @@ +{ + "env": { + "node": true + }, + "extends": [ + "standard", + "eslint:recommended", + "plugin:@typescript-eslint/eslint-recommended", + "plugin:@typescript-eslint/recommended"], + "parser": "@typescript-eslint/parser", + "parserOptions": { + "ecmaVersion": 13, + "sourceType": "module" + }, + "plugins": ["@typescript-eslint"], + "rules": { + "no-use-before-define": "off", + "no-unused-vars": "warn", + "no-console": "off", + "camelcase": "off", + "no-throw-literal": "warn" + }, + "settings": { + } +} diff --git a/server/package.json b/server/package.json index 33fdf3c..273b04f 100644 --- a/server/package.json +++ b/server/package.json @@ -8,7 +8,9 @@ "prestart": "tsc", "start": "node .", "dev": "nodemon --watch src -e ts,ejs --exec yarn run start", - "build": "tsc --project ./" + "build": "tsc --project ./", + "lint": "eslint --ext .ts .", + "lint:fix": "eslint --ext .ts --fix ." }, "repository": "git+https://github.com/hiimchrislim/QuizVotingSystem.git", "author": "Chris, Akshit, Shubh, Ilir", @@ -42,6 +44,7 @@ "eslint-plugin-import": "^2.25.2", "eslint-plugin-node": "^11.1.0", "eslint-plugin-promise": "^5.1.1", + "husky": "^8.0.1", "nodemon": "^2.0.14", "prettier": "^2.4.1", "socket.io-client": "^4.3.2", diff --git a/server/src/controllers/pollController.ts b/server/src/controllers/pollController.ts index ad06951..51abe7f 100644 --- a/server/src/controllers/pollController.ts +++ b/server/src/controllers/pollController.ts @@ -1,76 +1,77 @@ -import { Poll } from "../db/schema"; -import { PollModel, StudentModel } from "../db/mogoose"; -import { io } from "../socket"; -import { client } from "../redis"; -import { customAlphabet } from "nanoid/async"; -import { pollResult } from "./socketController"; -const nanoid = customAlphabet("qwertyuiopasdfghjklzxcvbnm1234567890", 6); +import { Poll } from '../db/schema' +import { PollModel, StudentModel } from '../db/mogoose' +import { io } from '../socket' +import { client } from '../redis' +import { customAlphabet } from 'nanoid/async' +import { pollResult } from './socketController' +const nanoid = customAlphabet('qwertyuiopasdfghjklzxcvbnm1234567890', 6) // set poll code expiry to 1 day -const expiry = 60 * 60 * 24; +const expiry = 60 * 60 * 24 -async function createPoll(poll: Poll) { - if (!poll.hasOwnProperty("courseCode")) - return { status: 400, data: { message: "courseCode is required" } }; - const promise = [nanoid(), PollModel.create(poll)] as const; - const result = await Promise.all(promise); - const pollId = result[1]["_id"].toString(); - const pollCode = result[0]; - await client.set(pollCode, pollId, { EX: expiry, NX: true }); +async function createPoll (poll: Poll) { + if (!poll.courseCode) { return { status: 400, data: { message: 'courseCode is required' } } } + const promise = [nanoid(), PollModel.create(poll)] as const + const result = await Promise.all(promise) + const pollId = result[1]._id.toString() + const pollCode = result[0] + await client.set(pollCode, pollId, { EX: expiry, NX: true }) - return { status: 201, data: { pollCode, pollId } }; + return { status: 201, data: { pollCode, pollId } } } -async function changePollStatus(pollId: string, hasStarted: boolean) { - if (typeof hasStarted !== "boolean") +async function changePollStatus (pollId: string, hasStarted: boolean) { + if (typeof hasStarted !== 'boolean') { return { status: 400, - data: { message: "hasStarted should be boolean" }, - }; - const currSequence = await client.get(pollId); - console.log("currSequence", currSequence); - let newSequence; + data: { message: 'hasStarted should be boolean' } + } + } + const currSequence = await client.get(pollId) + console.log('currSequence', currSequence) + let newSequence // on every new start increment the sequence counter if (hasStarted) { - if (currSequence == null) newSequence = 0; - else newSequence = parseInt(currSequence); - if (newSequence < 0) newSequence *= -1; - newSequence++; - console.log("newSequence", newSequence); + if (currSequence == null) newSequence = 0 + else newSequence = parseInt(currSequence) + if (newSequence < 0) newSequence *= -1 + newSequence++ + console.log('newSequence', newSequence) await client.set(pollId, newSequence.toString(), { - EX: expiry, - }); - const result = await pollResult(pollId, newSequence); - io.to(pollId).emit("result", result); - } - // for every stop make the current counter negative to indicate that it is not an active sequence - else { + EX: expiry + }) + const result = await pollResult(pollId, newSequence) + io.to(pollId).emit('result', result) + } else { + // for every stop make the current counter negative to indicate that it is not an active sequence if (currSequence != null) { - newSequence = parseInt(currSequence) * -1; + newSequence = parseInt(currSequence) * -1 await client.set(pollId, newSequence.toString(), { - EX: expiry, - }); + EX: expiry + }) } } - io.to(pollId).emit("pollStarted", hasStarted); + io.to(pollId).emit('pollStarted', hasStarted) - return { status: 200, data: { message: "poll status successfully changed" } }; + return { status: 200, data: { message: 'poll status successfully changed' } } } -async function getStudents(courseCode: string, startTime: Date, endTime: Date) { +async function getStudents (courseCode: string, startTime: Date, endTime: Date) { + // TODO Already addressed in TODO bellow + // eslint-disable-next-line no-useless-catch try { - const pollDoc = await PollModel.find({ courseCode }); - let promises: Promise[] = []; - let responses: any[] = []; + const pollDoc = await PollModel.find({ courseCode }) + const promises: Promise[] = [] + const responses: any[] = [] pollDoc.forEach((element) => { promises.push( StudentModel.aggregate([ { $match: { pollId: element._id.toString(), - timestamp: { $gte: startTime, $lte: endTime }, - }, + timestamp: { $gte: startTime, $lte: endTime } + } }, { $project: { @@ -81,57 +82,54 @@ async function getStudents(courseCode: string, startTime: Date, endTime: Date) { utorid: 1, timestamp: { $dateToString: { - date: "$timestamp", - timezone: "America/Toronto" - }, + date: '$timestamp', + timezone: 'America/Toronto' + } }, pollName: element.name, description: element.description, - answer: 1, - }, - }, + answer: 1 + } + } ]).then((data) => { data.forEach((val) => { - responses.push(val); - }); + responses.push(val) + }) }) - ); - }); - await Promise.all(promises); - console.log(responses); - return { responses }; + ) + }) + await Promise.all(promises) + console.log(responses) + return { responses } } catch (err) { /** * TODO: Add error handler */ - throw err; + throw err } } -async function getPollStatus(pollId: any) { - if (pollId === null || pollId === undefined || typeof pollId !== "string") - return { status: 400, data: { message: "Invalid poll Id" } }; - const result = await client.get(pollId); - const pollStarted = result === null ? false : parseInt(result) > 0; - return { status: 200, data: { pollStarted } }; +async function getPollStatus (pollId: any) { + if (pollId === null || pollId === undefined || typeof pollId !== 'string') { return { status: 400, data: { message: 'Invalid poll Id' } } } + const result = await client.get(pollId) + const pollStarted = result === null ? false : parseInt(result) > 0 + return { status: 200, data: { pollStarted } } } -async function getResult(pollId: any) { - if (pollId === null || pollId === undefined || typeof pollId !== "string") - return { status: 400, data: { message: "Invalid poll Id" } }; - const currSequence = await client.get(pollId); - const result = await pollResult(pollId, parseInt(currSequence)); - return { status: 200, data: { ...result } }; +async function getResult (pollId: any) { + if (pollId === null || pollId === undefined || typeof pollId !== 'string') { return { status: 400, data: { message: 'Invalid poll Id' } } } + const currSequence = await client.get(pollId) + const result = await pollResult(pollId, parseInt(currSequence)) + return { status: 200, data: { ...result } } } -async function endForever(pollCode: string) { - if (pollCode === null || pollCode === undefined) - return { status: 400, data: { message: "Invalid poll code" } }; - const pollId = await client.get(pollCode); - await Promise.all([client.del(pollCode), client.del(pollId)]); - io.to(pollId).emit("end", true); - io.of("/").in(pollId).disconnectSockets(); - return { status: 200, data: { message: "Poll closed" } }; +async function endForever (pollCode: string) { + if (pollCode === null || pollCode === undefined) { return { status: 400, data: { message: 'Invalid poll code' } } } + const pollId = await client.get(pollCode) + await Promise.all([client.del(pollCode), client.del(pollId)]) + io.to(pollId).emit('end', true) + io.of('/').in(pollId).disconnectSockets() + return { status: 200, data: { message: 'Poll closed' } } } export { @@ -140,5 +138,5 @@ export { getStudents, getPollStatus, getResult, - endForever, -}; + endForever +} diff --git a/server/src/controllers/socketController.ts b/server/src/controllers/socketController.ts index ad7b023..71b9a39 100644 --- a/server/src/controllers/socketController.ts +++ b/server/src/controllers/socketController.ts @@ -1,75 +1,71 @@ -import { StudentModel } from "../db/mogoose"; -import { io } from "../socket"; -import { Socket } from "socket.io"; -import { client } from "../redis"; +import { StudentModel } from '../db/mogoose' +import { io } from '../socket' +import { Socket } from 'socket.io' +import { client } from '../redis' -async function join(socket: Socket, pollCode: string) { +async function join (socket: Socket, pollCode: string) { try { - console.log(`join: ${socket.id}`); - if (pollCode === null || pollCode === undefined) - throw { code: 1, message: "Invalid poll code" }; - const pollId = await client.get(pollCode); - console.log(pollId); - if (pollId === null) throw { code: 1, message: "Invalid poll code" }; + console.log(`join: ${socket.id}`) + if (pollCode === null || pollCode === undefined) { throw { code: 1, message: 'Invalid poll code' } } + const pollId = await client.get(pollCode) + console.log(pollId) + if (pollId === null) throw { code: 1, message: 'Invalid poll code' } // ensure that socket is connected to 1 room (other than the default room) socket.rooms.forEach((room) => { - if (room !== socket.id) socket.leave(room); - }); + if (room !== socket.id) socket.leave(room) + }) - const currSequence = await client.get(pollId); + const currSequence = await client.get(pollId) const hasStarted = - currSequence == null ? false : parseInt(currSequence) > 0; - console.log("Has Started", hasStarted); - socket.join(pollId); - socket.data["pollId"] = pollId; - io.to(socket.id).emit("pollStarted", hasStarted); + currSequence == null ? false : parseInt(currSequence) > 0 + console.log('Has Started', hasStarted) + socket.join(pollId) + socket.data.pollId = pollId + io.to(socket.id).emit('pollStarted', hasStarted) } catch (err) { - console.log(err); - io.to(socket.id).emit("error", err); + console.log(err) + io.to(socket.id).emit('error', err) } } -async function pollResult(pollId: string, sequence: number) { +async function pollResult (pollId: string, sequence: number) { try { const result = await StudentModel.aggregate([ { $match: { pollId, sequence } }, { $facet: { - result: [{ $group: { _id: "$answer", count: { $sum: 1 } } }], - totalVotes: [{ $count: "totalVotes" }], - }, - }, + result: [{ $group: { _id: '$answer', count: { $sum: 1 } } }], + totalVotes: [{ $count: 'totalVotes' }] + } + } ]).then((data: any): any => { return { result: data[0].result, totalVotes: - data[0].totalVotes.length > 0 ? data[0].totalVotes[0].totalVotes : 0, - }; - }); - console.log(result); - return result; + data[0].totalVotes.length > 0 ? data[0].totalVotes[0].totalVotes : 0 + } + }) + console.log(result) + return result } catch (err) { - console.log(err); + console.log(err) } } -async function vote(socket: Socket, answer: number, utorid: string) { +async function vote (socket: Socket, answer: number, utorid: string) { try { - console.log(`vote: ${socket.id}`); - let pollId = socket.data.pollId; - if (pollId === null || pollId === undefined) - throw { code: 1, message: "haven't joined any room" }; - const currSequence = await client.get(pollId); - console.log(currSequence); + console.log(`vote: ${socket.id}`) + const pollId = socket.data.pollId + if (pollId === null || pollId === undefined) { throw { code: 1, message: "haven't joined any room" } } + const currSequence = await client.get(pollId) + console.log(currSequence) if (currSequence === null || parseInt(currSequence) < 0) { - throw { code: 2, message: "Poll not live yet" }; + throw { code: 2, message: 'Poll not live yet' } } - if (utorid === undefined || utorid === null) - throw { code: 1, message: "Invalid utorid" }; - if (answer === undefined || answer === null) - throw { code: 2, message: "Invalid answer" }; + if (utorid === undefined || utorid === null) { throw { code: 1, message: 'Invalid utorid' } } + if (answer === undefined || answer === null) { throw { code: 2, message: 'Invalid answer' } } await StudentModel.updateOne( { @@ -81,19 +77,19 @@ async function vote(socket: Socket, answer: number, utorid: string) { utorid, sequence: parseInt(currSequence), answer, - timestamp: new Date(), + timestamp: new Date() }, { upsert: true } - ); + ) pollResult(pollId, parseInt(currSequence)).then((data) => { - io.to(pollId).emit("result", data); - }); - io.to(socket.id).emit("ack", answer); - return; + io.to(pollId).emit('result', data) + }) + io.to(socket.id).emit('ack', answer) + return } catch (err) { - console.log(err); - io.to(socket.id).emit("error", err); + console.log(err) + io.to(socket.id).emit('error', err) } } -export { vote, join, pollResult }; +export { vote, join, pollResult } diff --git a/server/src/controllers/userController.ts b/server/src/controllers/userController.ts index 82d4388..02d258d 100644 --- a/server/src/controllers/userController.ts +++ b/server/src/controllers/userController.ts @@ -1,11 +1,10 @@ -import { client } from "../redis"; +import { client } from '../redis' -async function getUser(utorid: any) { - if (utorid === null || utorid === undefined || typeof utorid !== "string") - return { status: 400, data: { message: "Invalid utorid" } }; - let userType = await client.get(utorid); - if (userType == null) userType = "student"; - return { status: 200, data: { userType } }; +async function getUser (utorid: any) { + if (utorid === null || utorid === undefined || typeof utorid !== 'string') { return { status: 400, data: { message: 'Invalid utorid' } } } + let userType = await client.get(utorid) + if (userType == null) userType = 'student' + return { status: 200, data: { userType } } } -export { getUser }; +export { getUser } diff --git a/server/src/db/mogoose.ts b/server/src/db/mogoose.ts index ead7cf7..10ca3e2 100644 --- a/server/src/db/mogoose.ts +++ b/server/src/db/mogoose.ts @@ -1,16 +1,16 @@ -import { model, connect, connection } from "mongoose"; +import { model, connect, connection } from 'mongoose' import { PollDocument, pollSchema, StudentDocument, - studentSchema, -} from "./schema"; + studentSchema +} from './schema' -export const PollModel = model("Poll", pollSchema); -export const StudentModel = model("Student", studentSchema); +export const PollModel = model('Poll', pollSchema) +export const StudentModel = model('Student', studentSchema) connect(process.env.MONGODB_URL).catch((err) => { - throw err; -}); + throw err +}) -export const db = connection; +export const db = connection diff --git a/server/src/db/schema.ts b/server/src/db/schema.ts index 4f36ea4..2a9d99f 100644 --- a/server/src/db/schema.ts +++ b/server/src/db/schema.ts @@ -1,4 +1,4 @@ -import { Schema, Document, Types } from "mongoose"; +import { Schema, Document, Types } from 'mongoose' /** * student subdocument. Used as nested objects in PollResults schema @@ -18,25 +18,25 @@ export interface StudentDocument extends Student, Document {} export const studentSchema = new Schema({ utorid: { type: String, - required: true, + required: true }, answer: { type: Number, - required: true, + required: true }, timestamp: { type: Date, - required: true, + required: true }, sequence: { type: Number, - required: true, + required: true }, pollId: { type: String, - required: true, - }, -}); + required: true + } +}) /** * Poll schema. Represents how a single poll will look like @@ -64,7 +64,7 @@ export const pollSchema = new Schema({ description: String, courseCode: { type: String, required: true }, created: { type: Date, required: true }, - options: Number, -}); + options: Number +}) -export const ObjectId = Types.ObjectId; +export const ObjectId = Types.ObjectId diff --git a/server/src/redis.ts b/server/src/redis.ts index 3bf6d41..9a39ad8 100644 --- a/server/src/redis.ts +++ b/server/src/redis.ts @@ -1,30 +1,30 @@ -import { createClient } from "redis"; -import fs from "fs"; -import path from "path"; -import readline from "readline"; +import { createClient } from 'redis' +import fs from 'fs' +import path from 'path' +import readline from 'readline' const client = createClient({ - url: process.env.REDIS_URL, + url: process.env.REDIS_URL }); (async () => { - client.on("error", (err) => console.log("Redis Client Error", err)); + client.on('error', (err) => console.log('Redis Client Error', err)) - await client.connect(); - console.log("connected to redis"); + await client.connect() + console.log('connected to redis') const readstream = fs.createReadStream( path.join(__dirname, process.env.WHITELIST) - ); + ) const rl = readline.createInterface({ input: readstream, - crlfDelay: Infinity, - }); + crlfDelay: Infinity + }) for await (const line of rl) { - console.log(line.trim()); - client.set(line.trim(), "instructor"); + console.log(line.trim()) + client.set(line.trim(), 'instructor') } -})(); +})() -export { client }; +export { client } diff --git a/server/src/routes/pollRoute.ts b/server/src/routes/pollRoute.ts index 7a58775..450a34d 100644 --- a/server/src/routes/pollRoute.ts +++ b/server/src/routes/pollRoute.ts @@ -1,92 +1,92 @@ -import { Router } from "express"; +import { Router } from 'express' import { changePollStatus, createPoll, getPollStatus, getResult, getStudents, - endForever, -} from "../controllers/pollController"; + endForever +} from '../controllers/pollController' -const pollRouter = Router(); +const pollRouter = Router() -pollRouter.post("/", async (req, res) => { - const { name, description, courseCode, options } = req.body; +pollRouter.post('/', async (req, res) => { + const { name, description, courseCode, options } = req.body try { const poll = { name, description, courseCode: courseCode.toUpperCase(), options, - created: new Date(), - }; - const result = await createPoll(poll); - return res.status(result.status).send(result.data); + created: new Date() + } + const result = await createPoll(poll) + return res.status(result.status).send(result.data) } catch (err) { - console.log(err); - return res.status(500).send({ message: "Internal Server Error" }); + console.log(err) + return res.status(500).send({ message: 'Internal Server Error' }) } -}); +}) -pollRouter.patch("/:pollId", async (req, res) => { - const { pollId } = req.params; - const { hasStarted } = req.body; +pollRouter.patch('/:pollId', async (req, res) => { + const { pollId } = req.params + const { hasStarted } = req.body try { - const result = await changePollStatus(pollId, hasStarted); - return res.status(result.status).send(result.data); + const result = await changePollStatus(pollId, hasStarted) + return res.status(result.status).send(result.data) } catch (err) { - console.log(err); - return res.status(500).send({ message: "Internal Server Error" }); + console.log(err) + return res.status(500).send({ message: 'Internal Server Error' }) } -}); +}) -pollRouter.get("/students", async (req, res) => { - const courseCode = req.query.courseCode as string; - const startTime = req.query.startTime as string; - const endTime = req.query.endTime as string; - //const { startTime, endTime } = req.body; - console.log(req.query); - console.log(startTime, endTime); +pollRouter.get('/students', async (req, res) => { + const courseCode = req.query.courseCode as string + const startTime = req.query.startTime as string + const endTime = req.query.endTime as string + // const { startTime, endTime } = req.body; + console.log(req.query) + console.log(startTime, endTime) try { const result = await getStudents( courseCode.toUpperCase(), new Date(startTime), new Date(endTime) - ); - return res.status(200).send(result); + ) + return res.status(200).send(result) } catch (err) { - return res.status(500).send({ message: "Failed to find students" }); + return res.status(500).send({ message: 'Failed to find students' }) } -}); +}) -pollRouter.get("/status", async (req, res) => { - const { pollId } = req.query; +pollRouter.get('/status', async (req, res) => { + const { pollId } = req.query try { - const result = await getPollStatus(pollId); - return res.status(result.status).send(result.data); + const result = await getPollStatus(pollId) + return res.status(result.status).send(result.data) } catch (err) { - return res.status(500).send({ message: "Internal Server Error" }); + return res.status(500).send({ message: 'Internal Server Error' }) } -}); +}) -pollRouter.get("/result", async (req, res) => { - const { pollId } = req.query; +pollRouter.get('/result', async (req, res) => { + const { pollId } = req.query try { - const result = await getResult(pollId); - return res.status(result.status).send(result.data); + const result = await getResult(pollId) + return res.status(result.status).send(result.data) } catch (err) { - return res.status(500).send({ message: "Internal Server Error" }); + return res.status(500).send({ message: 'Internal Server Error' }) } -}); +}) -pollRouter.patch("/end/:pollCode", async (req, res) => { - const { pollCode } = req.params; +pollRouter.patch('/end/:pollCode', async (req, res) => { + const { pollCode } = req.params try { - const result = await endForever(pollCode); - return res.status(result.status).send(result.data); + const result = await endForever(pollCode) + return res.status(result.status).send(result.data) } catch (err) { - return res.status(500).send({ message: "Internal Server Error" }); + return res.status(500).send({ message: 'Internal Server Error' }) } -}); +}) -export default pollRouter; +export default pollRouter diff --git a/server/src/routes/userRoutes.ts b/server/src/routes/userRoutes.ts index 0057905..cb5d0a9 100644 --- a/server/src/routes/userRoutes.ts +++ b/server/src/routes/userRoutes.ts @@ -1,16 +1,16 @@ -import { Router } from "express"; -import { getUser } from "../controllers/userController"; +import { Router } from 'express' +import { getUser } from '../controllers/userController' -const userRouter = Router(); +const userRouter = Router() -userRouter.get("/", async (req, res) => { +userRouter.get('/', async (req, res) => { try { - const result = await getUser(req.headers.utorid); - return res.status(result.status).send(result.data); + const result = await getUser(req.headers.utorid) + return res.status(result.status).send(result.data) } catch (err) { - console.log(err); - return res.status(500).send({ message: "Internal Server Error" }); + console.log(err) + return res.status(500).send({ message: 'Internal Server Error' }) } -}); +}) -export default userRouter; +export default userRouter diff --git a/server/src/server.ts b/server/src/server.ts index 5314d71..4050c02 100644 --- a/server/src/server.ts +++ b/server/src/server.ts @@ -1,62 +1,62 @@ -"use strict"; +'use strict' -import * as dotenv from "dotenv"; -dotenv.config(); -import express from "express"; -import cors from "cors"; -import cookieParser from "cookie-parser"; -import { io } from "./socket"; -import pollRouter from "./routes/pollRoute"; -import { db } from "./db/mogoose"; -import userRouter from "./routes/userRoutes"; -import { getUser } from "./controllers/userController"; -db.on("open", () => { - console.log("Connected to mongo"); -}); +import * as dotenv from 'dotenv' +import express from 'express' +import cors from 'cors' +import cookieParser from 'cookie-parser' +import { io } from './socket' +import pollRouter from './routes/pollRoute' +import { db } from './db/mogoose' +import userRouter from './routes/userRoutes' +import { getUser } from './controllers/userController' +dotenv.config() +db.on('open', () => { + console.log('Connected to mongo') +}) // starting the express server -const app = express(); -const port = process.env.PORT || 5000; +const app = express() +const port = process.env.PORT || 5000 // parse cookies and body and enable cors app.use( cors({ origin: process.env.FRONTEND_URL, credentials: true, - methods: "GET,POST,DELETE,PATCH", + methods: 'GET,POST,DELETE,PATCH' }) -); -app.use(express.urlencoded({ extended: true })); -app.use(express.json()); -app.use(cookieParser()); -app.use("/user", userRouter); +) +app.use(express.urlencoded({ extended: true })) +app.use(express.json()) +app.use(cookieParser()) +app.use('/user', userRouter) app.use(async (req, res, next) => { try { - const userType = await getUser(req.headers.utorid); - if (userType.data.userType === "instructor") next(); - else next(new Error("Forbidden User")); + const userType = await getUser(req.headers.utorid) + if (userType.data.userType === 'instructor') next() + else next(new Error('Forbidden User')) } catch (err) { - next(new Error("Forbidden User")); + next(new Error('Forbidden User')) } -}); -app.use("/poll", pollRouter); +}) +app.use('/poll', pollRouter) const server = app.listen(port, () => { - console.log("Listening on http://localhost:" + port); - io.attach(server); + console.log('Listening on http://localhost:' + port) + io.attach(server) io.use(async (socket, next) => { try { - if (socket.handshake.headers.utorid != undefined) { - socket.data["utorid"] = socket.handshake.headers.utorid; - next(); - return; - } else if (socket.data.utorid != undefined) { - next(); - return; + if (socket.handshake.headers.utorid) { + socket.data.utorid = socket.handshake.headers.utorid + next() + return + } else if (socket.data.utorid) { + next() + return } - next(new Error("Not Authorized")); + next(new Error('Not Authorized')) } catch (err) { - next(new Error("Not Authorized")); + next(new Error('Not Authorized')) } - }); -}); + }) +}) diff --git a/server/src/socket.ts b/server/src/socket.ts index 09dac3a..781e940 100644 --- a/server/src/socket.ts +++ b/server/src/socket.ts @@ -1,42 +1,42 @@ // socket setup -import { Server, Socket } from "socket.io"; -import { vote, join } from "./controllers/socketController"; -import { RateLimiterMemory } from "rate-limiter-flexible"; +import { Server, Socket } from 'socket.io' +import { vote, join } from './controllers/socketController' +import { RateLimiterMemory } from 'rate-limiter-flexible' const rateLimiter = new RateLimiterMemory({ points: 5, // 5 points connections - duration: 3, // per second -}); + duration: 3 // per second +}) const io = new Server({ - path: "/socket.io", - cors: { origin: process.env.FRONTEND_URL, credentials: true }, -}); + path: '/socket.io', + cors: { origin: process.env.FRONTEND_URL, credentials: true } +}) // log the socket id when client socket connects for the first time -io.on("connection", (socket: Socket) => { - console.log(`connect: ${socket.id}`); +io.on('connection', (socket: Socket) => { + console.log(`connect: ${socket.id}`) // let the socket join rooms once connected - socket.on("join", async (pollCode: string) => { + socket.on('join', async (pollCode: string) => { try { - await rateLimiter.consume(socket.data.utorid); - await join(socket, pollCode); + await rateLimiter.consume(socket.data.utorid) + await join(socket, pollCode) // let the socket vote in the connected room - socket.on("vote", async (answer: number) => { + socket.on('vote', async (answer: number) => { try { - await rateLimiter.consume(socket.data.utorid); - await vote(socket, answer, socket.data.utorid); + await rateLimiter.consume(socket.data.utorid) + await vote(socket, answer, socket.data.utorid) } catch (err) { - socket.emit("error", { code: 2, message: "retry again later" }); + socket.emit('error', { code: 2, message: 'retry again later' }) } - }); + }) } catch (err) { - socket.emit("error", { code: 2, message: "retry again later" }); + socket.emit('error', { code: 2, message: 'retry again later' }) } - }); -}); + }) +}) -export { io }; +export { io } diff --git a/server/yarn.lock b/server/yarn.lock index 877a989..9e3ae79 100644 --- a/server/yarn.lock +++ b/server/yarn.lock @@ -1434,6 +1434,11 @@ http-errors@~1.7.2: statuses ">= 1.5.0 < 2" toidentifier "1.0.0" +husky@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/husky/-/husky-8.0.1.tgz#511cb3e57de3e3190514ae49ed50f6bc3f50b3e9" + integrity sha512-xs7/chUH/CKdOCs7Zy0Aev9e/dKOMZf3K1Az1nar3tzlv0jfqnYtu235bstsWTmXOR0EfINrPa97yy4Lz6RiKw== + iconv-lite@0.4.24: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"