From 9a2158b2427ebad90bc76d6d843256428c50ae7d Mon Sep 17 00:00:00 2001 From: Lincoln Lim Date: Sat, 29 Jun 2024 04:50:42 +0800 Subject: [PATCH 01/18] Updated Association code, created getImageForReview endpoint, remove sender and receiver attribute, added more field for review updating --- index.js | 22 ---------------------- models/Guest.js | 1 + models/Review.js | 7 +++++-- routes/cdn/contentDelivery.js | 34 +++++++++++++++++++++++++++++++++- routes/cdn/coreData.js | 5 +++++ routes/reviews/reviews.js | 15 +++++++-------- 6 files changed, 51 insertions(+), 33 deletions(-) diff --git a/index.js b/index.js index 84bfd95..ed66c21 100644 --- a/index.js +++ b/index.js @@ -118,28 +118,6 @@ async function onDBSynchronise() { console.log(`Created new dummy host. Host ID: ${newHost.userID}`) } - const guests = await Guest.findAll() - if (guests.length > 0) { - Universal.data["DUMMY_GUEST_ID"] = guests[0].userID - console.log(`Found existing dummy guest. Guest ID: ${Universal.data["DUMMY_GUEST_ID"]}`) - } else { - const newGuest = await Guest.create({ - "userID": "47f4497b-1331-4b8a-97a4-095a79a1fd48", - "username": "Susie Jones", - "email": "susie_jones@gmail.com", - "password": "SusieJones123", - "contactNum": "82228111", - "address": "Block 321, Hougang Avenue 10, #10-567", - "emailVerified": "false", - "favCuisine": "", - "mealsMatched": "0", - "resetKey": null, - "resetKeyExpiration": null - }) - Universal.data["DUMMY_GUEST_ID"] = newGuest.userID - console.log(`Created new dummy guest. Guest ID: ${newGuest.userID}`) - } - const guests = await Guest.findAll() if (guests.length > 0) { Universal.data["DUMMY_GUEST_USERID"] = guests[0].userID diff --git a/models/Guest.js b/models/Guest.js index c6f7c78..12d5e79 100644 --- a/models/Guest.js +++ b/models/Guest.js @@ -63,6 +63,7 @@ module.exports = (sequelize, DataTypes) => { }) Guest.hasMany(models.Review, { foreignKey: "guestID", + as: "reviews", onDelete: "cascade" }) } diff --git a/models/Review.js b/models/Review.js index 65075f5..ce18071 100644 --- a/models/Review.js +++ b/models/Review.js @@ -40,8 +40,11 @@ module.exports = (sequelize, DataTypes) => { // Associations Review.associate = (models) => { - Review.belongsTo(models.Host) - Review.belongsTo(models.Guest) + Review.belongsTo(models.Host), + Review.belongsTo(models.Guest, { + foreignKey: 'guestID', + as: 'guest' + }); } return Review; diff --git a/routes/cdn/contentDelivery.js b/routes/cdn/contentDelivery.js index 7d1e2ce..48c946c 100644 --- a/routes/cdn/contentDelivery.js +++ b/routes/cdn/contentDelivery.js @@ -3,7 +3,7 @@ const router = express.Router(); const path = require("path"); const FileManager = require("../../services/FileManager"); const Logger = require("../../services/Logger"); -const { FoodListing } = require("../../models"); +const { FoodListing,Review } = require("../../models"); router.get("/getImageForListing", async (req, res) => { const { listingID, imageName } = req.query; @@ -36,4 +36,36 @@ router.get("/getImageForListing", async (req, res) => { return; }); +router.get("/getImageForReview", async (req, res) => { + const { reviewID, imageName } = req.query; + if (!reviewID || !imageName) { + res.status(400).send("ERROR: Invalid request parameters."); + return; + } + + // Find the review + const findReview = await Review.findByPk(reviewID); + if (!findReview) { + res.status(404).send("ERROR: Review not found."); + return; + } + + const findImageName = await FileManager.prepFile(imageName); + if (!findImageName.startsWith("SUCCESS")) { + res.status(404).send("ERROR: Image not found."); + return; + } + + const reviewImages = findReview.images.split("|"); + + if (reviewImages.includes(imageName) !== true) { + res.status(404).send("ERROR: Requested image does not belong to its corresponding review."); + return; + } + + res.status(200).sendFile(findImageName.substring("SUCCESS: File path: ".length)) + // Logger.log(`CDN GETREVIEWSIMAGE: Image(s) for reviews sent successfully.`) + return; +}); + module.exports = router; \ No newline at end of file diff --git a/routes/cdn/coreData.js b/routes/cdn/coreData.js index db711e6..0123569 100644 --- a/routes/cdn/coreData.js +++ b/routes/cdn/coreData.js @@ -174,6 +174,11 @@ router.get("/getReviews", async (req, res) => { // GET full reviews list const reviews = await Review.findAll({ where, order, + include: [{ + model: Guest, + as: 'guest', + attributes: ['username'] + }] }) if (req.query.order === "images") { diff --git a/routes/reviews/reviews.js b/routes/reviews/reviews.js index 297fbde..5168d5f 100644 --- a/routes/reviews/reviews.js +++ b/routes/reviews/reviews.js @@ -5,21 +5,21 @@ const path = require('path'); const { v4: uuidv4 } = require('uuid'); const FileManager = require('../../services/FileManager'); const { Universal } = require("../../services") -const { storeFiles } = require("../../middleware/storeFiles"); +const { storeImages } = require("../../middleware/storeImages"); const { Review, Host, Guest } = require('../../models'); const Logger = require('../../services/Logger'); router.route("/") .post(async (req, res) => { - storeFiles(req, res, async (err) => { + storeImages(req, res, async (err) => { const hostID = Universal.data["DUMMY_HOST_ID"] const host = await Host.findByPk(hostID) const guestID = Universal.data["DUMMY_GUEST_ID"] const guest = await Guest.findByPk(guestID) - const { sender, receiver, foodRating, hygieneRating, comments, dateCreated } = req.body; + const { foodRating, hygieneRating, comments, dateCreated } = req.body; - if (!sender || !receiver || !foodRating || !hygieneRating || !dateCreated) { + if (!foodRating || !hygieneRating || !dateCreated) { return res.status(400).send("ERROR: Missing required fields"); } try { @@ -29,8 +29,9 @@ router.route("/") const saveResult = await FileManager.saveFile(file.filename); if (saveResult !== true) { throw new Error(saveResult); + } else { + fileUrls.push(`${file.filename}`); } - fileUrls.push(`${file.filename}`); } const reviewID = Universal.generateUniqueID(); @@ -38,8 +39,6 @@ router.route("/") const review = { reviewID: reviewID, - sender, - receiver, foodRating: foodRating, hygieneRating: hygieneRating, comments: comments, @@ -107,7 +106,7 @@ router.route("/reviews/") return res.status(400).send("ERROR: Missing review ID"); } - const updateFields = ["foodRating", "hygieneRating", "comments", "images"]; + const updateFields = ["foodRating", "hygieneRating", "comments", "images", "likeCount", "dateCreated","guestID","hostID"]; const updateDict = {}; updateFields.forEach(field => { if (req.query[field]) { From 89ea6e47fb4711b6e3dc8ec9e1b92814a816ecf4 Mon Sep 17 00:00:00 2001 From: Lincoln Lim Date: Sat, 29 Jun 2024 05:02:27 +0800 Subject: [PATCH 02/18] Resolved ERR_HTTP_HEADERS_SENT error. --- routes/reviews/reviews.js | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/routes/reviews/reviews.js b/routes/reviews/reviews.js index 5168d5f..6e13d08 100644 --- a/routes/reviews/reviews.js +++ b/routes/reviews/reviews.js @@ -13,18 +13,31 @@ const Logger = require('../../services/Logger'); router.route("/") .post(async (req, res) => { storeImages(req, res, async (err) => { + + if (err instanceof multer.MulterError) { + Logger.log(`CDN REVIEWS POST ERROR: Image upload error; error: ${err}.`); + return res.status(400).send("ERROR: Image upload error"); + } else if (err) { + Logger.log(`CDN REVIEWS POST ERROR: Internal server error; error: ${err}.`); + return res.status(500).send("ERROR: Internal server error"); + } + const hostID = Universal.data["DUMMY_HOST_ID"] - const host = await Host.findByPk(hostID) const guestID = Universal.data["DUMMY_GUEST_ID"] - const guest = await Guest.findByPk(guestID) const { foodRating, hygieneRating, comments, dateCreated } = req.body; if (!foodRating || !hygieneRating || !dateCreated) { return res.status(400).send("ERROR: Missing required fields"); } try { - const fileUrls = []; + // const host = await Host.findByPk(hostID) + // const guest = await Guest.findByPk(guestID) + // if (!host || !guest) { + // return res.status(404).send("ERROR: Host or guest not found"); + // } + // Not using the above code for now as we are hardcoding the hostID and guestID + const fileUrls = []; for (const file of req.files) { const saveResult = await FileManager.saveFile(file.filename); if (saveResult !== true) { @@ -53,14 +66,7 @@ router.route("/") res.send("SUCCESS: Review submitted successfully"); } catch { Logger.log(`CDN REVIEWS POST ERROR: Failed to submit review; error: ${err}.`); - res.status(500).send("ERROR: Failed to submit review"); - } - if (err instanceof multer.MulterError) { - Logger.log(`CDN REVIEWS POST ERROR: Image upload error; error: ${err}.`); - res.status(400).send("ERROR: Image upload error"); - } else { - Logger.log(`CDN REVIEWS POST ERROR: Internal server error; error: ${err}.`); - res.status(500).send("ERROR: Internal server error"); + return res.status(500).send("ERROR: Failed to submit review"); } }); }); @@ -104,9 +110,9 @@ router.route("/reviews/") .put(async (req, res) => { if (!req.query.id) { return res.status(400).send("ERROR: Missing review ID"); - + } - const updateFields = ["foodRating", "hygieneRating", "comments", "images", "likeCount", "dateCreated","guestID","hostID"]; + const updateFields = ["foodRating", "hygieneRating", "comments", "images", "likeCount", "dateCreated", "guestID", "hostID"]; const updateDict = {}; updateFields.forEach(field => { if (req.query[field]) { From f4462e94b2d9358dc645013be6a025e549909504 Mon Sep 17 00:00:00 2001 From: Prakhar Trivedi Date: Sat, 29 Jun 2024 12:13:14 +0800 Subject: [PATCH 03/18] added type hint for bucket in FireStorage --- services/FireStorage.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/services/FireStorage.js b/services/FireStorage.js index d17b1d6..fa714e2 100644 --- a/services/FireStorage.js +++ b/services/FireStorage.js @@ -3,6 +3,9 @@ var serviceAccount; require('dotenv').config() class FireStorage { + /** + * @type {import('@google-cloud/storage').Bucket} + */ static #bucket = null; static #initialized = false; From 27ed1dde698eeae0d65bb5eda075ba0a415d95d7 Mon Sep 17 00:00:00 2001 From: Lincoln Lim Date: Sat, 29 Jun 2024 14:21:58 +0800 Subject: [PATCH 04/18] Update guestID fetching in backend --- routes/reviews/reviews.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routes/reviews/reviews.js b/routes/reviews/reviews.js index 6e13d08..5add15b 100644 --- a/routes/reviews/reviews.js +++ b/routes/reviews/reviews.js @@ -23,7 +23,7 @@ router.route("/") } const hostID = Universal.data["DUMMY_HOST_ID"] - const guestID = Universal.data["DUMMY_GUEST_ID"] + const guestID = Universal.data["DUMMY_GUEST_USERID"] const { foodRating, hygieneRating, comments, dateCreated } = req.body; if (!foodRating || !hygieneRating || !dateCreated) { From 3b07ead01af77da8da5b62bf7426e35c66bcb026 Mon Sep 17 00:00:00 2001 From: Lincoln Lim Date: Sat, 29 Jun 2024 20:24:27 +0800 Subject: [PATCH 05/18] Changed some UERROR to ERROR, Added logger.log to catch errors --- routes/cdn/contentDelivery.js | 4 +++- routes/cdn/coreData.js | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/routes/cdn/contentDelivery.js b/routes/cdn/contentDelivery.js index 48c946c..0d473dc 100644 --- a/routes/cdn/contentDelivery.js +++ b/routes/cdn/contentDelivery.js @@ -46,12 +46,14 @@ router.get("/getImageForReview", async (req, res) => { // Find the review const findReview = await Review.findByPk(reviewID); if (!findReview) { + Logger.log(`CDN CONTENTDELIVERY GETIMAGEFORREVIEW ERROR: Review not found.`) res.status(404).send("ERROR: Review not found."); return; } const findImageName = await FileManager.prepFile(imageName); if (!findImageName.startsWith("SUCCESS")) { + Logger.log(`CDN CONTENTDELIVERY GETIMAGEFORREVIEW ERROR: Image not found.`) res.status(404).send("ERROR: Image not found."); return; } @@ -59,12 +61,12 @@ router.get("/getImageForReview", async (req, res) => { const reviewImages = findReview.images.split("|"); if (reviewImages.includes(imageName) !== true) { + Logger.log(`CDN CONTENTDELIVERY GETIMAGEFORREVIEW ERROR: Requested image does not belong to its corresponding review.`) res.status(404).send("ERROR: Requested image does not belong to its corresponding review."); return; } res.status(200).sendFile(findImageName.substring("SUCCESS: File path: ".length)) - // Logger.log(`CDN GETREVIEWSIMAGE: Image(s) for reviews sent successfully.`) return; }); diff --git a/routes/cdn/coreData.js b/routes/cdn/coreData.js index 0123569..d9b01b1 100644 --- a/routes/cdn/coreData.js +++ b/routes/cdn/coreData.js @@ -145,7 +145,7 @@ router.get("/getReviews", async (req, res) => { // GET full reviews list const order = []; if (!req.query.hostID) { - return res.status(400).send("UERROR: Missing host ID or order."); + return res.status(400).send("ERROR: Missing host ID or order."); } else { where.hostID = req.query.hostID; } @@ -206,7 +206,7 @@ router.get("/getReviews", async (req, res) => { // GET full reviews list router.get("/reviews", async (req, res) => { // GET review from review id if (!req.query.id) { - return res.status(400).send("UERROR: Missing review ID"); + return res.status(400).send("ERROR: Missing review ID"); } else { const review = await Review.findByPk(req.query.id); if (review) { From 7ab3186545faff1c9228b6066723351ff0f05dbf Mon Sep 17 00:00:00 2001 From: Nic <3xpect1916@gmail.com> Date: Sat, 29 Jun 2024 21:55:47 +0800 Subject: [PATCH 06/18] done rendering from database and editing of message --- models/ChatMessage.js | 8 +- routes/chat/WebSocketServer.js | 204 ++++++++++++++++++--------------- 2 files changed, 120 insertions(+), 92 deletions(-) diff --git a/models/ChatMessage.js b/models/ChatMessage.js index 5e26aca..b947d31 100644 --- a/models/ChatMessage.js +++ b/models/ChatMessage.js @@ -1,3 +1,5 @@ +const { Timestamp } = require('firebase-admin/firestore'); + /** * * @param {import('sequelize').Sequelize} sequelize @@ -15,13 +17,17 @@ module.exports = (sequelize, DataTypes) => { type: DataTypes.STRING, allowNull: false }, - from: { + sender: { type: DataTypes.STRING, allowNull: false }, datetime: { type: DataTypes.STRING, allowNull: false + }, + timestamp: { + type: DataTypes.STRING, + allowNull: false } }, { tableName: 'chatMessages' }) diff --git a/routes/chat/WebSocketServer.js b/routes/chat/WebSocketServer.js index 439d824..ae0a04f 100644 --- a/routes/chat/WebSocketServer.js +++ b/routes/chat/WebSocketServer.js @@ -3,109 +3,131 @@ const http = require("http"); const WebSocket = require("ws"); const { ChatHistory, ChatMessage } = require("../../models"); const Universal = require("../../services/Universal"); -const Cache = require("../../services/Cache"); - +const mysql = require('mysql'); function startWebSocketServer(app) { - if (!process.env.WS_PORT) { console.log("WARNING: WS_PORT environment variable not set. Defaulting to 8080.") } - const PORT = process.env.WS_PORT || 8080; - - const server = http.createServer(app); - - const wss = new WebSocket.Server({ server }); - - const clients = []; - - wss.on("connection", (ws) => { - console.log("WS connection arrived"); - clients.push(ws); - - if (!Cache.cache["chat"]) { - Cache.cache["chat"] = {}; + const PORT = 8080; + const server = http.createServer(app); + const wss = new WebSocket.Server({ server }); + const clients = []; + + wss.on("connection", async (ws) => { + console.log("WS connection arrived"); + clients.push(ws); + + // Assuming you have some way to identify users, like user IDs + const user1ID = "Jamie"; // Replace with actual user IDs + const user2ID = "James"; // Replace with actual user IDs + + try { + // Check if a ChatHistory exists between user1 and user2 + let chatHistory = await ChatHistory.findOne({ + where: { + user1ID, + user2ID, + }, + }); + + if (!chatHistory) { + // Create a new ChatHistory if it doesn't exist + chatHistory = await ChatHistory.create({ + chatID: Universal.generateUniqueID(), + user1ID, + user2ID, + datetime: new Date().toISOString(), // Replace with current datetime logic + }); + } + + // Fetch and send previous chat messages + const previousMessages = await ChatMessage.findAll({ + where: { + chatID: chatHistory.chatID, + }, + order: [['datetime', 'ASC']], // Order messages by datetime ascending + }); + + ws.send(JSON.stringify({ + type: 'chat_history', + messages: previousMessages, + })); + + // Now you can handle further WebSocket message events + ws.on("message", async (message) => { + const parsedMessage = JSON.parse(message); + console.log("Received message:", parsedMessage); + if (parsedMessage.action === "edit") { + handleEditMessage(parsedMessage); + } else if (parsedMessage.action === "delete") { + handleDeleteMessage(parsedMessage); } - - const cachedMessages = Cache.cache["chat"]; - for (const messageId in cachedMessages) { - const message = { - id: messageId, - ...cachedMessages[messageId], - }; - ws.send(JSON.stringify(message)); + else{ + try { + // Create ChatMessage in the database + const createdMessage = await ChatMessage.create({ + messageID: parsedMessage.messageid, + message: parsedMessage.message, + sender: parsedMessage.sender, + datetime: parsedMessage.datetime, + timestamp: parsedMessage.timestamp, + chatID: chatHistory.chatID, // Assign the chatID from ChatHistory + }); + + // Broadcast the message to all clients + broadcastMessage(JSON.stringify(createdMessage), ws); + } catch (error) { + console.error("Error creating message:", error); } + } + }); - ws.on("message", (message) => { - const parsedMessage = JSON.parse(message); - - Cache.cache["chat"][parsedMessage.id] = { - sender: parsedMessage.sender, - message: parsedMessage.message, - timestamp: parsedMessage.timestamp, - }; - Cache.save(); - - if (parsedMessage.action === "edit") { - handleEditMessage(parsedMessage); - } else if (parsedMessage.action === "delete") { - handleDeleteMessage(parsedMessage); - } else { - broadcastMessage(message, ws); - } - }); + } catch (error) { + console.error("Error checking ChatHistory:", error); + } - ws.on("close", () => { - const index = clients.indexOf(ws); - if (index > -1) { - clients.splice(index, 1); - } - }); + ws.on("close", () => { + const index = clients.indexOf(ws); + if (index > -1) { + clients.splice(index, 1); + } }); + }); - wss.on("error", (error) => { - console.error("WebSocket server error:", error); - }); + wss.on("error", (error) => { + console.error("WebSocket server error:", error); + }); - function broadcastMessage(message, sender) { - clients.forEach((client) => { - if (client !== sender && client.readyState === WebSocket.OPEN) { - client.send(message); - } - }); - } - - function handleDeleteMessage(deleteMessage) { - const messageId = deleteMessage.id; - delete Cache.cache["chat"][messageId]; - Cache.save(); - - const jsonMessage = JSON.stringify({ - id: messageId, - action: "delete", - action: "reload", - }); - broadcastMessage(jsonMessage); + function handleEditMessage(editedMessage) { + const messageId = editedMessage.id; + const findMessage = ChatMessage.findByPk(messageId); + if (!findMessage) { + console.log("Message not found"); } + const editMessage = ChatMessage.update({ + message: editedMessage.message, + timestamp: `${new Date().getHours()}:${new Date() + .getMinutes() + .toString() + .padStart(2, "0")}` + }, { + where: { + messageID: messageId, + }, + }); + } + - function handleEditMessage(editedMessage) { - const messageId = editedMessage.id; - if (Cache.cache["chat"][messageId]) { - Cache.cache["chat"][messageId].message = editedMessage.message; - Cache.cache["chat"][messageId].edited = true; - Cache.save(); - - const jsonMessage = JSON.stringify({ - ...Cache.cache["chat"][messageId], - id: messageId, - action: "edit", - action: "reload", - }); - broadcastMessage(jsonMessage); - } else { - console.error(`Message with ID ${messageId} not found.`); - } - } - server.listen(PORT, () => { - console.log(`WebSocket Server running on port ${PORT}`); + function broadcastMessage(message, sender) { + clients.forEach((client) => { + if (client !== sender && client.readyState === WebSocket.OPEN) { + client.send(message); + } }); + } + + + server.listen(PORT, () => { + console.log(`WebSocket Server running on port ${PORT}`); + }); } module.exports = startWebSocketServer; From f07a027d688b30fb495d595fa25370597b71ff78 Mon Sep 17 00:00:00 2001 From: Nic <3xpect1916@gmail.com> Date: Sat, 29 Jun 2024 22:27:17 +0800 Subject: [PATCH 07/18] done delete message --- routes/chat/WebSocketServer.js | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/routes/chat/WebSocketServer.js b/routes/chat/WebSocketServer.js index ae0a04f..4fdd673 100644 --- a/routes/chat/WebSocketServer.js +++ b/routes/chat/WebSocketServer.js @@ -112,6 +112,27 @@ function startWebSocketServer(app) { messageID: messageId, }, }); + const jsonMessage = { + action: "reload", + } + broadcastMessage(JSON.stringify(jsonMessage)); + } + + function handleDeleteMessage(deletedMessage) { + const messageId = deletedMessage.id; + const findMessage = ChatMessage.findByPk(messageId); + if (!findMessage) { + console.log("Message not found"); + } + const deleteMessage = ChatMessage.destroy({ + where: { + messageID: messageId, + }, + }); + const jsonMessage = { + action: "reload", + } + broadcastMessage(JSON.stringify(jsonMessage)); } From f680922c058c0b880671d86f088ab9093da8439f Mon Sep 17 00:00:00 2001 From: Prakhar Trivedi Date: Wed, 3 Jul 2024 16:58:31 +0800 Subject: [PATCH 08/18] strengthened updateListing endpoint added Extensions service with static method functionality --- routes/orders/listingDetails.js | 30 +++++++++++++++++++++++++++++- services/Extensions.js | 14 ++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 services/Extensions.js diff --git a/routes/orders/listingDetails.js b/routes/orders/listingDetails.js index b88262a..2c9e4b9 100644 --- a/routes/orders/listingDetails.js +++ b/routes/orders/listingDetails.js @@ -5,6 +5,8 @@ const Universal = require('../../services/Universal'); const FileManager = require('../../services/FileManager'); const { storeFile } = require('../../middleware/storeFile'); const Logger = require('../../services/Logger'); +const yup = require('yup'); +const Extensions = require('../../services/Extensions'); router.post("/uploadListingImage", async (req, res) => { storeFile(req, res, async (err) => { @@ -107,8 +109,34 @@ router.post("/updateListing", async (req, res) => { return } + const validationSchema = yup.object({ + title: yup.string(), + shortDescription: yup.string().max(50), + longDescription: yup.string().max(350), + portionPrice: yup.number(), + approxAddress: yup.string(), + totalSlots: yup.number(), + datetime: yup.string(), + published: yup.boolean() + }) + + let newData = Extensions.filterDictionary(req.body, (key) => key != "listingID") + try { + newData = validationSchema.validateSync(newData, { abortEarly: false }) + console.log(newData) + if (Object.keys(newData).length == 0) { + res.status(200).send("SUCCESS: Nothing to update.") + return + } + } catch (err) { + // send back errors + res.status(400).send(`ERROR: Data validation errors occurred. Errors: ${err.errors.join(", ")}`) + return + } + try { - listing.update(req.body) + console.log(newData) + listing.update(newData) await listing.save() Logger.log(`ORDERS LISTINGDETAILS UPDATELISTING: Listing '${listingID}' updated.`) diff --git a/services/Extensions.js b/services/Extensions.js new file mode 100644 index 0000000..d09b254 --- /dev/null +++ b/services/Extensions.js @@ -0,0 +1,14 @@ +class Extensions { + /** + * Filter a dictionary with a predicate. + * Example Usage: `Extensions.filterDictionary(dictionary, (key, value) => key.startsWith('a'))` + * @param {object} dictionary + * @param {function(string, string)} predicate + * @returns object + */ + static filterDictionary = (dictionary, predicate) => { + return Object.fromEntries(Object.entries(dictionary).filter(([k, v]) => predicate(k, v))) + } +} + +module.exports = Extensions; \ No newline at end of file From ffc97092ac4e0359ef0e6045ab367fdbf67d42ec Mon Sep 17 00:00:00 2001 From: Prakhar Trivedi Date: Wed, 3 Jul 2024 18:31:57 +0800 Subject: [PATCH 09/18] fixed sequelize model intellisense issues hard imported models, other models not hard imported will still be auto imported but wont have intellisense functionality --- models/index.js | 76 ++++++++++----------------------- routes/orders/listingDetails.js | 2 - 2 files changed, 22 insertions(+), 56 deletions(-) diff --git a/models/index.js b/models/index.js index d5cb4b1..12967e5 100644 --- a/models/index.js +++ b/models/index.js @@ -10,12 +10,12 @@ const Universal = require('../services/Universal'); const basename = path.basename(__filename); const env = process.env.DB_CONFIG || 'development'; const config = require('../config/config.json')[env]; -const db = {}; if (!config) { throw new Error("Database configuration not found in config/config.json") } +// System Configuration Setup (e.g Logging) if (config.logging == true) { if (config.loggingOptions != undefined) { var queryLogsFile = "sqlQueries.txt" @@ -42,6 +42,7 @@ if (config.logging == true) { // If logging options not provided, sequelize will default to console.log } +// Sequelize Initialization /** * @type {Sequelize.Sequelize} */ @@ -60,6 +61,21 @@ if (process.env.DB_MODE == "mysql") { }) } +// Model Registration +const db = {}; + +// Hard-import models +db.Admin = require('./Admin')(sequelize, Sequelize.DataTypes); +db.ChatHistory = require('./ChatHistory')(sequelize, Sequelize.DataTypes); +db.ChatMessage = require('./ChatMessage')(sequelize, Sequelize.DataTypes); +db.FoodListing = require('./FoodListing')(sequelize, Sequelize.DataTypes); +db.Guest = require('./Guest')(sequelize, Sequelize.DataTypes); +db.Host = require('./Host')(sequelize, Sequelize.DataTypes); +db.Reservation = require('./Reservation')(sequelize, Sequelize.DataTypes); +db.Review = require('./Admin')(sequelize, Sequelize.DataTypes); +db.Warning = require('./Admin')(sequelize, Sequelize.DataTypes); + +// Auto-detect and import other models (intellisense will not work for these models) fs .readdirSync(__dirname) .filter(file => { @@ -71,11 +87,11 @@ fs ); }) .forEach(file => { - /** - * @type {Sequelize.Model} - */ const model = require(path.join(__dirname, file))(sequelize, Sequelize.DataTypes); - db[model.name] = model; + // Add model if model not hard-imported + if (db[model.name] !== undefined) { + db[model.name] = model; + } }); Object.keys(db).forEach(modelName => { @@ -84,52 +100,4 @@ Object.keys(db).forEach(modelName => { } }); -const { admin, chatHistory, chatMessage, foodListing, guest, host, reservation, review, warning, ...otherModels } = db - -/** - * @type {Sequelize.Model} - */ -const Admin = admin; - -/** - * @type {Sequelize.Model} - */ -const ChatHistory = chatHistory; - -/** - * @type {Sequelize.Model} - */ -const ChatMessage = chatMessage; - -/** - * @type {Sequelize.Model} - */ -const FoodListing = foodListing; - -/** - * @type {Sequelize.Model} - */ -const Guest = guest; - -/** - * @type {Sequelize.Model} - */ -const Host = host; - -/** - * @type {Sequelize.Model} - */ -const Reservation = reservation; - -/** - * @type {Sequelize.Model} - */ -const Review = review; - -/** - * @type {Sequelize.Model} - */ -const Warning = warning; - -// console.log({ Admin, FoodListing, Guest, Host, Reservation, Review, Warning, ...otherModels, sequelize, Sequelize }) -module.exports = { Admin, ChatHistory, ChatMessage, FoodListing, Guest, Host, Reservation, Review, Warning, ...otherModels, sequelize, Sequelize }; \ No newline at end of file +module.exports = { ...db, sequelize, Sequelize }; \ No newline at end of file diff --git a/routes/orders/listingDetails.js b/routes/orders/listingDetails.js index 2c9e4b9..8cefb7c 100644 --- a/routes/orders/listingDetails.js +++ b/routes/orders/listingDetails.js @@ -123,7 +123,6 @@ router.post("/updateListing", async (req, res) => { let newData = Extensions.filterDictionary(req.body, (key) => key != "listingID") try { newData = validationSchema.validateSync(newData, { abortEarly: false }) - console.log(newData) if (Object.keys(newData).length == 0) { res.status(200).send("SUCCESS: Nothing to update.") return @@ -135,7 +134,6 @@ router.post("/updateListing", async (req, res) => { } try { - console.log(newData) listing.update(newData) await listing.save() From 42a7e80f119b963c11a388dd282579f1e2652071 Mon Sep 17 00:00:00 2001 From: Prakhar Trivedi Date: Wed, 3 Jul 2024 20:14:06 +0800 Subject: [PATCH 10/18] added new automated router detect and register index script only activated if routerRegistration set to true in config --- ...iguringDatabase.md => ConfiguringSystem.md | 2 +- config/boilerplateConfig.json | 19 +----- index.js | 37 ++++++++---- routes/index.js | 58 +++++++++++++++++++ 4 files changed, 87 insertions(+), 29 deletions(-) rename ConfiguringDatabase.md => ConfiguringSystem.md (97%) create mode 100644 routes/index.js diff --git a/ConfiguringDatabase.md b/ConfiguringSystem.md similarity index 97% rename from ConfiguringDatabase.md rename to ConfiguringSystem.md index 5e497a0..479677f 100644 --- a/ConfiguringDatabase.md +++ b/ConfiguringSystem.md @@ -1,4 +1,4 @@ -# Configuring Database for MakanMatch Backend +# Configuring System Operation There's a few ways you can configure the database the backend system uses. diff --git a/config/boilerplateConfig.json b/config/boilerplateConfig.json index 9cac3c3..24fd281 100644 --- a/config/boilerplateConfig.json +++ b/config/boilerplateConfig.json @@ -11,21 +11,8 @@ "useFileLogging": false, "logPostBootOnly": false, "clearUponBoot": false - } - }, - "test": { - "username": "root", - "password": null, - "database": "database_test", - "host": "127.0.0.1", - "dialect": "mysql" - }, - "production": { - "username": "root", - "password": null, - "database": "database_production", - "host": "127.0.0.1", - "dialect": "mysql", - "routeLogging": false + }, + "routeLogging": false, + "routerRegistration": "manual" } } \ No newline at end of file diff --git a/index.js b/index.js index 14f36f9..7a806e3 100644 --- a/index.js +++ b/index.js @@ -54,17 +54,30 @@ app.get("/", (req, res) => { // Register routers app.use(checkHeaders) // Middleware to check Content-Type and API key headers -app.use("/misc", require("./routes/misc")); -app.use("/cdn", require("./routes/cdn/contentDelivery")); -app.use("/cdn", require("./routes/cdn/coreData")); -app.use("/reviews", require("./routes/reviews/reviews")); -app.use("/createAccount", require('./routes/identity/createAccount')); -app.use("/loginAccount", require('./routes/identity/loginAccount')); -app.use("/accountRecovery", require('./routes/identity/accountRecovery')); -app.use("/identity/emailVerification", require('./routes/identity/emailVerification')); -app.use("/identity/myAccount", require("./routes/identity/myAccount")); -app.use("/listings", require("./routes/listings/listings")); -app.use("/", require("./routes/orders/listingDetails")); +if (config["routerRegistration"] != "automated") { + console.log("MAIN: Route registration mode: MANUAL") + app.use("/misc", require("./routes/misc")); + app.use("/cdn", require("./routes/cdn/contentDelivery")); + app.use("/cdn", require("./routes/cdn/coreData")); + app.use("/reviews", require("./routes/reviews/reviews")); + app.use("/createAccount", require('./routes/identity/createAccount')); + app.use("/loginAccount", require('./routes/identity/LoginAccount')); + app.use("/accountRecovery", require('./routes/identity/AccountRecovery')); + app.use("/identity/emailVerification", require('./routes/identity/emailVerification')); + app.use("/identity/myAccount", require("./routes/identity/myAccount")); + app.use("/listings", require("./routes/listings/listings")); + app.use("/", require("./routes/orders/listingDetails")); +} else { + console.log("MAIN: Route registration mode: AUTOMATED") + require('./routes').forEach(({ router, at, name }) => { + try { + // console.log(`Registering ${name} at '${at}'`) + app.use(at, router) + } catch (err) { + Logger.logAndThrow(`MAIN: Failed to register router auto-loaded from ${name} at '${at}'. Error: ${err}`) + } + }) +} async function onDBSynchronise() { const guests = await Guest.findAll() @@ -90,7 +103,7 @@ async function onDBSynchronise() { Universal.data["DUMMY_GUEST_USERNAME"] = newGuest.username console.log(`Created dummy guest with User ID: ${newGuest.userID}`) } - + const joshuasHost = await Host.findByPk("272d3d17-fa63-49c4-b1ef-1a3b7fe63cf4") if (!joshuasHost) { const newHost = await Host.create({ diff --git a/routes/index.js b/routes/index.js new file mode 100644 index 0000000..63bad2f --- /dev/null +++ b/routes/index.js @@ -0,0 +1,58 @@ +const fs = require('fs'); +const path = require('path'); +const FileOps = require('../services/FileOps'); + +let ignoredFiles = FileOps.read(path.join(__dirname, '../.gitignore')).split('\n').map(item => path.basename(item)) +ignoredFiles += [ + 'index.js', + 'WebSocketServer.js', + 'Chat.js' +] + +var routes = [] + +const routeFiles = []; +function filesInDir(directory) { + fs.readdirSync(directory).forEach(file => { + const absolute = path.join(directory, file); + if (fs.statSync(absolute).isDirectory()) { + return filesInDir(absolute); + } else { + if (ignoredFiles.includes(file)) return; + return routeFiles.push(absolute); + } + }); +} +filesInDir(__dirname); + +routeFiles.forEach(routeFile => { + try { + const routerExport = require(routeFile); + + // See if a router is being exported + if (routerExport == undefined) { return; } + + var exportMode = null; + if (typeof routerExport == 'function') { + exportMode = "direct"; + } else if (typeof routerExport.router != 'undefined') { + exportMode = "indirect"; + } else { + console.log('No router found in ' + routeFile) + return; + } + + // Get the router + const router = exportMode === "direct" ? routerExport : routerExport.router; + + // Get router path + const routePath = exportMode == "direct" ? "/" : (routerExport.at === undefined ? "/" : routerExport.at); + + // Add to routes + routes.push({ router: router, at: routePath, name: path.basename(routeFile) }) + } catch (err) { + console.log(`ROUTES INDEX: Error auto-loading route from ${routeFile}. Will ignore and continue. Error: ${err}`) + } +}) + +module.exports = routes; \ No newline at end of file From 111104919fafcf998c534d86237c24df830b2f05 Mon Sep 17 00:00:00 2001 From: Lincoln Lim Date: Wed, 3 Jul 2024 22:34:33 +0800 Subject: [PATCH 11/18] Catching errors --- routes/reviews/reviews.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/routes/reviews/reviews.js b/routes/reviews/reviews.js index 5add15b..0b706eb 100644 --- a/routes/reviews/reviews.js +++ b/routes/reviews/reviews.js @@ -64,7 +64,7 @@ router.route("/") await Review.create(review); res.send("SUCCESS: Review submitted successfully"); - } catch { + } catch (err) { Logger.log(`CDN REVIEWS POST ERROR: Failed to submit review; error: ${err}.`); return res.status(500).send("ERROR: Failed to submit review"); } @@ -102,8 +102,8 @@ router.route("/reviews/") return res.status(404).send(`ERROR: Review not found`); } res.json(review); // Tested in postcode, working! - } catch { - Logger.log(`CDN REVIEWS REVIEWS ERROR: Failed to retrieve review with ID ${req.query.id}`); + } catch (err) { + Logger.log(`CDN REVIEWS REVIEWS ERROR: Failed to retrieve review with ID ${req.query.id} ${err}`); return res.status(500).send("ERROR: Failed to retrieve review"); } }) @@ -131,8 +131,8 @@ router.route("/reviews/") } else { res.send(`SUCCESS: Review with ID ${req.query.id} updated`); // Tested in postcode, working! } - } catch { - Logger.log(`CDN REVIEWS REVIEWS ERROR: Failed to update review with ID ${req.query.id}`); + } catch (err) { + Logger.log(`CDN REVIEWS REVIEWS ERROR: Failed to update review with ID ${req.query.id} ${err}`); return res.status(500).send("ERROR: Failed to update review"); } } @@ -150,8 +150,8 @@ router.route("/reviews/") } else { res.send(`SUCCESS: Review with ID ${req.query.id} deleted`); // Tested in postcode, working! } - } catch { - Logger.log(`CDN REVIEWS REVIEWS ERROR: Failed to delete review with ID ${req.query.id}`); + } catch (err){ + Logger.log(`CDN REVIEWS REVIEWS ERROR: Failed to delete review with ID ${req.query.id} ${err}`); return res.status(500).send("ERROR: Failed to delete review"); } }); From b3de7360767f07a1fa76bb29728fe6b5d0db0e81 Mon Sep 17 00:00:00 2001 From: Prakhar Trivedi Date: Wed, 3 Jul 2024 23:27:13 +0800 Subject: [PATCH 12/18] fixed some of jun hans bugs --- routes/cdn/coreData.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/routes/cdn/coreData.js b/routes/cdn/coreData.js index 9c450d1..1f301f0 100644 --- a/routes/cdn/coreData.js +++ b/routes/cdn/coreData.js @@ -84,6 +84,7 @@ router.get("/getListing", async (req, res) => { router.get("/accountInfo", async (req, res) => { // GET account information try { const targetUserID = req.query.userID; + if (!targetUserID) { res.status(400).send("ERROR: One or more required payloads not provided."); } let user, userType; user = await Guest.findOne({ where: { userID: targetUserID } }); @@ -103,7 +104,7 @@ router.get("/accountInfo", async (req, res) => { // GET account information } if (!user) { - return res.status(404).send("User does not exist."); + return res.status(404).send("ERROR: User does not exist."); } const accountInfo = { @@ -134,7 +135,8 @@ router.get("/accountInfo", async (req, res) => { // GET account information res.status(200).json(accountInfo); } catch (err) { - res.status(500).send("An error occured while fetching the account information.") + res.status(500).send("ERROR: An error occured while fetching the account information.") + console.log(err) } }) From b5e9b2c175cb45fd0266089919f3e84117e4a973 Mon Sep 17 00:00:00 2001 From: Prakhar Trivedi Date: Fri, 5 Jul 2024 10:08:22 +0800 Subject: [PATCH 13/18] made minor changes --- index.js | 1 + routes/cdn/coreData.js | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index 7a806e3..8840579 100644 --- a/index.js +++ b/index.js @@ -159,5 +159,6 @@ if (!SEQUELIZE_ACTIVE) { .catch(err => { console.log(err) console.log(`MAIN: Failed to setup sequelize. Terminating boot.`) + process.exit(1) }) } \ No newline at end of file diff --git a/routes/cdn/coreData.js b/routes/cdn/coreData.js index 2e5bff5..5f39866 100644 --- a/routes/cdn/coreData.js +++ b/routes/cdn/coreData.js @@ -10,7 +10,6 @@ const { validateToken } = require("../../middleware/auth"); router.get('/MyAccount', validateToken, (req, res) => { const userInfo = req.user; - console.log(userInfo) res.json(userInfo); }); From a7200150ed21caefe7871beb7543d7c056901b11 Mon Sep 17 00:00:00 2001 From: Prakhar Trivedi Date: Fri, 5 Jul 2024 10:34:16 +0800 Subject: [PATCH 14/18] changed all routers to use auto syntax routers will be detected and collated automatically by router/index --- index.js | 22 +++++++++++----------- routes/cdn/contentDelivery.js | 2 +- routes/cdn/coreData.js | 2 +- routes/identity/AccountRecovery.js | 2 +- routes/identity/CreateAccount.js | 2 +- routes/identity/LoginAccount.js | 2 +- routes/identity/emailVerification.js | 2 +- routes/identity/myAccount.js | 2 +- routes/listings/listings.js | 2 +- routes/misc.js | 2 +- routes/orders/listingDetails.js | 2 +- routes/reviews/reviews.js | 2 +- 12 files changed, 22 insertions(+), 22 deletions(-) diff --git a/index.js b/index.js index 8840579..5f6b8d5 100644 --- a/index.js +++ b/index.js @@ -56,17 +56,17 @@ app.get("/", (req, res) => { app.use(checkHeaders) // Middleware to check Content-Type and API key headers if (config["routerRegistration"] != "automated") { console.log("MAIN: Route registration mode: MANUAL") - app.use("/misc", require("./routes/misc")); - app.use("/cdn", require("./routes/cdn/contentDelivery")); - app.use("/cdn", require("./routes/cdn/coreData")); - app.use("/reviews", require("./routes/reviews/reviews")); - app.use("/createAccount", require('./routes/identity/createAccount')); - app.use("/loginAccount", require('./routes/identity/LoginAccount')); - app.use("/accountRecovery", require('./routes/identity/AccountRecovery')); - app.use("/identity/emailVerification", require('./routes/identity/emailVerification')); - app.use("/identity/myAccount", require("./routes/identity/myAccount")); - app.use("/listings", require("./routes/listings/listings")); - app.use("/", require("./routes/orders/listingDetails")); + app.use(require("./routes/misc").at || '/', require("./routes/misc").router); + app.use(require("./routes/cdn/contentDelivery").at || '/', require("./routes/cdn/contentDelivery").router); + app.use(require("./routes/cdn/coreData").at || '/', require("./routes/cdn/coreData").router); + app.use(require("./routes/reviews/reviews").at || '/', require("./routes/reviews/reviews").router); + app.use(require('./routes/identity/createAccount').at || '/', require('./routes/identity/createAccount').router); + app.use(require('./routes/identity/LoginAccount').at || '/', require('./routes/identity/LoginAccount').router); + app.use(require('./routes/identity/AccountRecovery').at || '/', require('./routes/identity/AccountRecovery').router); + app.use(require('./routes/identity/emailVerification').at || '/', require('./routes/identity/emailVerification').router); + app.use(require("./routes/identity/myAccount").at || '/', require("./routes/identity/myAccount").router); + app.use(require("./routes/listings/listings").at || '/', require("./routes/listings/listings").router); + app.use(require("./routes/orders/listingDetails").at || '/', require("./routes/orders/listingDetails").router); } else { console.log("MAIN: Route registration mode: AUTOMATED") require('./routes').forEach(({ router, at, name }) => { diff --git a/routes/cdn/contentDelivery.js b/routes/cdn/contentDelivery.js index 0d473dc..b609035 100644 --- a/routes/cdn/contentDelivery.js +++ b/routes/cdn/contentDelivery.js @@ -70,4 +70,4 @@ router.get("/getImageForReview", async (req, res) => { return; }); -module.exports = router; \ No newline at end of file +module.exports = { router, at: '/cdn' }; \ No newline at end of file diff --git a/routes/cdn/coreData.js b/routes/cdn/coreData.js index 5f39866..4986f35 100644 --- a/routes/cdn/coreData.js +++ b/routes/cdn/coreData.js @@ -217,4 +217,4 @@ router.get("/reviews", async (req, res) => { // GET review from review id } }) -module.exports = router; \ No newline at end of file +module.exports = { router, at: '/cdn' }; \ No newline at end of file diff --git a/routes/identity/AccountRecovery.js b/routes/identity/AccountRecovery.js index 928c289..a26bed3 100644 --- a/routes/identity/AccountRecovery.js +++ b/routes/identity/AccountRecovery.js @@ -81,4 +81,4 @@ router.post('/resetPassword', async (req, res) => { } }); -module.exports = router; +module.exports = { router, at: '/accountRecovery' }; diff --git a/routes/identity/CreateAccount.js b/routes/identity/CreateAccount.js index a51a604..c741cae 100644 --- a/routes/identity/CreateAccount.js +++ b/routes/identity/CreateAccount.js @@ -96,4 +96,4 @@ router.post("/", async (req, res) => { } }); -module.exports = router; \ No newline at end of file +module.exports = { router, at: '/createAccount' }; \ No newline at end of file diff --git a/routes/identity/LoginAccount.js b/routes/identity/LoginAccount.js index 378dd95..fd0db1e 100644 --- a/routes/identity/LoginAccount.js +++ b/routes/identity/LoginAccount.js @@ -80,4 +80,4 @@ router.post("/", async (req, res) => { } }); -module.exports = router; \ No newline at end of file +module.exports = { router, at: '/loginAccount' }; \ No newline at end of file diff --git a/routes/identity/emailVerification.js b/routes/identity/emailVerification.js index 9a64293..9e78636 100644 --- a/routes/identity/emailVerification.js +++ b/routes/identity/emailVerification.js @@ -90,4 +90,4 @@ router.get("/verifyEmail", async (req, res) => { } }); -module.exports = router; +module.exports = { router, at: '/identity/emailVerification' }; diff --git a/routes/identity/myAccount.js b/routes/identity/myAccount.js index 26d5c25..3134d8c 100644 --- a/routes/identity/myAccount.js +++ b/routes/identity/myAccount.js @@ -9,4 +9,4 @@ router.post('/logout', (req, res) => { -module.exports = router; \ No newline at end of file +module.exports = { router, at: '/identity/myAccount' }; \ No newline at end of file diff --git a/routes/listings/listings.js b/routes/listings/listings.js index b3fcc00..26d9344 100644 --- a/routes/listings/listings.js +++ b/routes/listings/listings.js @@ -144,4 +144,4 @@ router.delete("/deleteListing", async (req, res) => { } }); -module.exports = router; +module.exports = { router, at: '/listings' }; diff --git a/routes/misc.js b/routes/misc.js index e138e58..4d043fa 100644 --- a/routes/misc.js +++ b/routes/misc.js @@ -5,4 +5,4 @@ router.get("/health", (req, res) => { res.send("Healthy!") }) -module.exports = router; \ No newline at end of file +module.exports = { router, at: '/misc' }; \ No newline at end of file diff --git a/routes/orders/listingDetails.js b/routes/orders/listingDetails.js index 8cefb7c..db1dd71 100644 --- a/routes/orders/listingDetails.js +++ b/routes/orders/listingDetails.js @@ -146,4 +146,4 @@ router.post("/updateListing", async (req, res) => { } }) -module.exports = router; \ No newline at end of file +module.exports = { router }; \ No newline at end of file diff --git a/routes/reviews/reviews.js b/routes/reviews/reviews.js index 5333945..6bfafe1 100644 --- a/routes/reviews/reviews.js +++ b/routes/reviews/reviews.js @@ -156,4 +156,4 @@ router.route("/reviews/") } }); -module.exports = router; +module.exports = { router, at: '/reviews' }; From cdeda041d9afe42df501ffa45d616e8d1358d381 Mon Sep 17 00:00:00 2001 From: Prakhar Trivedi Date: Fri, 5 Jul 2024 10:34:33 +0800 Subject: [PATCH 15/18] removed arbitrary console log --- index.js | 1 - 1 file changed, 1 deletion(-) diff --git a/index.js b/index.js index 5f6b8d5..c971fdf 100644 --- a/index.js +++ b/index.js @@ -71,7 +71,6 @@ if (config["routerRegistration"] != "automated") { console.log("MAIN: Route registration mode: AUTOMATED") require('./routes').forEach(({ router, at, name }) => { try { - // console.log(`Registering ${name} at '${at}'`) app.use(at, router) } catch (err) { Logger.logAndThrow(`MAIN: Failed to register router auto-loaded from ${name} at '${at}'. Error: ${err}`) From 56ab84eee19a74c31a3db574778e4b6e869c8084 Mon Sep 17 00:00:00 2001 From: Nicholas Chew <3xpect1916@gmail.com> Date: Fri, 5 Jul 2024 12:01:26 +0800 Subject: [PATCH 16/18] removed hard coded timestamp --- models/ChatMessage.js | 6 ----- routes/chat/WebSocketServer.js | 40 ++++++++++++++++------------------ 2 files changed, 19 insertions(+), 27 deletions(-) diff --git a/models/ChatMessage.js b/models/ChatMessage.js index b947d31..38eb253 100644 --- a/models/ChatMessage.js +++ b/models/ChatMessage.js @@ -1,5 +1,3 @@ -const { Timestamp } = require('firebase-admin/firestore'); - /** * * @param {import('sequelize').Sequelize} sequelize @@ -25,10 +23,6 @@ module.exports = (sequelize, DataTypes) => { type: DataTypes.STRING, allowNull: false }, - timestamp: { - type: DataTypes.STRING, - allowNull: false - } }, { tableName: 'chatMessages' }) // Associations diff --git a/routes/chat/WebSocketServer.js b/routes/chat/WebSocketServer.js index 4fdd673..99f2b81 100644 --- a/routes/chat/WebSocketServer.js +++ b/routes/chat/WebSocketServer.js @@ -3,7 +3,6 @@ const http = require("http"); const WebSocket = require("ws"); const { ChatHistory, ChatMessage } = require("../../models"); const Universal = require("../../services/Universal"); -const mysql = require('mysql'); function startWebSocketServer(app) { const PORT = 8080; const server = http.createServer(app); @@ -51,25 +50,24 @@ function startWebSocketServer(app) { })); // Now you can handle further WebSocket message events - ws.on("message", async (message) => { - const parsedMessage = JSON.parse(message); - console.log("Received message:", parsedMessage); - if (parsedMessage.action === "edit") { - handleEditMessage(parsedMessage); - } else if (parsedMessage.action === "delete") { - handleDeleteMessage(parsedMessage); - } - else{ - try { - // Create ChatMessage in the database - const createdMessage = await ChatMessage.create({ - messageID: parsedMessage.messageid, - message: parsedMessage.message, - sender: parsedMessage.sender, - datetime: parsedMessage.datetime, - timestamp: parsedMessage.timestamp, - chatID: chatHistory.chatID, // Assign the chatID from ChatHistory - }); +// Now you can handle further WebSocket message events +ws.on("message", async (message) => { + const parsedMessage = JSON.parse(message); + console.log("Received message:", parsedMessage); + if (parsedMessage.action === "edit") { + handleEditMessage(parsedMessage, chatHistory.chatID, ws); + } else if (parsedMessage.action === "delete") { + handleDeleteMessage(parsedMessage); + } else { + try { + // Create ChatMessage in the database + const createdMessage = await ChatMessage.create({ + messageID: Universal.generateUniqueID(), + message: parsedMessage.message, + sender: parsedMessage.sender, + datetime: parsedMessage.datetime, + chatID: chatHistory.chatID, // Assign the chatID from ChatHistory + }); // Broadcast the message to all clients broadcastMessage(JSON.stringify(createdMessage), ws); @@ -139,7 +137,7 @@ function startWebSocketServer(app) { function broadcastMessage(message, sender) { clients.forEach((client) => { - if (client !== sender && client.readyState === WebSocket.OPEN) { + if (client.readyState === WebSocket.OPEN) { client.send(message); } }); From 790c6d6dc0df13389d576f073d0f144686a8f947 Mon Sep 17 00:00:00 2001 From: Nicholas Chew <3xpect1916@gmail.com> Date: Fri, 5 Jul 2024 12:38:59 +0800 Subject: [PATCH 17/18] changed error to show on frontend also --- routes/chat/WebSocketServer.js | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/routes/chat/WebSocketServer.js b/routes/chat/WebSocketServer.js index 99f2b81..4d085bf 100644 --- a/routes/chat/WebSocketServer.js +++ b/routes/chat/WebSocketServer.js @@ -93,11 +93,19 @@ ws.on("message", async (message) => { console.error("WebSocket server error:", error); }); - function handleEditMessage(editedMessage) { + async function handleEditMessage(editedMessage) { const messageId = editedMessage.id; - const findMessage = ChatMessage.findByPk(messageId); + // Assuming findByPk is asynchronous + const findMessage = await ChatMessage.findByPk(messageId); + if (!findMessage) { console.log("Message not found"); + const jsonMessage = { + action: "error", + message: "Error occured on the server", + }; + broadcastMessage(JSON.stringify(jsonMessage)); + return; } const editMessage = ChatMessage.update({ message: editedMessage.message, @@ -132,9 +140,6 @@ ws.on("message", async (message) => { } broadcastMessage(JSON.stringify(jsonMessage)); } - - - function broadcastMessage(message, sender) { clients.forEach((client) => { if (client.readyState === WebSocket.OPEN) { From 0cf6431ceabfc627b5a57c59021ccd963235d235 Mon Sep 17 00:00:00 2001 From: Nicholas Chew <3xpect1916@gmail.com> Date: Fri, 5 Jul 2024 14:18:34 +0800 Subject: [PATCH 18/18] removed timestamp and have item potency --- routes/chat/WebSocketServer.js | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/routes/chat/WebSocketServer.js b/routes/chat/WebSocketServer.js index 4d085bf..20041ae 100644 --- a/routes/chat/WebSocketServer.js +++ b/routes/chat/WebSocketServer.js @@ -95,6 +95,14 @@ ws.on("message", async (message) => { async function handleEditMessage(editedMessage) { const messageId = editedMessage.id; + if (!messageId) { + const jsonMessage = { + action: "error", + message: "ID not provided", + }; + broadcastMessage(JSON.stringify(jsonMessage)); + return; + } // Assuming findByPk is asynchronous const findMessage = await ChatMessage.findByPk(messageId); @@ -109,10 +117,7 @@ ws.on("message", async (message) => { } const editMessage = ChatMessage.update({ message: editedMessage.message, - timestamp: `${new Date().getHours()}:${new Date() - .getMinutes() - .toString() - .padStart(2, "0")}` + datetime: editedMessage.datetime, }, { where: { messageID: messageId, @@ -126,9 +131,20 @@ ws.on("message", async (message) => { function handleDeleteMessage(deletedMessage) { const messageId = deletedMessage.id; + if (!messageId) { + const jsonMessage = { + action: "reload", + } + broadcastMessage(JSON.stringify(jsonMessage)); + return; + } const findMessage = ChatMessage.findByPk(messageId); if (!findMessage) { - console.log("Message not found"); + const jsonMessage = { + action: "reload", + } + broadcastMessage(JSON.stringify(jsonMessage)); + return; } const deleteMessage = ChatMessage.destroy({ where: {