Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
7d7ac86
Merge pull request #84 from MakanMatch/main
Prakhar896 Jul 28, 2024
d6b8284
Added upload and remove profile picture endpoint
JunHammy Jul 28, 2024
25ac6b8
Refined contact number validation to accept empty in the case user wa…
JunHammy Jul 28, 2024
b790de6
Added endpoints for user management
JunHammy Jul 28, 2024
650a043
Refined fetchAllUsers endpoint for hygiene reports page
JunHammy Jul 28, 2024
c5db59a
Added sanitise data to fetchAllUsers for user management and hygiene …
JunHammy Jul 28, 2024
ec71221
added authentication measures
nicholascheww Jul 29, 2024
cdf2f8e
added api for upload image
nicholascheww Jul 29, 2024
523fb0a
Added create admin function in dbtools
JunHammy Jul 29, 2024
ae0ba3f
Added checking of Admin table in /changeAddress
JunHammy Jul 30, 2024
0152029
images name are being saved into the database
nicholascheww Jul 30, 2024
896c75c
images being shown in chat bubble
nicholascheww Jul 30, 2024
c0910bd
Updated code for PR #85
JunHammy Jul 31, 2024
88e2d35
changed how image messages are being sent as well as changing creatin…
nicholascheww Jul 31, 2024
e4a2156
Merge pull request #88 from MakanMatch/prakhar
Prakhar896 Jul 31, 2024
c2703f6
Fixed minor changes for PR #85
JunHammy Jul 31, 2024
12d85c7
removed debug console logs
Prakhar896 Jul 31, 2024
e783f79
fixed misspelling
Prakhar896 Jul 31, 2024
b0aa30c
changed how the API works and also renamed some logs tag
nicholascheww Jul 31, 2024
8298541
Merge branch 'main' into junhan
Prakhar896 Jul 31, 2024
74f56f1
Merge pull request #85 from MakanMatch/junhan
Prakhar896 Jul 31, 2024
6279b74
fixed improper FileManager check
Prakhar896 Jul 31, 2024
6584a1d
Merge branch 'main' into nicholas
Prakhar896 Jul 31, 2024
ac341b1
Merge pull request #86 from MakanMatch/nicholas
Prakhar896 Jul 31, 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
48 changes: 47 additions & 1 deletion dbTools.js
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,48 @@ async function createGuest() {
console.log(createdGuestIDs.length + " guests created successfully.")
}

async function createAdmin() {
var creating = true;
var createdAdminIDs = []
while (creating) {
console.log("")
console.log("Creating a new admin...")

const userID = uuidv4()
try {
const admin = await Admin.create({
userID: userID,
fname: prompt("Enter admin first name: "),
lname: prompt("Enter admin last name: "),
username: prompt("Enter admin username: "),
email: prompt("Email (must be unique): "),
password: await Encryption.hash(prompt("Password: ")),
contactNum: prompt("Phone number (must be unique) (optional): ") || null,
address: prompt("Address (optional): ") || null,
emailVerified: prompt("Email verified? (y/n): ").toLowerCase() !== 'n',
role: prompt("Role: ") || "MakanMatchAdmin"
})
} catch (err) {
console.log("Failed to create admin; error: " + err)
creating = prompt("Try again? (y/n) ") == "y"
console.log("")
continue
}

console.log("Admin created!")
console.log(`Admin ID: ${userID}`)
console.log("")
createdAdminIDs.push(userID)

if (prompt("Create another admin? (y/n): ").toLowerCase() !== 'y') {
creating = false;
console.log("")
}
}

console.log(createdAdminIDs.length + " admins created successfully.")
}

async function signJWT() {
console.log("")
if (!process.env.JWT_KEY) { console.log("JWT_KEY not found in .env; aborting..."); return; }
Expand Down Expand Up @@ -193,7 +235,11 @@ sequelize.sync({ alter: true })
if (tools.includes("createguest")) {
await createGuest()
}


if (tools.includes("createadmin")) {
await createAdmin()
}

if (tools.includes("signjwt")) {
await signJWT()
}
Expand Down
1 change: 1 addition & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ if (config["routerRegistration"] != "automated") {
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);
app.use(require("./routes/chat/manageChat").at || '/', require("./routes/chat/manageChat").router);
} else {
console.log("MAIN: Route registration mode: AUTOMATED")
require('./routes').forEach(({ router, at, name }) => {
Expand Down
23 changes: 23 additions & 0 deletions middleware/storeImage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
const multer = require('multer');
const path = require('path');
const { v4: uuidv4 } = require('uuid');
const imageFileFilter = require('./imageFileFilter');


const storage = multer.diskStorage({
destination: (req, file, callback) => {
callback(null, path.join(__dirname, '../FileStore'))
},
filename: (req, file, callback) => {
callback(null, uuidv4() + path.extname(file.originalname))
}
})

const storeImage = multer({
storage: storage,
limits: { fileSize: 1024 * 1024 * 10 },
fileFilter: imageFileFilter
})
.single('image')

module.exports = { storeImage };
4 changes: 4 additions & 0 deletions models/Admin.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ module.exports = (sequelize, DataTypes) => {
type: DataTypes.STRING,
allowNull: false
},
profilePicture: {
type: DataTypes.STRING,
allowNull: true
},
resetKey: {
type: DataTypes.STRING,
allowNull: true
Expand Down
4 changes: 4 additions & 0 deletions models/ChatMessage.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ module.exports = (sequelize, DataTypes) => {
type: DataTypes.STRING,
allowNull: false
},
image:{
type: DataTypes.STRING,
allowNull: true
},
senderID: {
type: DataTypes.STRING,
allowNull: false
Expand Down
4 changes: 4 additions & 0 deletions models/Guest.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ module.exports = (sequelize, DataTypes) => {
allowNull: false,
defaultValue: 0
},
profilePicture: {
type: DataTypes.STRING,
allowNull: true
},
resetKey: {
type: DataTypes.STRING,
allowNull: true
Expand Down
4 changes: 4 additions & 0 deletions models/Host.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@ module.exports = (sequelize, DataTypes) => {
type: DataTypes.STRING,
allowNull: true
},
profilePicture: {
type: DataTypes.STRING,
allowNull: true
},
resetKey: {
type: DataTypes.STRING,
allowNull: true
Expand Down
170 changes: 57 additions & 113 deletions routes/cdn/contentDelivery.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ const express = require("express");
const router = express.Router();
const FileManager = require("../../services/FileManager");
const path = require("path");
const { FoodListing, Review, Host, Guest, Admin } = require("../../models");
const { FoodListing, Review, Host, Guest, Admin, ChatMessage, ChatHistory } = require("../../models");
const { checkUser } = require("../../middleware/auth");
const { Logger, Extensions, TokenManager } = require("../../services");
const { Op } = require("sequelize");

router.get("/getImageForListing", async (req, res) => {
const { listingID, imageName } = req.query;
Expand Down Expand Up @@ -60,131 +61,74 @@ router.get("/getImageForReview", async (req, res) => {
return;
});

router.get("/getHostPaymentQR", checkUser, async (req, res) => {
const { listingID, hostID, token } = req.query;
var userID;
var userType;
if (!req.user) {
if (!token) {
return res.status(403).send("ERROR: Authorisation tests failed.");
} else {
try {
const { payload } = TokenManager.default().verify(token, false);
userID = payload.userID;

var user = await Guest.findByPk(userID, { attributes: ["userID"] });
userType = "Guest";
if (!user) {
user = await Host.findByPk(userID, { attributes: ["userID"] });
userType = "Host";
if (!user) {
user = await Admin.findByPk(userID, { attributes: ["userID"] });
userType = "Admin";
if (!user) {
return res.status(404).send("ERROR: User not found.");
}
}
}
} catch (err) {
return res.status(403).send("ERROR: Authorisation tests failed.");
}
}
} else {
userID = req.user.userID;
userType = req.user.userType;
router.get("/getImageForChat", async (req, res) => {
const { userID, messageID } = req.query;
if (!userID || !messageID) {
return res.status(400).send("ERROR: Invalid request parameters.");
}

var user;
var imageName;
if (userType == "Host" || userType == "Admin") {
// User is Host or Admin, deliver image directly through host record
var identifier = userID;
if (userType == "Admin") {
if (!hostID) {
return res.status(400).send("ERROR: Host ID not provided.");
}
identifier = hostID;
}
// Find the user
const user = await Guest.findByPk(userID) || await Host.findByPk(userID);
if (!user) {
return res.status(404).send("ERROR: User not found")
}

try {
user = await Host.findByPk(identifier);
if (!user) {
return res.status(404).send("ERROR: User not found.")
}
const message = await ChatMessage.findByPk(messageID);
if (!message) {
return res.status(404).send("ERROR: Message not found.");
}

if (!user.paymentImage) {
return res.status(200).send("SUCCESS: Host does not have a payment QR image yet.");
}
var imageName = message.image;

imageName = user.paymentImage;
} catch (err) {
Logger.log(`CDN GETHOSTPAYMENTQR ERROR: Failed to find host; error: ${err}.`);
return res.status(500).send("ERROR: Failed to process request.");
}
} else if (userType == "Guest") {
// User is guest, find the listing with an associated host, and get the image from there
if (!listingID) {
return res.status(400).send("ERROR: Listing ID not provided.");
}
const chatHistory = await ChatHistory.findByPk(message.chatID);

try {
const listing = await FoodListing.findByPk(listingID, {
include: [
{
model: Guest,
as: "guests",
where: { userID: userID }
},
{
model: Host,
as: "Host",
attributes: ["userID", "paymentImage"]
}
]
});
if (!listing || listing.guests.length == 0) {
return res.status(404).send("ERROR: Listing not found.");
} else if (Extensions.timeDiffInSeconds(new Date(), new Date(listing.datetime)) > 21600) {
return res.status(400).send("ERROR: Payment QR image not available yet.")
}
if(!chatHistory){
return res.status(404).send("ERROR: Chat history not found.");
}

if (!listing.Host.paymentImage) {
return res.status(200).send("SUCCESS: Host does not have a payment QR image yet.");
}

imageName = listing.Host.paymentImage;
} catch (err) {
Logger.log(`CDN GETHOSTPAYMENTQR ERROR: Failed to find listing; error: ${err}.`);
return res.status(500).send("ERROR: Failed to process request.");
}
if (chatHistory.user1ID != userID && chatHistory.user2ID != userID) {
return res.status(404).send("ERROR: User is not authorised to view this image.");
}

const findPaymentQR = await FileManager.prepFile(imageName);
if (!findPaymentQR.startsWith("SUCCESS")) {
Logger.log(`CDN GETHOSTPAYMENTQR ERROR: Failed to find payment QR image; error: ${findPaymentQR}`);
res.status(400).send("ERROR: Failed to find payment QR image.");
return;
const findImageName = await FileManager.prepFile(imageName);
if (!findImageName.startsWith("SUCCESS")) {
return res.status(404).send("ERROR: Image not found.");
}

return res.status(200).sendFile(findPaymentQR.substring("SUCCESS: File path: ".length));
})
return res.status(200).sendFile(findImageName.substring("SUCCESS: File path: ".length));
});

// router.get("/getProfilePicture", async (req, res) => {
// const { userID } = req.query;
// if (!userID) {
// res.status(400).send("ERROR: Invalid request parameters.");
// return;
// }
router.get("/getProfilePicture", async (req, res) => {
const { userID } = req.query;
if (!userID) {
res.status(400).send("ERROR: Invalid request parameters.");
return;
}

// const findImageName = await FileManager.prepFile(imageName);
// if (!findImageName.startsWith("SUCCESS")) {
// res.status(404).send("ERROR: Image not found.");
// return;
// }
// Check if image name is indeed a a pfp reference in user's record
let user = await Guest.findByPk(userID, { attributes: ["userID", "profilePicture"] }) ||
await Host.findByPk(userID, { attributes: ["userID", "profilePicture"] }) ||
await Admin.findByPk(userID, { attributes: ["userID", "profilePicture"] });
if(!user) {
res.status(404).send("ERROR: User not found.");
return;
}

const profilePicture = user.profilePicture;
if (!profilePicture) {
res.status(204).send("SUCCESS: User profile picture not found.");
return;
}

// res.status(200).sendFile(findImageName.substring("SUCCESS: File path: ".length))
// return;
// });
// Find image using FileManager
const findImageName = await FileManager.prepFile(profilePicture);
if (!findImageName.startsWith("SUCCESS")) {
res.status(404).send("ERROR: Image not found.");
return;
}

res.status(200).sendFile(findImageName.substring("SUCCESS: File path: ".length))
return;
});

router.get("/getHostPaymentQR", checkUser, async (req, res) => {
const { listingID, hostID, token } = req.query;
Expand Down
46 changes: 46 additions & 0 deletions routes/cdn/coreData.js
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,52 @@ router.get("/getReviews", checkUser, async (req, res) => { // GET full reviews l
}
})

router.get("/fetchAllUsers", validateToken, async (req, res) => { // GET all users
const fetchHostsOnly = req.query.fetchHostsOnly || "false";

const currentUserType = req.user.userType;
if (currentUserType !== "Admin") {
return res.status(403).send("ERROR: Unauthorized access.");
}

const hosts = await Host.findAll();
const hostsWithUserType = (hosts.map(host => {
const hostObj = host.toJSON(); // Convert Sequelize instance to plain object
hostObj.userType = "Host";
return hostObj;
}));

let responseArray = [];

if (fetchHostsOnly === "false") {
const guests = await Guest.findAll();
const guestsWithUserType = (guests.map(guest => {
const guestObj = guest.toJSON(); // Convert Sequelize instance to plain object
guestObj.userType = "Guest";
return guestObj;
}));
const allUsers = hostsWithUserType.concat(guestsWithUserType);
if (!allUsers || !Array.isArray(allUsers) || allUsers.length === 0) {
return res.status(200).send([]);
} else {
allUsers.forEach(user => {
responseArray.push(Extensions.sanitiseData(user, ["userID", "username", "email", "userType", "hygieneGrade"], ["password"], []));
});
return res.status(200).json(responseArray);
}
} else {
if (!hostsWithUserType || !Array.isArray(hostsWithUserType) || hostsWithUserType.length === 0) {
return res.status(200).send([]);
} else {
warningHosts = hostsWithUserType.filter(host => host.hygieneGrade <= 2.5);
warningHosts.forEach(host => {
responseArray.push(Extensions.sanitiseData(host, ["userID", "username", "email", "userType", "hygieneGrade"], ["password"], []));
});
return res.status(200).json(responseArray);
}
}
});

router.get("/consolidateReviewsStatistics", async (req, res) => { // GET full reservations list
const { hostID } = req.query;
if (!hostID) {
Expand Down
Loading