Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
6e60826
Added file
Sadliquid Jun 9, 2024
41eb3c2
Merge pull request #2 from MakanMatch/main
Prakhar896 Jun 10, 2024
ce84a48
Merge pull request #7 from MakanMatch/main
Prakhar896 Jun 14, 2024
ed46f25
Added backend routes for creating sample host, creating food listings…
Sadliquid Jun 18, 2024
2fd7411
Implemented image upload to Firebase Cloud Storage
Sadliquid Jun 20, 2024
e6ef66c
Added updating of images to the firebase public url for rendering on UI
Sadliquid Jun 20, 2024
8606c8c
Updated host UUID (hardcoded)
Sadliquid Jun 20, 2024
6c1220a
Fixed bug regarding SVG uploads
Sadliquid Jun 21, 2024
1bdd544
Tidied up imports and used Logger for successful image uploads
Sadliquid Jun 21, 2024
d11f681
Re-organised middleware
Sadliquid Jun 21, 2024
206e53d
Thoroughly enhanced robust-ness of backend logic, and error handling.…
Sadliquid Jun 21, 2024
f3336ec
Adjusted date-time formatting to conform to ISO string standard
Sadliquid Jun 22, 2024
4c9c0af
Standardised all responses to include status codes
Sadliquid Jun 22, 2024
45ddf16
Added in missing milliseconds in ISO string format. very very small c…
Sadliquid Jun 22, 2024
fe7790e
Moved Logger logging statement to API endpoint where image is actuall…
Sadliquid Jun 23, 2024
d8e6bf6
Placed all constants in one line and removed custom length for genera…
Sadliquid Jun 23, 2024
7a95ef2
Updated imports, simplified createHost route and removed verifyListing
Sadliquid Jun 23, 2024
e1c7544
Combined endpoints and fixed image bug
Sadliquid Jun 23, 2024
a7abd97
Added additional check if requested image is tied to correct listing
Sadliquid Jun 23, 2024
7d57969
merging paths
Prakhar896 Jun 23, 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
5 changes: 2 additions & 3 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,9 @@ app.get("/", (req, res) => {
})

// Register routers
app.use("/misc", require("./routes/misc"));

// API routes
app.use(checkHeaders) // Middleware to check Content-Type and API key headers
app.use("/misc", require("./routes/misc"));
app.use("/listings", require("./routes/listings/listings"));
app.use("/", require("./routes/orders/reservation"));

async function onDBSynchronise() {
Expand Down
17 changes: 17 additions & 0 deletions middleware/ListingsFileFilter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
const path = require('path');

const ListingsFileFilter = function (req, file, cb) {
const allowedMIMETypes = /jpeg|jpg|png|svg\+xml/;
const allowedExtensions = /jpeg|jpg|png|svg/;

const mimetype = allowedMIMETypes.test(file.mimetype);
const extname = allowedExtensions.test(path.extname(file.originalname).toLowerCase());

if (mimetype && extname) {
cb(null, true);
} else {
cb(new Error('Only .jpeg, .jpg, .png, and .svg files are allowed'), false);
}
};

module.exports = ListingsFileFilter;
23 changes: 23 additions & 0 deletions middleware/ListingsStoreFile.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 ListingsFileFilter = require('./ListingsFileFilter');


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 ListingsStoreFile = multer({
storage: storage,
limits: { fileSize: 1024 * 1024 * 10 },
fileFilter: ListingsFileFilter
})
.single('images')

module.exports = ListingsStoreFile;
25 changes: 24 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
"nodemailer": "^6.9.13",
"sequelize": "^6.37.3",
"sqlite3": "^5.1.7",
"uuid": "^10.0.0"
"uuid": "^10.0.0",
"uuidv4": "^6.2.13"
}
}
154 changes: 154 additions & 0 deletions routes/listings/listings.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
const express = require("express");
const multer = require("multer");
const router = express.Router();
const { FoodListing } = require("../../models");
const { Host } = require("../../models");
const Universal = require("../../services/Universal");
const FileManager = require("../../services/FileManager");
const ListingsStoreFile = require("../../middleware/ListingsStoreFile");

router.post("/createHost", async (req, res) => {
// POST a new host before creating a food listing
const data = req.body;
try {
const newHost = await Host.create(data);
res.status(200).json({
message: "Host created successfully!",
newHost,
});
} catch (error) {
console.error("Error creating host:", error);
res.status(400).json({
error: "One or more required payloads were not provided.",
});
}
});

router.get("/hostInfo", async (req, res) => {
try {
// GET host info before displaying listing's host name
const hostInfo = await Host.findByPk(
"272d3d17-fa63-49c4-b1ef-1a3b7fe63cf4"
); // hardcoded for now
if (hostInfo) {
res.status(200).json(hostInfo);
} else {
res.status(404).json({ error: "Host not found" });
}
} catch (error) {
console.error("Error fetching host info:", error);
res.status(500).json({ error: "Failed to fetch host info" });
}
});

router.get("/", async (req, res) => {
// GET all food listings
try {
const foodListings = await FoodListing.findAll();
res.status(200).json(foodListings);
} catch (error) {
console.error("Error retrieving food listings:", error);
res.status(500).json({ error: "Internal server error" });
}
});

// Flow: addListing -> refreshes -> fetchListings -> Image component sources from /getImageForListing?listingID=<value>&imageName=<value> -> send file back down -> Image component renders

router.get("/getImageForListing", async (req, res) => {
const { listingID, imageName } = req.query;
if (!listingID || !imageName) {
res.status(400).send("ERROR: Invalid request parameters.");
console.error("Invalid request parameters.");
return;
}

// Find the listing
const findListing = await FoodListing.findByPk(listingID);
const findImageName = await FileManager.prepFile(imageName);
if (!findImageName) {
res.status(404).send("ERROR: Image not found.");
console.error("Image not found.");
return;
}
if (!findListing) {
res.status(404).send("ERROR: Listing not found.");
console.error("Listing not found.");
return;
}
if (findListing.images !== imageName) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will need to change to findListing.images.split("|") later on. Do take note.

res.status(404).send("ERROR: Requested image does not belong to its corresponding listing.");
console.error("Requested image does not belong to its corresponding listing.");
return;
}
res.status(200).sendFile(findImageName.substring("SUCCESS: File path: ".length))
return;
});

router.post("/addListing", async (req, res) => {
Comment thread
Sadliquid marked this conversation as resolved.
ListingsStoreFile(req, res, async (err) => {
console.log(req.body);
if (
!req.body.title ||
!req.body.shortDescription ||
!req.body.longDescription ||
!req.body.portionPrice ||
!req.body.totalSlots ||
!req.body.datetime
) {
res.status(400).send(
"One or more required payloads were not provided"
);
return;
} else {
if (err instanceof multer.MulterError) {
console.error("Multer error:", err);
res.status(400).send("Image upload error");
} else if (err) {
console.error("Unknown error occured during upload:", err);
res.status(500).send("Internal server error");
} else if (!req.file) {
res.status(400).send("No file was selected to upload");
} else {
const uploadImageResponse = await FileManager.saveFile(
req.file.filename
);
if (uploadImageResponse) {
const formattedDatetime = req.body.datetime + ":00.000Z";
const listingDetails = {
listingID: Universal.generateUniqueID(),
title: req.body.title,
images: req.file.filename,
shortDescription: req.body.shortDescription,
longDescription: req.body.longDescription,
portionPrice: req.body.portionPrice,
totalSlots: req.body.totalSlots,
datetime: formattedDatetime,
approxAddress: "Yishun, Singapore", // hardcoded for now
address:
"1 North Point Dr, #01-164/165 Northpoint City, Singapore 768019", // hardcoded for now
hostID: "272d3d17-fa63-49c4-b1ef-1a3b7fe63cf4", // hardcoded for now
published: true,
};
const addListingResponse = await FoodListing.create(
listingDetails
);
if (addListingResponse) {
res.status(200).json({
message: "Food listing created successfully",
listingDetails,
});
return;
} else {
res.status(400).send("Failed to create food listing");
return;
}
} else {
res.status(400).send("Failed to upload image");
return;
}
}
}
});
});

module.exports = router;