diff --git a/backend/src/controllers/poll.controller.js b/backend/src/controllers/poll.controller.js new file mode 100644 index 0000000..078822d --- /dev/null +++ b/backend/src/controllers/poll.controller.js @@ -0,0 +1,27 @@ +import { createPollService } from "../services/poll.service.js"; + +export async function createPollController(req, res) { + try { + const {title, description, options} = req.body; + const user = req.user; + const poll = await createPollService(title, description, options, user._id); + res.status(201).json({ + success: true, + message: "Poll created successfully", + data: poll, + }); + + } catch (err) { + if (err.statusCode) { + res.status(err.statusCode).json({ + success: false, + message: err.message, + }); + } else { + res.status(500).json({ + success: false, + message: err.message, + }); + } + } +} diff --git a/backend/src/controllers/user.controller.js b/backend/src/controllers/user.controller.js index ed822ec..969626e 100644 --- a/backend/src/controllers/user.controller.js +++ b/backend/src/controllers/user.controller.js @@ -53,3 +53,34 @@ export async function signinController(req, res) { } } } + +export const checkUser = async (req, res) => { + try{ + res.json({ + success : true, + message : "Found", + data : req.user + }) + } + catch(err){ + res.status(500).json({ + success : false, + message : err.message + }) + } +} + +export async function logOutController(req, res) { + try{ + res.clearCookie("livepoll-access-token").status(200).json({ + success : true, + message : "User logged out successfully." + }) + } + catch(err){ + res.status(500).json({ + success : false, + message : err.message + }) + } +} \ No newline at end of file diff --git a/backend/src/index.js b/backend/src/index.js index 8cf5b72..56909b3 100644 --- a/backend/src/index.js +++ b/backend/src/index.js @@ -8,6 +8,7 @@ import userRouter from './routes/v1/user.route.js'; import swaggerDocs from '../swagger.js'; import swaggerUi from 'swagger-ui-express'; import cookieParser from "cookie-parser"; +import pollRouter from './routes/v1/poll.route.js'; const app = express(); @@ -27,6 +28,8 @@ app.use(express.json()) app.get("/ping", (_req, res) => { res.json({ message: "pong" }) }) + +app.use("/api/v1/poll", pollRouter); app.use("/api/v1/user", userRouter); await connectDB(); diff --git a/backend/src/models/poll.model.js b/backend/src/models/poll.model.js new file mode 100644 index 0000000..094a51d --- /dev/null +++ b/backend/src/models/poll.model.js @@ -0,0 +1,29 @@ +import mongoose, { Schema } from "mongoose"; + +const pollSchema = new Schema({ + title : { + type : String, + required : true, + trim : true + }, + description : { + type : String, + required : true, + trim : true + }, + creatorId : { + type : Schema.Types.ObjectId, + ref : "User", + required : true + }, + options : [ + { + type : Object, + required : true, + } + ] +}, {timestamps : true}); + + +const PollModel = mongoose.model("Poll", pollSchema); +export default PollModel; \ No newline at end of file diff --git a/backend/src/repositories/poll.repo.js b/backend/src/repositories/poll.repo.js new file mode 100644 index 0000000..2d5f9c6 --- /dev/null +++ b/backend/src/repositories/poll.repo.js @@ -0,0 +1,11 @@ +import PollModel from "../models/poll.model.js"; + +export async function createPollByData(data) { + try { + const poll = await PollModel.create(data); + return poll; + } + catch(err){ + throw err; + } +} \ No newline at end of file diff --git a/backend/src/routes/v1/poll.route.js b/backend/src/routes/v1/poll.route.js new file mode 100644 index 0000000..28cdd2e --- /dev/null +++ b/backend/src/routes/v1/poll.route.js @@ -0,0 +1,63 @@ +import express from "express"; +import { verifyToken } from "../../middlwares/verifyToken.js"; +import { createPollController } from "../../controllers/poll.controller.js"; +import pollDataSchema from "../../validations/pollDataValidation.js"; +import validator from "../../validations/validator.js"; +const pollRouter = express.Router(); + +/** + * @swagger + * /poll/test: + * get: + * summary: Test route for poll + * tags: [Poll] + * responses: + * 200: + * description: Success + */ +pollRouter.get("/test", (req, res) => { + res.json({ + success : true, + message : "Poll route is working✔️" + }) +}) + +/** + * @swagger + * /poll/create: + * post: + * summary: Create poll + * tags: [Poll] + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * properties: + * title: + * type: string + * description: + * type: string + * options: + * type: array + * items: + * type: string + * required: + * - title + * - description + * - options + * responses: + * 201: + * description: Poll created successfully + * 400: + * description: Validation error + * 401: + * description: Unauthorized + * 500: + * description: Internal server error + * + */ +pollRouter.post("/create", validator(pollDataSchema), verifyToken, createPollController); + +export default pollRouter; \ No newline at end of file diff --git a/backend/src/routes/v1/user.route.js b/backend/src/routes/v1/user.route.js index 5273c8d..b3305b3 100644 --- a/backend/src/routes/v1/user.route.js +++ b/backend/src/routes/v1/user.route.js @@ -1,5 +1,5 @@ import express from "express"; -import { signinController, signupController } from "../../controllers/user.controller.js"; +import { checkUser, logOutController, signinController, signupController } from "../../controllers/user.controller.js"; import validate from "../../validations/validator.js"; import signupSchema from "../../validations/signupValidation.js"; import signinSchema from "../../validations/signinValidation.js"; @@ -81,20 +81,18 @@ userRouter.post("/signup", validate(signupSchema), signupController); */ userRouter.post("/signin", validate(signinSchema), signinController); -userRouter.get("/user", verifyToken, (req, res) => { - try{ - res.json({ - success : true, - message : "Found", - data : req.user - }) - } - catch(err){ - res.status(500).json({ - success : false, - message : err.message - }) - } -}) +userRouter.get("/logout", logOutController); + +/** + * @swagger + * /check: + * get: + * summary: Get user details + * tags: [User] + * responses: + * 200: + * description: Success + */ +userRouter.get("/check", verifyToken, checkUser); export default userRouter; \ No newline at end of file diff --git a/backend/src/services/poll.service.js b/backend/src/services/poll.service.js new file mode 100644 index 0000000..536ef20 --- /dev/null +++ b/backend/src/services/poll.service.js @@ -0,0 +1,23 @@ +import mongoose from "mongoose"; +import { createPollByData } from "../repositories/poll.repo.js"; + +export async function createPollService(title, description, options, userId) { + try { + const optionsData = options.map(option => ({ + name: option, + _id : new mongoose.Types.ObjectId() + })); + + const data = { + title, + description, + options : optionsData, + creatorId: userId + } + const poll = await createPollByData(data); + return poll; + } + catch(err){ + throw err; + } +} \ No newline at end of file diff --git a/backend/src/validations/pollDataValidation.js b/backend/src/validations/pollDataValidation.js new file mode 100644 index 0000000..9f39381 --- /dev/null +++ b/backend/src/validations/pollDataValidation.js @@ -0,0 +1,36 @@ +import { z } from "zod"; + +const pollOptionSchema = z + .string({ + required_error: "A Option is required", + invalid_type_error: "A Option must be a string", + }) + .min(1, "A Option must be at least 1 characters long") + .max(50, "A Option must be at most 50 characters long") + .trim(); + +const pollDataSchema = z.object({ + title: z + .string({ + required_error: "Title is required", + invalid_type_error: "Title must be a string", + }) + .min(3, "Title must be at least 3 characters long") + .max(50, "Title must be at most 50 characters long") + .trim(), + description: z + .string({ + required_error: "Description is required", + invalid_type_error: "Description must be a string", + }) + .min(3, "Description must be at least 3 characters long") + .max(500, "Description must be at most 500 characters long") + .trim(), + options: z + .array(pollOptionSchema) + .min(2, "Poll must have at least 2 options") + .max(10, "Poll can have at most 10 options"), +}); + + +export default pollDataSchema; \ No newline at end of file