diff --git a/backend/src/config/veriables.js b/backend/src/config/veriables.js index 64a49c7..fc8e3c5 100644 --- a/backend/src/config/veriables.js +++ b/backend/src/config/veriables.js @@ -4,4 +4,5 @@ dotenv.config(); export const PORT = Number(process.env.PORT); export const DB_URL = process.env.DB_CONNECTION; export const SALT = Number(process.env.SALT_ROUNDS); -export const JWT_PRIVATE = process.env.JWT_PRIVATE; \ No newline at end of file +export const JWT_PRIVATE = process.env.JWT_PRIVATE; +export const CLIENT_URL = process.env.CLIENT_URL; \ No newline at end of file diff --git a/backend/src/controllers/vote.controller.js b/backend/src/controllers/vote.controller.js new file mode 100644 index 0000000..c40debc --- /dev/null +++ b/backend/src/controllers/vote.controller.js @@ -0,0 +1,25 @@ +import { voteMessageTestService } from "../services/vote.service.js"; + +export async function voteTestController(req, res) { + try{ + const message = voteMessageTestService(); + res.json({ + success: true, + message: message, + }); + } + catch(err) { + console.log(err); + if (err.statusCode) { + res.status(err.statusCode).json({ + success: false, + message: err.message, + }); + } else { + 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 60f78e8..4320ea2 100644 --- a/backend/src/index.js +++ b/backend/src/index.js @@ -2,14 +2,15 @@ import express from 'express' import cors from 'cors' import { createServer } from 'http' import { Server } from 'socket.io' -import { PORT } from './config/veriables.js'; +import { CLIENT_URL, PORT } from './config/veriables.js'; import { connectDB } from './config/dbConfig.js'; 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'; - +import { handlePollSocket } from './socket/poll.socket.js'; +import voteRouter from './routes/v1/vote.route.js'; const app = express(); const httpServer = createServer(app); @@ -18,13 +19,17 @@ app.use('/docs', swaggerUi.serve, swaggerUi.setup(swaggerDocs)); const io = new Server(httpServer, { cors: { - origin: "*" + origin: CLIENT_URL, + methods: ["GET", "POST", "PUT", "DELETE"] } }) +handlePollSocket(io); + + app.use(cookieParser()); app.use(cors({ - origin: "http://localhost:5173", + origin: CLIENT_URL, credentials: true })) app.use(express.json()) @@ -34,6 +39,7 @@ app.get("/ping", (_req, res) => { app.use("/api/v1/poll", pollRouter); app.use("/api/v1/user", userRouter); +app.use("/api/v1/vote", voteRouter); await connectDB(); httpServer.listen(PORT, () => { diff --git a/backend/src/repositories/poll.repo.js b/backend/src/repositories/poll.repo.js index 7ba6892..e6bed98 100644 --- a/backend/src/repositories/poll.repo.js +++ b/backend/src/repositories/poll.repo.js @@ -65,7 +65,7 @@ export async function updatePollVoteCount(pollId, optionId) { export async function findPolls(page, limit) { try{ const skip = (page - 1) * limit; - const polls = await PollModel.find().skip(skip).limit(limit).populate("creatorId"); + const polls = await PollModel.find().sort({createdAt : -1}).skip(skip).limit(limit).populate("creatorId"); return polls; } catch(err){ diff --git a/backend/src/routes/v1/vote.route.js b/backend/src/routes/v1/vote.route.js new file mode 100644 index 0000000..5403480 --- /dev/null +++ b/backend/src/routes/v1/vote.route.js @@ -0,0 +1,19 @@ +import express from "express"; +import { verifyToken } from "../../middlwares/verifyToken.js"; +import { voteTestController } from "../../controllers/vote.controller.js"; +const voteRouter = express.Router(); + +/** + * @swagger + * /vote/test: + * get: + * summary: Test route for vote + * tags: [Vote] + * responses: + * 200: + * description: Success + */ +voteRouter.get("/test", voteTestController) +// voteRouter.get("/voted/:pollId", verifyToken, getVotedDataController); + +export default voteRouter; \ No newline at end of file diff --git a/backend/src/services/vote.service.js b/backend/src/services/vote.service.js new file mode 100644 index 0000000..566d8aa --- /dev/null +++ b/backend/src/services/vote.service.js @@ -0,0 +1,8 @@ +export function voteMessageTestService(){ + try{ + return "Vote route is working✔️"; + } + catch(err){ + throw err; + } +} \ No newline at end of file diff --git a/backend/src/socket/poll.socket.js b/backend/src/socket/poll.socket.js new file mode 100644 index 0000000..3c4e263 --- /dev/null +++ b/backend/src/socket/poll.socket.js @@ -0,0 +1,34 @@ +import { getPollDataService } from "../services/poll.service.js"; + +export const handlePollSocket = (io) => { + io.on("connection", (socket) => { + console.log(`User connected: ${socket.id}`); + + socket.on("joinPoll", (pollId) => { + socket.join(pollId); + console.log(`User ${socket.id} joined poll room: ${pollId}`); + }); + + socket.on("disconnect", () => { + console.log(`User disconnected: ${socket.id}`); + }); + + socket.on("vote", async (data) => { + if (data.success) { + console.log("Vote received:", data); + + try { + const pollData = await getPollDataService(data.pollId); + + io.to(data.pollId).emit("pollDataUpdated", { data: pollData }); + } catch (error) { + console.error("Error fetching poll data:", error); + socket.emit("error", { message: "Failed to fetch poll data" }); + } + } else { + console.log("Vote failed:", data); + socket.emit("error", { message: "Vote was unsuccessful" }); + } + }); + }); +}; diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 81f6983..34ab2ce 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -17,6 +17,7 @@ "react-query": "^3.39.3", "react-router-dom": "^6.27.0", "react-toastify": "^10.0.6", + "socket.io-client": "^4.8.1", "zustand": "^5.0.1" }, "devDependencies": { @@ -1338,6 +1339,12 @@ "win32" ] }, + "node_modules/@socket.io/component-emitter": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", + "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==", + "license": "MIT" + }, "node_modules/@types/babel__core": { "version": "7.20.5", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", @@ -2183,7 +2190,6 @@ "version": "4.3.7", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", - "dev": true, "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -2303,6 +2309,28 @@ "dev": true, "license": "MIT" }, + "node_modules/engine.io-client": { + "version": "6.6.2", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.6.2.tgz", + "integrity": "sha512-TAr+NKeoVTjEVW8P3iHguO1LO6RlUz9O5Y8o7EY0fU+gY1NYqas7NN3slpFtbXEsLMHk0h90fJMfKjRkQ0qUIw==", + "license": "MIT", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.17.1", + "xmlhttprequest-ssl": "~2.1.1" + } + }, + "node_modules/engine.io-parser": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", + "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/es-abstract": { "version": "1.23.3", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz", @@ -4024,7 +4052,6 @@ "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, "license": "MIT" }, "node_modules/mz": { @@ -5141,6 +5168,34 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/socket.io-client": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.8.1.tgz", + "integrity": "sha512-hJVXfu3E28NmzGk8o1sHhN3om52tRvwYeidbj7xKy2eIIse5IoKX3USlS6Tqt3BHAtflLIkCQBkzVrEEfWUyYQ==", + "license": "MIT", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.2", + "engine.io-client": "~6.6.1", + "socket.io-parser": "~4.2.4" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-parser": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", + "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", + "license": "MIT", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", @@ -5947,6 +6002,35 @@ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "license": "ISC" }, + "node_modules/ws": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xmlhttprequest-ssl": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.1.2.tgz", + "integrity": "sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", diff --git a/frontend/package.json b/frontend/package.json index f1e3c35..47ea03d 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -19,6 +19,7 @@ "react-query": "^3.39.3", "react-router-dom": "^6.27.0", "react-toastify": "^10.0.6", + "socket.io-client": "^4.8.1", "zustand": "^5.0.1" }, "devDependencies": { diff --git a/frontend/src/components/Errors/ErrorFallback.jsx b/frontend/src/components/Errors/ErrorFallback.jsx index 94558d5..612ba48 100644 --- a/frontend/src/components/Errors/ErrorFallback.jsx +++ b/frontend/src/components/Errors/ErrorFallback.jsx @@ -1,4 +1,5 @@ import React from "react"; +import { Link } from "react-router-dom"; const ErrorFallback = ({ onRetry }) => { return ( @@ -14,6 +15,9 @@ const ErrorFallback = ({ onRetry }) => { > Retry + + Go Home + ); diff --git a/frontend/src/pages/Bookmark.jsx b/frontend/src/pages/Bookmark.jsx index 4de55ac..6a6af38 100644 --- a/frontend/src/pages/Bookmark.jsx +++ b/frontend/src/pages/Bookmark.jsx @@ -5,6 +5,7 @@ import { useQuery, useQueryClient } from "react-query"; import ErrorFallback from "../components/Errors/ErrorFallback"; import useBookmark from "../hooks/useBookmark"; import { useNavigate } from "react-router-dom"; +import { formatDataByDate } from "../utils/util"; function Bookmark() { const { handleBookmark } = useBookmark(); @@ -34,6 +35,7 @@ function Bookmark() { }); await handleBookmark(bookmarkId); }; + console.log(data); return (