Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
53b3641
Merge pull request #15 from MakanMatch/main
Prakhar896 Jun 24, 2024
c478ddb
Merge pull request #17 from MakanMatch/main
Prakhar896 Jun 25, 2024
42fc00f
Merge pull request #18 from MakanMatch/main
Prakhar896 Jun 25, 2024
b1bd088
Merge pull request #19 from MakanMatch/main
Prakhar896 Jun 25, 2024
869c655
Merge pull request #20 from MakanMatch/main
Prakhar896 Jun 25, 2024
84fb2c3
setup websocket server and real time messaging temporary capabilities
nicholascheww Jun 25, 2024
b312c91
Added backend route for creating a sample guest
Sadliquid Jun 25, 2024
55d1fb4
Made dummy host and guest code to test full submitted review data
lincoln0623 Jun 26, 2024
7b07f42
Completed to abstract specific reviews with specific review ID
lincoln0623 Jun 26, 2024
238aab7
Added backend routes fo
Sadliquid Jun 26, 2024
6ddd08f
Optimised the getReviews route to fetch review data base on query
lincoln0623 Jun 26, 2024
9939e87
done basic chat functions
nicholascheww Jun 26, 2024
1c6212f
added backend for email verification
JunHammy Jun 26, 2024
11a7f31
Added backend route for edit listing
Sadliquid Jun 26, 2024
b62791b
edit and delete message able to work for sender
nicholascheww Jun 26, 2024
bb7e221
Updating a listing now deletes previous files attached to listing
Sadliquid Jun 26, 2024
f7cdfc6
Added backend route for deleting a listing as well as removing attach…
Sadliquid Jun 26, 2024
dde9988
Added JWT to handle auth state management
JunHammy Jun 27, 2024
c0585cb
Fixed get, update, delete specific review route method and test out w…
lincoln0623 Jun 27, 2024
e714e26
Removed backend route for editing a listing
Sadliquid Jun 27, 2024
76d61e4
linked up chat history to cache
nicholascheww Jun 27, 2024
cb0045b
Added fallback for failed image checks to remove all images from clou…
Sadliquid Jun 27, 2024
81e74f3
Refined auth state management
JunHammy Jun 27, 2024
9697fcd
Merge pull request #24 from MakanMatch/prakhar
Prakhar896 Jun 27, 2024
94c965d
Remove all non-existing attributes and unnecessary imports
lincoln0623 Jun 27, 2024
90f10cf
Changes sending error data method by using Logger.log, Removed displa…
lincoln0623 Jun 27, 2024
16cc79a
Changed middleware handling format
lincoln0623 Jun 27, 2024
0e0197f
added error handling if storeFiles middleware catch an error
lincoln0623 Jun 27, 2024
75137a1
Changed from using parameters to query for host reviews fetching
lincoln0623 Jun 27, 2024
a0a45a7
changed from findOne to findByPk
lincoln0623 Jun 27, 2024
7a9ee12
done edit and delete functions
nicholascheww Jun 27, 2024
f7b073d
Optimised update fields data fetching
lincoln0623 Jun 27, 2024
d352ec2
Checked result and reviewID for update and destroy before sending suc…
lincoln0623 Jun 27, 2024
e1ab025
Added multer
lincoln0623 Jun 27, 2024
ec51e55
Removed unused backend routes and added dummy account creation to onD…
Sadliquid Jun 27, 2024
3ce8a7e
updated code for PR #23
JunHammy Jun 27, 2024
afb6f20
changed saveUser check
Prakhar896 Jun 27, 2024
712709c
Merge pull request #23 from MakanMatch/junhan
Prakhar896 Jun 27, 2024
a9496d8
Merge branch 'main' into joshua
Prakhar896 Jun 27, 2024
e6061f8
Merge pull request #25 from MakanMatch/joshua
Prakhar896 Jun 27, 2024
4ea61fe
Done route sorting the images by descending order
lincoln0623 Jun 27, 2024
4ba329a
Merge branch 'main' into nicholas
Prakhar896 Jun 27, 2024
936ec15
Merge pull request #26 from MakanMatch/nicholas
Prakhar896 Jun 27, 2024
439c46c
Remove unnecessary else log and catch error
lincoln0623 Jun 28, 2024
8f944d2
Changed handling parameters form uerror to error, optimized response …
lincoln0623 Jun 28, 2024
9071f6e
removed unnecessary and redundant where clause
lincoln0623 Jun 28, 2024
4a6bbdc
handle UERROR
lincoln0623 Jun 28, 2024
dd64afe
integrating
Prakhar896 Jun 28, 2024
69affa0
Fixed bugs
lincoln0623 Jun 28, 2024
93ec9a2
remove unused import
lincoln0623 Jun 28, 2024
10a1221
handle uerror if no review id
lincoln0623 Jun 28, 2024
a6e8591
handle if there are no reveiws for host
lincoln0623 Jun 28, 2024
531a729
changed to array for response data
lincoln0623 Jun 28, 2024
0368252
Merge branch 'main' into lincoln
Prakhar896 Jun 28, 2024
7f829c1
Merge pull request #22 from MakanMatch/lincoln
Prakhar896 Jun 28, 2024
3a0694b
integrating
Prakhar896 Jun 28, 2024
96152ce
chat messages able to be saved into sql
nicholascheww Jun 28, 2024
1c91217
linked up chat message model to chat history model
nicholascheww Jun 28, 2024
4221136
improved some stuff
Prakhar896 Jun 28, 2024
f9fc8a1
weird bug where created listing is deleted upon a subsequent boot
Prakhar896 Jun 28, 2024
e4b8476
dbTools now accepts multiple tool activations
Prakhar896 Jun 28, 2024
57c7aec
Merge branch 'nicholas' into main
Prakhar896 Jun 28, 2024
98fa1c6
reimported Cache
Prakhar896 Jun 28, 2024
32abd97
Merge branch 'prakhar' into main
Prakhar896 Jun 29, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 12 additions & 6 deletions dbTools.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,21 @@ async function clearFiles() {

sequelize.sync({ alter: true })
.then(() => {
if (!process.argv[2] || process.argv[2].length == 0) { console.log("No tool activated."); return; }
if (process.argv[2].toLowerCase() == "reset") {
resetDB()
} else if (process.argv[2].toLowerCase() == "clearfiles") {
clearFiles()
} else {
const tools = process.argv.slice(2)
if (tools.length == 0) {
console.log("No tool activated.")
return
}
console.log(`Tools activated: ${tools.join(", ")}`)
console.log()

if (tools.includes("reset")) {
resetDB()
}

if (tools.includes("clearfiles")) {
clearFiles()
}
})
.catch(err => {
console.error(err)
Expand Down
65 changes: 38 additions & 27 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ require('./services/BootCheck').check()
const express = require('express');
const cors = require('cors');
const { v4: uuidv4 } = require('uuid')
const { FoodListing, Host } = require('./models')
const { FoodListing, Guest, Host } = require('./models')
const Encryption = require("./services/Encryption")
require('dotenv').config()

const env = process.env.DB_CONFIG || 'development';
Expand All @@ -12,7 +13,6 @@ const SEQUELIZE_ACTIVE = true;

// Set up services
const Universal = require('./services/Universal')
const FileOps = require('./services/FileOps')

const Logger = require('./services/Logger')
Logger.setup()
Expand All @@ -26,17 +26,21 @@ Cache.load();
const FileManager = require('./services/FileManager');
FileManager.setup().catch(err => { Logger.logAndThrow(err) })



// Import middleware
const checkHeaders = require('./middleware/headersCheck');
const logRoutes = require('./middleware/logRoutes');

// Configure express app
// Configure express app and chat web socket server
const app = express();
app.use(cors())
app.use(express.json())
app.use(express.urlencoded({ extended: true }))
// app.use(express.static('public'))
app.set("view engine", "ejs");
const startWebSocketServer = require('./routes/chat/WebSocketServer');
startWebSocketServer(app);

// Top-level middleware
if (config["routeLogging"] !== false) { app.use(logRoutes) }
Expand All @@ -57,42 +61,43 @@ 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"));

async function onDBSynchronise() {
const currentDatetime = new Date()
const datetime = new Date(currentDatetime.getTime() + 24 * 60 * 60 * 1000).toISOString();

const listings = await FoodListing.findAll()
if (listings.length > 0) {
Universal.data["DUMMY_LISTING_ID"] = listings[0].listingID
console.log(`Found existing listing, using as dummy. Listing ID: ${listings[0].listingID}`)
const guests = await Guest.findAll()
if (guests.length > 0) {
Universal.data["DUMMY_GUEST_ID"] = guests[0].userID
Universal.data["DUMMY_GUEST_USERNAME"] = guests[0].username
console.log(`Found existing guest, using as dummy. Guest User ID: ${guests[0].userID}`)
} else {
const newListing = await FoodListing.create({
listingID: uuidv4(),
title: "Chili Crab for Dinner",
images: "sample3.jpg",
shortDescription: "Making chili crab for dinner again! Come join!",
longDescription: "Seeing that chili crab last time was a hit, cooking some again! Bought fresh groceries from the market today for it too. Come join me for dinner!",
portionPrice: "5.00",
approxAddress: "Near Tampines West Community Centre, Singapore",
address: "Block 67, Tampines Avenue 10, Singapore 520678",
totalSlots: "5",
datetime: datetime,
published: false
const newGuest = await Guest.create({
userID: "47f4497b-1331-4b8a-97a4-095a79a1fd48",
username: "Susie Jones",
email: "susie_jones@gmail.com",
password: await Encryption.hash("SusieJones123"),
contactNum: "82228111",
address: "Block 321, Hougang Avenue 10, #10-567",
emailVerified: false,
favCuisine: "",
mealsMatched: 0,
resetKey: "265c18",
resetKeyExpiration: "2024-06-22T14:30:00.000Z"
})
Universal.data["DUMMY_LISTING_ID"] = newListing.listingID
console.log(`Created dummy listing with ID: ${newListing.listingID}`)
Universal.data["DUMMY_GUEST_ID"] = newGuest.userID
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({
"userID": "272d3d17-fa63-49c4-b1ef-1a3b7fe63cf4",
"username": "Jamie Oliver",
"email": "jamie_oliver@gmail.com",
"password": "123456789",
"password": await Encryption.hash("123456789"),
"contactNum": "81118222",
"address": "12 Washington Avenue",
"emailVerified": false,
Expand All @@ -106,9 +111,15 @@ async function onDBSynchronise() {
if (!newHost) {
console.log("WARNING: Failed to create dummy host.")
} else {
Universal.data["DUMMY_HOST_ID"] = newHost.userID
Universal.data["DUMMY_HOST_USERNAME"] = newHost.username
Universal.data["DUMMY_HOST_FOODRATING"] = newHost.foodRating
console.log("Created dummy host.")
}
} else {
Universal.data["DUMMY_HOST_ID"] = joshuasHost.userID
Universal.data["DUMMY_HOST_USERNAME"] = joshuasHost.username
Universal.data["DUMMY_HOST_FOODRATING"] = joshuasHost.foodRating
console.log("Found dummy host existing already, skipping creation.")
}
}
Expand All @@ -121,7 +132,7 @@ if (!SEQUELIZE_ACTIVE) {
} else {
// Server initialisation with sequelize
const db = require("./models");
db.sequelize.sync({ alter: true })
db.sequelize.sync()
.then(() => {
// Create sample FoodListing
onDBSynchronise()
Expand Down
28 changes: 26 additions & 2 deletions middleware/auth.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,27 @@
require('dotenv').config()
const jwt = require('jsonwebtoken');
require('dotenv').config();

// TODO
const validateToken = (req, res, next) => {
const authHeader = req.headers.authorization;
if (!authHeader) {
console.error('No authorization header');
return res.sendStatus(401);
}

const token = authHeader.split(' ')[1];
if (!token) {
console.error('No token found in authorization header');
return res.sendStatus(401);
}

jwt.verify(token, process.env.JWT_KEY, (err, user) => {
if (err) {
console.error('Token verification failed:', err);
return res.sendStatus(403);
}
// console.log("Decoded this user: " + user)
req.user = user;
next();
});
};
module.exports = { validateToken };
7 changes: 5 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,23 @@
"dependencies": {
"axios": "^1.7.2",
"bcrypt": "^5.1.1",
"cookie-parser": "^1.4.6",
"cors": "^2.8.5",
"dotenv": "^16.4.5",
"ejs": "^3.1.10",
"express": "^4.19.2",
"express-ws": "^5.0.2",
"firebase": "^10.12.2",
"firebase-admin": "^12.1.1",
"fs": "^0.0.1-security",
"jsonwebtoken": "^9.0.2",
"multer": "^1.4.5-lts.1",
"mysql2": "^3.10.0",
"nodemailer": "^6.9.13",
"sequelize": "^6.37.3",
"sqlite3": "^5.1.7",
"uuid": "^10.0.0",
"yup": "^1.4.0",
"uuidv4": "^6.2.13"
"uuidv4": "^6.2.13",
"yup": "^1.4.0"
}
}
133 changes: 121 additions & 12 deletions routes/cdn/coreData.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,39 @@ const express = require("express");
const router = express.Router();
const path = require("path");
const FileManager = require("../../services/FileManager");
const { FoodListing, Host, Guest, Admin } = require("../../models");
const { FoodListing, Host, Guest, Admin, Review } = require("../../models");
const Logger = require("../../services/Logger");
const { Sequelize } = require('sequelize');
const Universal = require("../../services/Universal");
const { validateToken } = require("../../middleware/auth");

router.get('/MyAccount', validateToken, (req, res) => {
const userInfo = req.user;
console.log(userInfo)
res.json(userInfo);
});

router.get("/fetchHostDetails", async (req, res) => {
const hostDetails = {
hostUsername: Universal.data["DUMMY_HOST_USERNAME"],
hostFoodRating: Universal.data["DUMMY_HOST_FOODRATING"]
}
res.status(200).json(hostDetails);
})

router.get("/fetchGuestDetails", async (req, res) => {
const targetGuest = await Guest.findByPk(Universal.data["DUMMY_GUEST_ID"])
if (!targetGuest) {
return res.status(404).send("Dummy Guest not found.");
}
const guestFavCuisine = targetGuest.favCuisine;
const guestDetails = {
guestUserID: Universal.data["DUMMY_GUEST_ID"],
guestUsername: Universal.data["DUMMY_GUEST_USERNAME"],
guestFavCuisine: guestFavCuisine
}
res.status(200).json(guestDetails);
})

router.get("/listings", async (req, res) => { // GET all food listings
try {
Expand All @@ -14,6 +46,25 @@ router.get("/listings", async (req, res) => { // GET all food listings
}
});

router.get("/checkFavouriteListing", async (req, res) => { // GET favourite listing
try {
const listingID = req.query.listingID;
const userID = req.query.userID;
const guest = await Guest.findByPk(userID);
if (!guest) {
return res.status(404).send("Guest not found.");
}
const favouriteCuisines = guest.favCuisine.split("|");
if (favouriteCuisines.includes(listingID)) {
res.status(200).json({ message: "SUCCESS: Listing is a favourite", listingIsFavourite: true });
} else {
res.status(200).json({ message: "SUCCESS: Listing is not a favourite", listingIsFavourite: false });
}
} catch (error) {
res.status(500).send("ERROR: Internal server error");
}
});

router.get("/getListing", async (req, res) => {
const listingID = req.query.id || req.body.listingID;
if (!listingID) {
Expand Down Expand Up @@ -80,26 +131,84 @@ router.get("/accountInfo", async (req, res) => { // GET account information
accountInfo.mealsMatched = user.mealsMatched;
}

// console.log(`Account info for userID ${targetUserID}: ${JSON.stringify(accountInfo)}`)
res.status(200).json(accountInfo);

} catch (err) {
res.status(500).send("An error occured while fetching the account information.")
}
})

router.get("/getReviews", (req, res) => { // GET full reviews list
res.json({}); // Placeholder, change later
router.get("/getReviews", async (req, res) => { // GET full reviews list
try {
const where = {};
const order = [];

if (!req.query.hostID) {
return res.status(400).send("UERROR: Missing host ID or order.");
} else {
where.hostID = req.query.hostID;
}

if (req.query.order) {
if (req.query.order === "mostRecent") {
order.push(['dateCreated', 'DESC']);
} else if (req.query.order === "highestRating") {
order.push([
Sequelize.literal('foodRating + hygieneRating'), 'DESC'
])
} else if (req.query.order === "lowestRating") {
order.push([
Sequelize.literal('foodRating + hygieneRating'), 'ASC'
])
} else {
order.push(['dateCreated', 'DESC']);
}
}

try {
const host = await Host.findByPk(req.query.hostID);
if (!host) {
return res.status(404).send("UERROR: Host not found.");
} else {
const reviews = await Review.findAll({
where,
order,
})

if (req.query.order === "images") {
reviews.sort((a, b) => {
const imageCountA = a.images ? a.images.split("|").length : 0;
const imageCountB = b.images ? b.images.split("|").length : 0;
return imageCountB - imageCountA;
});
}

if (reviews.length > 0) {
res.json(reviews);
}
}
} catch (err) {
Logger.log(`CDN COREDATA GETREVIEWS ERROR: Failed to retrieve reviews; error: ${err}.`);
return res.status(404).send("ERROR: No reviews found.");
}

} catch (err) {
Logger.log(`CDN COREDATA GETREVIEWS ERROR: Failed to retrieve reviews; error: ${err}.`);
return res.status(500).send("ERROR: An error occured while fetching reviews.");
}
})

router.get("/reviews", (req, res) => { // GET review from review id
res.send("TBC")
// const review = reviews[req.query.id];
// if (review) {
// res.json(review);
// } else {
// res.status(404).send(`Review with ID ${req.params.id} not found`);
// }
router.get("/reviews", async (req, res) => { // GET review from review id
if (!req.query.id) {
return res.status(400).send("UERROR: Missing review ID");
} else {
const review = await Review.findByPk(req.query.id);
if (review) {
res.json(review);
} else {
return res.status(404).send(`ERROR: Review with ID ${req.params.id} not found`);
}
}
})

module.exports = router;
Loading