diff --git a/backend/controllers/category.js b/backend/controllers/category.js new file mode 100644 index 0000000..90cbd94 --- /dev/null +++ b/backend/controllers/category.js @@ -0,0 +1,47 @@ +const db = require("../utils/database"); + +exports.getAllCategoriesWithPagination = async (req, res) => { + const limit = +req.query?.limit; + const page = +req.query?.page; + const offset = (page - 1) * limit; + try { + const [data, _] = await db.execute( + "SELECT * FROM Category C ORDER BY C.CategoryID ASC LIMIT ? OFFSET ?", + [limit.toString(), offset.toString()] + ); + + const [result] = await db.execute("SELECT COUNT(*) AS count FROM Category"); + const { count: total } = result[0]; + return res.json({ data, total }); + } catch (error) { + res.json({ error: "Cannot fetch categories from database!" }); + } +}; + +exports.addCategory = async (req, res) => { + const { name } = req.body; + + try { + const [result] = await db.execute( + "INSERT INTO Category (Name) VALUES (?)", + [name] + ); + res.json({ message: "Adding new category successfully!" }); + } catch (error) { + res.json({ error: "Cannot add new category!" }); + } +}; + +exports.removeCategory = async (req, res) => { + const { id } = req.params; + + try { + const [result] = await db.execute( + `DELETE FROM Category WHERE CategoryID = ?`, + [id] + ); + res.json({ message: "Delete category successfully!" }); + } catch (error) { + res.json({ error: "Cannot remove category from database!" }); + } +}; diff --git a/backend/controllers/product.js b/backend/controllers/product.js index 569e4c6..a606fa2 100644 --- a/backend/controllers/product.js +++ b/backend/controllers/product.js @@ -6,7 +6,7 @@ exports.addProduct = async (req, res) => { try { const [result] = await db.execute( `INSERT INTO Product (Name, Price, StockQuantity, UserID, Description, CategoryID) VALUES (?, ?, ?, ?, ?, ?)`, - [name, price, qty, userID, description, category], + [name, price, qty, userID, description, category] ); const productID = result.insertId; @@ -15,7 +15,7 @@ exports.addProduct = async (req, res) => { db.execute(`INSERT INTO Image_URL (URL, ProductID) VALUES (?, ?)`, [ imagePath, productID, - ]), + ]) ); await Promise.all(imageInsertPromises); //perallel @@ -39,7 +39,7 @@ exports.addFavorite = async (req, res) => { // Use parameterized query to prevent SQL injection const [result] = await db.execute( `INSERT INTO Favorites (UserID, ProductID) VALUES (?, ?)`, - [userID, productID], + [userID, productID] ); res.json({ @@ -59,7 +59,7 @@ exports.removeFavorite = async (req, res) => { // Use parameterized query to prevent SQL injection const [result] = await db.execute( `DELETE FROM Favorites WHERE UserID = ? AND ProductID = ?`, - [userID, productID], + [userID, productID] ); res.json({ @@ -103,7 +103,7 @@ exports.getFavorites = async (req, res) => { p.Date, u.Name; `, - [userID], + [userID] ); res.json({ @@ -168,7 +168,7 @@ exports.getProductById = async (req, res) => { JOIN User U ON p.UserID = U.UserID WHERE p.ProductID = ? `, - [id], + [id] ); // Log raw data for debugging @@ -211,6 +211,65 @@ exports.getProductById = async (req, res) => { } }; +exports.getProductWithPagination = async (req, res) => { + const limit = +req.query.limit; + const page = +req.query.page; + + const offset = (page - 1) * limit; + + try { + const [data, fields] = await db.execute( + ` + SELECT + P.ProductID, + P.Name AS ProductName, + P.Price, + P.Date AS DateUploaded, + U.Name AS SellerName, + MIN(I.URL) AS ProductImage, + C.Name AS Category + FROM Product P + LEFT JOIN Image_URL I ON P.ProductID = I.ProductID + LEFT JOIN User U ON P.UserID = U.UserID + LEFT JOIN Category C ON P.CategoryID = C.CategoryID + GROUP BY + P.ProductID, + P.Name, + P.Price, + P.Date, + U.Name, + C.Name + ORDER BY P.ProductID ASC + LIMIT ? OFFSET ? + `, + [limit.toString(), offset.toString()] + ); + + const [result] = await db.execute( + `SELECT COUNT(*) AS totalProd FROM Product` + ); + const { totalProd } = result[0]; + + return res.json({ totalProd, products: data }); + } catch (error) { + res.json({ error: "Error fetching products!" }); + } +}; + +exports.removeProduct = async (req, res) => { + const { id } = req.params; + + try { + const [result] = await db.execute( + `DELETE FROM Product WHERE ProductID = ?`, + [id] + ); + res.json({ message: "Delete product successfully!" }); + } catch (error) { + res.json({ error: "Cannot remove product from database!" }); + } +}; + // db_con.query( // "SELECT ProductID FROM product WHERE ProductID = ?", // [productID], diff --git a/backend/controllers/user.js b/backend/controllers/user.js index 32aef17..37c5b06 100644 --- a/backend/controllers/user.js +++ b/backend/controllers/user.js @@ -13,13 +13,13 @@ exports.sendVerificationCode = async (req, res) => { // Generate a random 6-digit code const verificationCode = crypto.randomInt(100000, 999999).toString(); console.log( - `Generated verification code for ${email}: ${verificationCode}`, + `Generated verification code for ${email}: ${verificationCode}` ); // Check if email already exists in verification table const [results, fields] = await db.execute( "SELECT * FROM AuthVerification WHERE Email = ?", - [email], + [email] ); if (results.length > 0) { @@ -27,7 +27,7 @@ exports.sendVerificationCode = async (req, res) => { const [result] = await db.execute( `UPDATE AuthVerification SET VerificationCode = ?, Authenticated = FALSE, Date = CURRENT_TIMESTAMP WHERE Email = ?`, - [verificationCode, email], + [verificationCode, email] ); // Send email and respond @@ -37,7 +37,7 @@ exports.sendVerificationCode = async (req, res) => { // Insert new record const [result] = await db.execute( "INSERT INTO AuthVerification (Email, VerificationCode, Authenticated) VALUES (?, ?, FALSE)", - [email, verificationCode], + [email, verificationCode] ); // Send email and respond await sendVerificationEmail(email, verificationCode); @@ -62,7 +62,7 @@ exports.verifyCode = async (req, res) => { // Check verification code const [results, fields] = await db.execute( "SELECT * FROM AuthVerification WHERE Email = ? AND VerificationCode = ? AND Authenticated = 0 AND Date > DATE_SUB(NOW(), INTERVAL 15 MINUTE)", - [email, code], + [email, code] ); if (results.length === 0) { console.log(`Invalid or expired verification code for email ${email}`); @@ -76,7 +76,7 @@ exports.verifyCode = async (req, res) => { // Mark as authenticated const [result] = await db.execute( "UPDATE AuthVerification SET Authenticated = TRUE WHERE Email = ?", - [email], + [email] ); res.json({ success: true, @@ -95,7 +95,7 @@ exports.completeSignUp = async (req, res) => { try { const [results, fields] = await db.execute( `SELECT * FROM AuthVerification WHERE Email = ? AND Authenticated = 1;`, - [data.email], + [data.email] ); if (results.length === 0) { @@ -105,20 +105,20 @@ exports.completeSignUp = async (req, res) => { // Create the user const [createResult] = await db.execute( `INSERT INTO User (Name, Email, UCID, Password, Phone, Address) - VALUES ('${data.name}', '${data.email}', '${data.UCID}', '${data.password}', '${data.phone}', '${data.address}')`, + VALUES ('${data.name}', '${data.email}', '${data.UCID}', '${data.password}', '${data.phone}', '${data.address}')` ); // Insert role using the user's ID const [insertResult] = await db.execute( `INSERT INTO UserRole (UserID, Client, Admin) VALUES (LAST_INSERT_ID(), ${data.client || true}, ${ - data.admin || false - })`, + data.admin || false + })` ); // Delete verification record const [deleteResult] = await db.execute( - `DELETE FROM AuthVerification WHERE Email = '${data.email}'`, + `DELETE FROM AuthVerification WHERE Email = '${data.email}'` ); res.json({ @@ -310,7 +310,7 @@ exports.deleteUser = async (req, res) => { // Delete from UserRole first (assuming foreign key constraint) const [result1] = await db.execute( "DELETE FROM UserRole WHERE UserID = ?", - [userId], + [userId] ); // Then delete from User table @@ -328,3 +328,38 @@ exports.deleteUser = async (req, res) => { return res.status(500).json({ error: "Could not delete user!" }); } }; + +exports.getUsersWithPagination = async (req, res) => { + const limit = +req.query.limit; + const page = +req.query.page; + + const offset = (page - 1) * limit; + try { + const [users, fields] = await db.execute( + "SELECT * FROM User LIMIT ? OFFSET ?", + [limit.toString(), offset.toString()] + ); + + const [result] = await db.execute("SELECT COUNT(*) AS count FROM User"); + const { count: total } = result[0]; + + res.json({ users, total }); + } catch (error) { + console.error("Errors: ", error); + return res.status(500).json({ error: "\nCould not fetch users!" }); + } +}; + +exports.isAdmin = async (req, res) => { + const { id } = req.params; + try { + const [result] = await db.execute( + "SELECT R.Admin FROM marketplace.userrole R WHERE R.UserID = ?", + [id] + ); + const { Admin } = result[0]; + res.json({ isAdmin: Admin }); + } catch (error) { + res.json({ error: "Cannot verify admin status!" }); + } +}; diff --git a/backend/index.js b/backend/index.js index 7afa3fe..5ba3b27 100644 --- a/backend/index.js +++ b/backend/index.js @@ -9,6 +9,7 @@ const searchRouter = require("./routes/search"); const recommendedRouter = require("./routes/recommendation"); const history = require("./routes/history"); const review = require("./routes/review"); +const categoryRouter = require("./routes/category"); const { generateEmailTransporter } = require("./utils/mail"); const { @@ -42,10 +43,10 @@ app.use("/api/search", searchRouter); app.use("/api/engine", recommendedRouter); app.use("/api/history", history); app.use("/api/review", review); - +app.use("/api/category", categoryRouter); // Set up a scheduler to run cleanup every hour -clean_up_time = 30*60*1000; +clean_up_time = 30 * 60 * 1000; setInterval(cleanupExpiredCodes, clean_up_time); app.listen(3030, () => { diff --git a/backend/routes/category.js b/backend/routes/category.js new file mode 100644 index 0000000..9a04a08 --- /dev/null +++ b/backend/routes/category.js @@ -0,0 +1,14 @@ +const express = require("express"); +const { + getAllCategoriesWithPagination, + addCategory, + removeCategory, +} = require("../controllers/category"); + +const router = express.Router(); + +router.get("/getCategories", getAllCategoriesWithPagination); +router.post("/addCategory", addCategory); +router.delete("/:id", removeCategory); + +module.exports = router; diff --git a/backend/routes/product.js b/backend/routes/product.js index 944e63b..4b59e25 100644 --- a/backend/routes/product.js +++ b/backend/routes/product.js @@ -7,6 +7,8 @@ const { getAllProducts, getProductById, addProduct, + removeProduct, + getProductWithPagination, } = require("../controllers/product"); const router = express.Router(); @@ -22,6 +24,12 @@ router.post("/delFavorite", removeFavorite); router.post("/addProduct", addProduct); router.get("/getProduct", getAllProducts); + +//Remove product +router.delete("/:id", removeProduct); +//Get products with pagination +router.get("/getProductWithPagination", getProductWithPagination); + router.get("/:id", getProductById); // Simplified route module.exports = router; diff --git a/backend/routes/user.js b/backend/routes/user.js index 1ccbc88..657b793 100644 --- a/backend/routes/user.js +++ b/backend/routes/user.js @@ -8,6 +8,8 @@ const { updateUser, deleteUser, doLogin, + isAdmin, + getUsersWithPagination, } = require("../controllers/user"); const router = express.Router(); @@ -36,4 +38,10 @@ router.post("/update", updateUser); //Delete A uses Data: router.post("/delete", deleteUser); +//Check admin status +router.get("/isAdmin/:id", isAdmin); + +//Fetch user with pagination +router.get("/getUserWithPagination", getUsersWithPagination); + module.exports = router; diff --git a/backend/utils/database.js b/backend/utils/database.js index 6e75c3a..643b1f9 100644 --- a/backend/utils/database.js +++ b/backend/utils/database.js @@ -4,6 +4,7 @@ const pool = mysql.createPool({ host: "localhost", user: "root", database: "Marketplace", + password: "12345678", }); module.exports = pool.promise();