Finish admin dashboard and update sql code
This commit is contained in:
47
backend/controllers/category.js
Normal file
47
backend/controllers/category.js
Normal file
@@ -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!" });
|
||||
}
|
||||
};
|
||||
90
backend/controllers/history.js
Normal file
90
backend/controllers/history.js
Normal file
@@ -0,0 +1,90 @@
|
||||
const db = require("../utils/database");
|
||||
|
||||
exports.HistoryByUserId = async (req, res) => {
|
||||
const { id } = req.body;
|
||||
try {
|
||||
const [data] = await db.execute(
|
||||
`
|
||||
WITH RankedImages AS (
|
||||
SELECT
|
||||
P.ProductID,
|
||||
P.Name AS ProductName,
|
||||
P.Price,
|
||||
P.Date AS DateUploaded,
|
||||
U.Name AS SellerName,
|
||||
I.URL AS ProductImage,
|
||||
C.Name AS Category,
|
||||
ROW_NUMBER() OVER (PARTITION BY P.ProductID ORDER BY I.URL) AS RowNum
|
||||
FROM Product P
|
||||
JOIN Image_URL I ON P.ProductID = I.ProductID
|
||||
JOIN User U ON P.UserID = U.UserID
|
||||
JOIN Category C ON P.CategoryID = C.CategoryID
|
||||
JOIN History H ON H.ProductID = P.ProductID
|
||||
WHERE H.UserID = ?
|
||||
)
|
||||
SELECT
|
||||
ProductID,
|
||||
ProductName,
|
||||
Price,
|
||||
DateUploaded,
|
||||
SellerName,
|
||||
ProductImage,
|
||||
Category
|
||||
FROM RankedImages
|
||||
WHERE RowNum = 1;
|
||||
`,
|
||||
[id],
|
||||
);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: "Products fetched successfully",
|
||||
data,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Error finding products:", error);
|
||||
return res.status(500).json({
|
||||
found: false,
|
||||
error: "Database error occurred",
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
exports.AddHistory = async (req, res) => {
|
||||
const { userID, productID } = req.body;
|
||||
console.log(userID);
|
||||
try {
|
||||
// Use parameterized query to prevent SQL injection
|
||||
const [result] = await db.execute(
|
||||
`INSERT INTO History (UserID, ProductID) VALUES (?, ?)`,
|
||||
[userID, productID],
|
||||
);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: "Product added to history successfully",
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Error adding favorite product:", error);
|
||||
return res.json({ error: "Could not add favorite product" });
|
||||
}
|
||||
};
|
||||
|
||||
exports.DelHistory = async (req, res) => {
|
||||
const { userID, productID } = req.body;
|
||||
console.log(userID);
|
||||
try {
|
||||
// Use parameterized query to prevent SQL injection
|
||||
const [result] = await db.execute(`DELETE FROM History WHERE UserID=?`, [
|
||||
userID,
|
||||
]);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: "Product deleted from History successfully",
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Error adding favorite product:", error);
|
||||
return res.json({ error: "Could not add favorite product" });
|
||||
}
|
||||
};
|
||||
269
backend/controllers/product.js
Normal file
269
backend/controllers/product.js
Normal file
@@ -0,0 +1,269 @@
|
||||
const db = require("../utils/database");
|
||||
|
||||
exports.addFavorite = async (req, res) => {
|
||||
const { userID, productID } = req.body;
|
||||
console.log(userID);
|
||||
try {
|
||||
// Use parameterized query to prevent SQL injection
|
||||
const [result] = await db.execute(
|
||||
`INSERT INTO Favorites (UserID, ProductID) VALUES (?, ?)`,
|
||||
[userID, productID]
|
||||
);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: "Product added to favorites successfully",
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Error adding favorite product:", error);
|
||||
return res.json({ error: "Could not add favorite product" });
|
||||
}
|
||||
};
|
||||
|
||||
exports.removeFavorite = async (req, res) => {
|
||||
const { userID, productID } = req.body;
|
||||
console.log(userID);
|
||||
try {
|
||||
// Use parameterized query to prevent SQL injection
|
||||
const [result] = await db.execute(
|
||||
`DELETE FROM Favorites WHERE UserID = ? AND ProductID = ?`,
|
||||
[userID, productID]
|
||||
);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: "Product removed from favorites successfully",
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Error removing favorite product:", error);
|
||||
return res.json({ error: "Could not remove favorite product" });
|
||||
}
|
||||
};
|
||||
|
||||
exports.getFavorites = async (req, res) => {
|
||||
const { userID } = req.body;
|
||||
|
||||
try {
|
||||
const [favorites] = await db.execute(
|
||||
`
|
||||
SELECT
|
||||
p.ProductID,
|
||||
p.Name,
|
||||
p.Description,
|
||||
p.Price,
|
||||
p.CategoryID,
|
||||
p.UserID,
|
||||
p.Date,
|
||||
u.Name AS SellerName,
|
||||
MIN(i.URL) AS image_url
|
||||
FROM Favorites f
|
||||
JOIN Product p ON f.ProductID = p.ProductID
|
||||
JOIN User u ON p.UserID = u.UserID
|
||||
LEFT JOIN Image_URL i ON p.ProductID = i.ProductID
|
||||
WHERE f.UserID = ?
|
||||
GROUP BY
|
||||
p.ProductID,
|
||||
p.Name,
|
||||
p.Description,
|
||||
p.Price,
|
||||
p.CategoryID,
|
||||
p.UserID,
|
||||
p.Date,
|
||||
u.Name;
|
||||
`,
|
||||
[userID]
|
||||
);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
favorites: favorites,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Error retrieving favorites:", error);
|
||||
res.status(500).json({ error: "Could not retrieve favorite products" });
|
||||
}
|
||||
};
|
||||
|
||||
// Get all products along with their image URLs
|
||||
exports.getAllProducts = async (req, res) => {
|
||||
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
|
||||
JOIN Image_URL I ON P.ProductID = I.ProductID
|
||||
JOIN User U ON P.UserID = U.UserID
|
||||
JOIN Category C ON P.CategoryID = C.CategoryID
|
||||
GROUP BY
|
||||
P.ProductID,
|
||||
P.Name,
|
||||
P.Price,
|
||||
P.Date,
|
||||
U.Name,
|
||||
C.Name;
|
||||
`);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: "Products fetched successfully",
|
||||
data,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Error finding products:", error);
|
||||
return res.status(500).json({
|
||||
found: false,
|
||||
error: "Database error occurred",
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
exports.getProductById = async (req, res) => {
|
||||
const { id } = req.params;
|
||||
console.log("Received Product ID:", id);
|
||||
|
||||
try {
|
||||
const [data] = await db.execute(
|
||||
`
|
||||
SELECT p.*,U.Name AS SellerName,U.Email as SellerEmail,U.Phone as SellerPhone, i.URL AS image_url
|
||||
FROM Product p
|
||||
LEFT JOIN Image_URL i ON p.ProductID = i.ProductID
|
||||
JOIN User U ON p.UserID = U.UserID
|
||||
WHERE p.ProductID = ?
|
||||
`,
|
||||
[id]
|
||||
);
|
||||
|
||||
// Log raw data for debugging
|
||||
console.log("Raw Database Result:", data);
|
||||
|
||||
if (data.length === 0) {
|
||||
console.log("No product found with ID:", id);
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: "Product not found",
|
||||
});
|
||||
}
|
||||
|
||||
// Collect all image URLs
|
||||
const images = data
|
||||
.map((row) => row.image_url)
|
||||
.filter((url) => url !== null);
|
||||
|
||||
// Create product object with all details from first row and collected images
|
||||
const product = {
|
||||
...data[0], // Base product details
|
||||
images: images, // Collected image URLs
|
||||
};
|
||||
|
||||
// Log processed product for debugging
|
||||
console.log("Processed Product:", product);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: "Product fetched successfully",
|
||||
data: product,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Full Error Details:", error);
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
message: "Database error occurred",
|
||||
error: error.message,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
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],
|
||||
// (err, results) => {
|
||||
// if (err) {
|
||||
// console.error("Error checking product:", err);
|
||||
// return res.json({ error: "Database error" });
|
||||
// }
|
||||
|
||||
// if (results.length === 0) {
|
||||
// return res.json({ error: "Product does not exist" });
|
||||
// }
|
||||
// },
|
||||
// );
|
||||
|
||||
// db_con.query(
|
||||
// "INSERT INTO Favorites (UserID, ProductID) VALUES (?, ?)",
|
||||
// [userID, productID],
|
||||
// (err, result) => {
|
||||
// if (err) {
|
||||
// console.error("Error adding favorite product:", err);
|
||||
// return res.json({ error: "Could not add favorite product" });
|
||||
// }
|
||||
// res.json({
|
||||
// success: true,
|
||||
// message: "Product added to favorites successfully",
|
||||
// });
|
||||
// },
|
||||
// );
|
||||
53
backend/controllers/recommendation.js
Normal file
53
backend/controllers/recommendation.js
Normal file
@@ -0,0 +1,53 @@
|
||||
const db = require("../utils/database");
|
||||
|
||||
// TODO: Get the recommondaed product given the userID
|
||||
exports.RecommondationByUserId = async (req, res) => {
|
||||
const { id } = req.body;
|
||||
try {
|
||||
const [data, fields] = await db.execute(
|
||||
`
|
||||
WITH RankedImages AS (
|
||||
SELECT
|
||||
P.ProductID,
|
||||
P.Name AS ProductName,
|
||||
P.Price,
|
||||
P.Date AS DateUploaded,
|
||||
U.Name AS SellerName,
|
||||
I.URL AS ProductImage,
|
||||
C.Name AS Category,
|
||||
ROW_NUMBER() OVER (PARTITION BY P.ProductID ORDER BY I.URL) AS RowNum
|
||||
FROM Product P
|
||||
JOIN Image_URL I ON P.ProductID = I.ProductID
|
||||
JOIN User U ON P.UserID = U.UserID
|
||||
JOIN Category C ON P.CategoryID = C.CategoryID
|
||||
JOIN Recommendation R ON P.ProductID = R.RecommendedProductID
|
||||
WHERE R.UserID = ?
|
||||
)
|
||||
SELECT
|
||||
ProductID,
|
||||
ProductName,
|
||||
Price,
|
||||
DateUploaded,
|
||||
SellerName,
|
||||
ProductImage,
|
||||
Category
|
||||
FROM RankedImages
|
||||
WHERE RowNum = 1;
|
||||
`,
|
||||
[id],
|
||||
);
|
||||
|
||||
console.log(data);
|
||||
res.json({
|
||||
success: true,
|
||||
message: "Products fetched successfully",
|
||||
data,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Error finding products:", error);
|
||||
return res.status(500).json({
|
||||
found: false,
|
||||
error: "Database error occurred",
|
||||
});
|
||||
}
|
||||
};
|
||||
302
backend/controllers/review.js
Normal file
302
backend/controllers/review.js
Normal file
@@ -0,0 +1,302 @@
|
||||
const db = require("../utils/database");
|
||||
|
||||
/**
|
||||
* Get reviews for a specific product
|
||||
* Returns both reviews for the product and reviews by the product owner for other products
|
||||
*/
|
||||
exports.getReviews = async (req, res) => {
|
||||
const { id } = req.params;
|
||||
console.log("Received Product ID:", id);
|
||||
|
||||
try {
|
||||
// First query: Get reviews for this specific product
|
||||
const [productReviews] = await db.execute(
|
||||
`SELECT
|
||||
R.ReviewID,
|
||||
R.UserID,
|
||||
R.ProductID,
|
||||
R.Comment,
|
||||
R.Rating,
|
||||
R.Date AS ReviewDate,
|
||||
U.Name AS ReviewerName,
|
||||
P.Name AS ProductName,
|
||||
'product' AS ReviewType
|
||||
FROM Review R
|
||||
JOIN User U ON R.UserID = U.UserID
|
||||
JOIN Product P ON R.ProductID = P.ProductID
|
||||
WHERE R.ProductID = ?`,
|
||||
[id],
|
||||
);
|
||||
|
||||
// // Second query: Get reviews written by the product owner for other products
|
||||
// const [sellerReviews] = await db.execute(
|
||||
// `SELECT
|
||||
// R.ReviewID,
|
||||
// R.UserID,
|
||||
// R.ProductID,
|
||||
// R.Comment,
|
||||
// R.Rating,
|
||||
// R.Date AS ReviewDate,
|
||||
// U.Name AS ReviewerName,
|
||||
// P.Name AS ProductName,
|
||||
// 'seller' AS ReviewType
|
||||
// FROM Review R
|
||||
// JOIN User U ON R.UserID = U.UserID
|
||||
// JOIN Product P ON R.ProductID = P.ProductID
|
||||
// WHERE R.UserID = (
|
||||
// SELECT UserID
|
||||
// FROM Product
|
||||
// WHERE ProductID = ?
|
||||
// )
|
||||
// AND R.ProductID != ?`,
|
||||
// [id, id],
|
||||
// );
|
||||
|
||||
// Combine the results
|
||||
const combinedReviews = [...productReviews];
|
||||
|
||||
// Log data for debugging
|
||||
console.log("Combined Reviews:", combinedReviews);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: "Reviews fetched successfully",
|
||||
data: combinedReviews,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Full Error Details:", error);
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
message: "Database error occurred",
|
||||
error: error.message,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Submit a new review for a product
|
||||
*/
|
||||
exports.submitReview = async (req, res) => {
|
||||
const { productId, userId, rating, comment } = req.body;
|
||||
|
||||
// Validate required fields
|
||||
if (!productId || !userId || !rating || !comment) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: "Missing required fields",
|
||||
});
|
||||
}
|
||||
|
||||
// Validate rating is between 1 and 5
|
||||
if (rating < 1 || rating > 5) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: "Rating must be between 1 and 5",
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
// Check if user has already reviewed this product
|
||||
const [existingReview] = await db.execute(
|
||||
`SELECT ReviewID FROM Review WHERE ProductID = ? AND UserID = ?`,
|
||||
[productId, userId],
|
||||
);
|
||||
|
||||
if (existingReview.length > 0) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: "You have already reviewed this product",
|
||||
});
|
||||
}
|
||||
|
||||
// Check if user is trying to review their own product
|
||||
const [productOwner] = await db.execute(
|
||||
`SELECT UserID FROM Product WHERE ProductID = ?`,
|
||||
[productId],
|
||||
);
|
||||
|
||||
if (productOwner.length > 0 && productOwner[0].UserID === userId) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: "You cannot review your own product",
|
||||
});
|
||||
}
|
||||
|
||||
// Insert the review into the database
|
||||
const [result] = await db.execute(
|
||||
`INSERT INTO Review (
|
||||
ProductID,
|
||||
UserID,
|
||||
Rating,
|
||||
Comment,
|
||||
Date
|
||||
) VALUES (?, ?, ?, ?, NOW())`,
|
||||
[productId, userId, rating, comment],
|
||||
);
|
||||
|
||||
// Get the inserted review id
|
||||
const reviewId = result.insertId;
|
||||
|
||||
// Fetch the newly created review to return to client
|
||||
const [newReview] = await db.execute(
|
||||
`SELECT
|
||||
R.ReviewID,
|
||||
R.ProductID,
|
||||
R.UserID,
|
||||
R.Rating,
|
||||
R.Comment,
|
||||
R.Date AS ReviewDate,
|
||||
U.Name AS ReviewerName,
|
||||
P.Name AS ProductName
|
||||
FROM Review R
|
||||
JOIN User U ON R.UserID = U.UserID
|
||||
JOIN Product P ON R.ProductID = P.ProductID
|
||||
WHERE R.ReviewID = ?`,
|
||||
[reviewId],
|
||||
);
|
||||
|
||||
res.status(201).json({
|
||||
success: true, // Fixed from false to true
|
||||
message: "Review submitted successfully",
|
||||
data: newReview[0],
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Error submitting review:", error);
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
message: "Database error occurred",
|
||||
error: error.message,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// /**
|
||||
// * Update an existing review
|
||||
// */
|
||||
// exports.updateReview = async (req, res) => {
|
||||
// const { reviewId } = req.params;
|
||||
// const { rating, comment } = req.body;
|
||||
// const userId = req.body.userId; // Assuming you have middleware that validates the user
|
||||
|
||||
// // Validate required fields
|
||||
// if (!reviewId || !rating || !comment) {
|
||||
// return res.status(400).json({
|
||||
// success: false,
|
||||
// message: "Missing required fields",
|
||||
// });
|
||||
// }
|
||||
|
||||
// // Validate rating is between 1 and 5
|
||||
// if (rating < 1 || rating > 5) {
|
||||
// return res.status(400).json({
|
||||
// success: false,
|
||||
// message: "Rating must be between 1 and 5",
|
||||
// });
|
||||
// }
|
||||
|
||||
// try {
|
||||
// // Check if review exists and belongs to the user
|
||||
// const [existingReview] = await db.execute(
|
||||
// `SELECT ReviewID, UserID FROM Review WHERE ReviewID = ?`,
|
||||
// [reviewId],
|
||||
// );
|
||||
|
||||
// if (existingReview.length === 0) {
|
||||
// return res.status(404).json({
|
||||
// success: false,
|
||||
// message: "Review not found",
|
||||
// });
|
||||
// }
|
||||
|
||||
// if (existingReview[0].UserID !== userId) {
|
||||
// return res.status(403).json({
|
||||
// success: false,
|
||||
// message: "You can only update your own reviews",
|
||||
// });
|
||||
// }
|
||||
|
||||
// // Update the review
|
||||
// await db.execute(
|
||||
// `UPDATE Review
|
||||
// SET Rating = ?, Comment = ?, Date = NOW()
|
||||
// WHERE ReviewID = ?`,
|
||||
// [rating, comment, reviewId],
|
||||
// );
|
||||
|
||||
// // Fetch the updated review
|
||||
// const [updatedReview] = await db.execute(
|
||||
// `SELECT
|
||||
// R.ReviewID,
|
||||
// R.ProductID,
|
||||
// R.UserID,
|
||||
// R.Rating,
|
||||
// R.Comment,
|
||||
// R.Date AS ReviewDate,
|
||||
// U.Name AS ReviewerName,
|
||||
// P.Name AS ProductName
|
||||
// FROM Review R
|
||||
// JOIN User U ON R.UserID = U.UserID
|
||||
// JOIN Product P ON R.ProductID = P.ProductID
|
||||
// WHERE R.ReviewID = ?`,
|
||||
// [reviewId],
|
||||
// );
|
||||
|
||||
// res.json({
|
||||
// success: true,
|
||||
// message: "Review updated successfully",
|
||||
// data: updatedReview[0],
|
||||
// });
|
||||
// } catch (error) {
|
||||
// console.error("Error updating review:", error);
|
||||
// return res.status(500).json({
|
||||
// success: false,
|
||||
// message: "Database error occurred",
|
||||
// error: error.message,
|
||||
// });
|
||||
// }
|
||||
// };
|
||||
|
||||
// /**
|
||||
// * Delete a review
|
||||
// */
|
||||
// exports.deleteReview = async (req, res) => {
|
||||
// const { reviewId } = req.params;
|
||||
// const userId = req.body.userId; // Assuming you have middleware that validates the user
|
||||
|
||||
// try {
|
||||
// // Check if review exists and belongs to the user
|
||||
// const [existingReview] = await db.execute(
|
||||
// `SELECT ReviewID, UserID FROM Review WHERE ReviewID = ?`,
|
||||
// [reviewId],
|
||||
// );
|
||||
|
||||
// if (existingReview.length === 0) {
|
||||
// return res.status(404).json({
|
||||
// success: false,
|
||||
// message: "Review not found",
|
||||
// });
|
||||
// }
|
||||
|
||||
// if (existingReview[0].UserID !== userId) {
|
||||
// return res.status(403).json({
|
||||
// success: false,
|
||||
// message: "You can only delete your own reviews",
|
||||
// });
|
||||
// }
|
||||
|
||||
// // Delete the review
|
||||
// await db.execute(`DELETE FROM Review WHERE ReviewID = ?`, [reviewId]);
|
||||
|
||||
// res.json({
|
||||
// success: true,
|
||||
// message: "Review deleted successfully",
|
||||
// });
|
||||
// } catch (error) {
|
||||
// console.error("Error deleting review:", error);
|
||||
// return res.status(500).json({
|
||||
// success: false,
|
||||
// message: "Database error occurred",
|
||||
// error: error.message,
|
||||
// });
|
||||
// }
|
||||
// };
|
||||
164
backend/controllers/search.js
Normal file
164
backend/controllers/search.js
Normal file
@@ -0,0 +1,164 @@
|
||||
const db = require("../utils/database");
|
||||
|
||||
exports.searchProductsByName = async (req, res) => {
|
||||
const { name } = req.query;
|
||||
|
||||
if (name.length === 0) {
|
||||
console.log("Searching for products with no name", name);
|
||||
}
|
||||
|
||||
console.log("Searching for products with name:", name);
|
||||
|
||||
try {
|
||||
// Modify SQL to return all products when no search term is provided
|
||||
const sql = `
|
||||
SELECT p.*, i.URL as image
|
||||
FROM Product p
|
||||
LEFT JOIN Image_URL i ON p.ProductID = i.ProductID
|
||||
${name ? "WHERE p.Name LIKE ?" : ""}
|
||||
ORDER BY p.ProductID
|
||||
`;
|
||||
|
||||
const params = name ? [`%${name}%`] : [];
|
||||
console.log("Executing SQL:", sql);
|
||||
console.log("With parameters:", params);
|
||||
|
||||
const [data] = await db.execute(sql, params);
|
||||
|
||||
console.log("Raw Database Result:", data);
|
||||
|
||||
if (data.length === 0) {
|
||||
console.log("No products found matching:", name);
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: "No products found matching your search",
|
||||
});
|
||||
}
|
||||
|
||||
// Group products by ProductID to handle multiple images per product
|
||||
const productsMap = new Map();
|
||||
|
||||
data.forEach((row) => {
|
||||
if (!productsMap.has(row.ProductID)) {
|
||||
const product = {
|
||||
ProductID: row.ProductID,
|
||||
Name: row.Name,
|
||||
Description: row.Description,
|
||||
Price: row.Price,
|
||||
images: row.image,
|
||||
};
|
||||
productsMap.set(row.ProductID, product);
|
||||
} else if (row.image_url) {
|
||||
productsMap.get(row.ProductID).images.push(row.image_url);
|
||||
}
|
||||
});
|
||||
|
||||
const products = Array.from(productsMap.values());
|
||||
|
||||
console.log("Processed Products:", products);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: "Products fetched successfully",
|
||||
data: products,
|
||||
count: products.length,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Database Error:", error);
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
message: "Database error occurred",
|
||||
error: error.message || "Unknown database error",
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// exports.searchProductsByName = async (req, res) => {
|
||||
// const { name } = req.query;
|
||||
|
||||
// // Add better validation and error handling
|
||||
// if (!name || typeof name !== "string") {
|
||||
// return res.status(400).json({
|
||||
// success: false,
|
||||
// message: "Valid search term is required",
|
||||
// });
|
||||
// }
|
||||
|
||||
// console.log("Searching for products with name:", name);
|
||||
|
||||
// try {
|
||||
// // Log the SQL query and parameters for debugging
|
||||
// const sql = `
|
||||
// SELECT p.*, i.URL AS image_url
|
||||
// FROM Product p
|
||||
// LEFT JOIN Image_URL i ON p.ProductID = i.ProductID
|
||||
// WHERE p.Name LIKE ?
|
||||
// `;
|
||||
// const params = [`%${name}%`];
|
||||
// console.log("Executing SQL:", sql);
|
||||
// console.log("With parameters:", params);
|
||||
|
||||
// const [data] = await db.execute(sql, params);
|
||||
|
||||
// // Log raw data for debugging
|
||||
// console.log("Raw Database Result:", data);
|
||||
|
||||
// if (data.length === 0) {
|
||||
// console.log("No products found matching:", name);
|
||||
// return res.status(404).json({
|
||||
// success: false,
|
||||
// message: "No products found matching your search",
|
||||
// });
|
||||
// }
|
||||
|
||||
// // Group products by ProductID to handle multiple images per product
|
||||
// const productsMap = new Map();
|
||||
|
||||
// data.forEach((row) => {
|
||||
// if (!productsMap.has(row.ProductID)) {
|
||||
// // Create a clean object without circular references
|
||||
// const product = {
|
||||
// ProductID: row.ProductID,
|
||||
// Name: row.Name,
|
||||
// Description: row.Description,
|
||||
// Price: row.Price,
|
||||
// // Add any other product fields you need
|
||||
// images: row.image_url ? [row.image_url] : [],
|
||||
// };
|
||||
// productsMap.set(row.ProductID, product);
|
||||
// } else if (row.image_url) {
|
||||
// // Add additional image to existing product
|
||||
// productsMap.get(row.ProductID).images.push(row.image_url);
|
||||
// }
|
||||
// });
|
||||
|
||||
// // Convert map to array of products
|
||||
// const products = Array.from(productsMap.values());
|
||||
|
||||
// // Log processed products for debugging
|
||||
// console.log("Processed Products:", products);
|
||||
|
||||
// res.json({
|
||||
// success: true,
|
||||
// message: "Products fetched successfully",
|
||||
// data: products,
|
||||
// count: products.length,
|
||||
// });
|
||||
// } catch (error) {
|
||||
// // Enhanced error logging
|
||||
// console.error("Database Error Details:", {
|
||||
// message: error.message,
|
||||
// code: error.code,
|
||||
// errno: error.errno,
|
||||
// sqlState: error.sqlState,
|
||||
// sqlMessage: error.sqlMessage,
|
||||
// sql: error.sql,
|
||||
// });
|
||||
|
||||
// return res.status(500).json({
|
||||
// success: false,
|
||||
// message: "Database error occurred",
|
||||
// error: error.message || "Unknown database error",
|
||||
// });
|
||||
// }
|
||||
// };
|
||||
308
backend/controllers/user.js
Normal file
308
backend/controllers/user.js
Normal file
@@ -0,0 +1,308 @@
|
||||
const crypto = require("crypto");
|
||||
const db = require("../utils/database");
|
||||
const { sendVerificationEmail } = require("../utils/helper");
|
||||
|
||||
exports.sendVerificationCode = async (req, res) => {
|
||||
const { email } = req.body;
|
||||
|
||||
if (!email) {
|
||||
return res.status(400).json({ error: "Email is required" });
|
||||
}
|
||||
|
||||
try {
|
||||
// Generate a random 6-digit code
|
||||
const verificationCode = crypto.randomInt(100000, 999999).toString();
|
||||
console.log(
|
||||
`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]
|
||||
);
|
||||
|
||||
if (results.length > 0) {
|
||||
// Update existing record
|
||||
const [result] = await db.execute(
|
||||
`UPDATE AuthVerification SET VerificationCode = ?, Authenticated = FALSE, Date = CURRENT_TIMESTAMP
|
||||
WHERE Email = ?`,
|
||||
[verificationCode, email]
|
||||
);
|
||||
|
||||
// Send email and respond
|
||||
await sendVerificationEmail(email, verificationCode);
|
||||
res.json({ success: true, message: "Verification code sent" });
|
||||
} else {
|
||||
// Insert new record
|
||||
const [result] = await db.execute(
|
||||
"INSERT INTO AuthVerification (Email, VerificationCode, Authenticated) VALUES (?, ?, FALSE)",
|
||||
[email, verificationCode]
|
||||
);
|
||||
// Send email and respond
|
||||
await sendVerificationEmail(email, verificationCode);
|
||||
res.json({ success: true, message: "Verification code sent" });
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error:", error);
|
||||
res.status(500).json({ error: "Server error" });
|
||||
}
|
||||
};
|
||||
|
||||
exports.verifyCode = async (req, res) => {
|
||||
const { email, code } = req.body;
|
||||
|
||||
if (!email || !code) {
|
||||
return res.status(400).json({ error: "Email and code are required" });
|
||||
}
|
||||
|
||||
console.log(`Attempting to verify code for ${email}: ${code}`);
|
||||
|
||||
try {
|
||||
// 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]
|
||||
);
|
||||
if (results.length === 0) {
|
||||
console.log(`Invalid or expired verification code for email ${email}`);
|
||||
return res
|
||||
.status(400)
|
||||
.json({ error: "Invalid or expired verification code" });
|
||||
}
|
||||
|
||||
const userId = results[0].UserID;
|
||||
|
||||
// Mark as authenticated
|
||||
const [result] = await db.execute(
|
||||
"UPDATE AuthVerification SET Authenticated = TRUE WHERE Email = ?",
|
||||
[email]
|
||||
);
|
||||
res.json({
|
||||
success: true,
|
||||
message: "Verification successful",
|
||||
userId,
|
||||
});
|
||||
} catch (error) {
|
||||
console.log("Error: ", error);
|
||||
res.status(500).json({ error: "Database error!" });
|
||||
}
|
||||
};
|
||||
|
||||
exports.completeSignUp = async (req, res) => {
|
||||
const data = req.body;
|
||||
|
||||
try {
|
||||
const [results, fields] = await db.execute(
|
||||
`SELECT * FROM AuthVerification WHERE Email = ? AND Authenticated = 1;`,
|
||||
[data.email]
|
||||
);
|
||||
|
||||
if (results.length === 0) {
|
||||
return res.status(400).json({ error: "Email not verified" });
|
||||
}
|
||||
|
||||
// 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}')`
|
||||
);
|
||||
|
||||
// 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
|
||||
})`
|
||||
);
|
||||
|
||||
// Delete verification record
|
||||
const [deleteResult] = await db.execute(
|
||||
`DELETE FROM AuthVerification WHERE Email = '${data.email}'`
|
||||
);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: "User registration completed successfully",
|
||||
name: data.name,
|
||||
email: data.email,
|
||||
UCID: data.UCID,
|
||||
});
|
||||
} catch (error) {
|
||||
console.log("Error: ", error);
|
||||
res.status(500).json({ error: "Database error!" });
|
||||
}
|
||||
};
|
||||
|
||||
exports.getAllUser = async (req, res) => {
|
||||
try {
|
||||
const [users, fields] = await db.execute("SELECT * FROM User;");
|
||||
res.json({ users });
|
||||
} catch (error) {
|
||||
console.error("Errors: ", error);
|
||||
return res.status(500).json({ error: "\nCould not fetch users!" });
|
||||
}
|
||||
};
|
||||
|
||||
exports.findUserByEmail = async (req, res) => {
|
||||
const { email } = req.body;
|
||||
|
||||
// Input validation
|
||||
if (!email) {
|
||||
return res.status(400).json({
|
||||
found: false,
|
||||
error: "Email is required",
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
// Query to find user with matching email and password
|
||||
const query = "SELECT * FROM User WHERE email = ?";
|
||||
const [data, fields] = await db.execute(query, [email]);
|
||||
|
||||
// Check if user was found
|
||||
if (data && data.length > 0) {
|
||||
console.log(data);
|
||||
const user = data[0];
|
||||
|
||||
// Return all user data except password
|
||||
return res.json({
|
||||
found: true,
|
||||
userID: user.UserID,
|
||||
name: user.Name,
|
||||
email: user.Email,
|
||||
UCID: user.UCID,
|
||||
phone: user.Phone,
|
||||
address: user.Address,
|
||||
// Include any other fields your user might have
|
||||
// Make sure the field names match exactly with your database column names
|
||||
});
|
||||
} else {
|
||||
// User not found or invalid credentials
|
||||
return res.json({
|
||||
found: false,
|
||||
error: "Invalid email or password",
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error finding user:", error);
|
||||
return res.status(500).json({
|
||||
found: false,
|
||||
error: "Database error occurred",
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
exports.updateUser = async (req, res) => {
|
||||
try {
|
||||
const userId = req.body?.userId;
|
||||
const name = req.body?.name;
|
||||
const email = req.body?.email;
|
||||
const phone = req.body?.phone;
|
||||
const UCID = req.body?.UCID;
|
||||
const address = req.body?.address;
|
||||
|
||||
if (!userId) {
|
||||
return res.status(400).json({ error: "User ID is required" });
|
||||
}
|
||||
|
||||
// Build updateData manually
|
||||
const updateData = {};
|
||||
if (name) updateData.name = name;
|
||||
if (email) updateData.email = email;
|
||||
if (phone) updateData.phone = phone;
|
||||
if (UCID) updateData.UCID = UCID;
|
||||
if (address) updateData.address = address;
|
||||
|
||||
if (Object.keys(updateData).length === 0) {
|
||||
return res.status(400).json({ error: "No valid fields to update" });
|
||||
}
|
||||
|
||||
const updateFields = [];
|
||||
const values = [];
|
||||
|
||||
Object.entries(updateData).forEach(([key, value]) => {
|
||||
updateFields.push(`${key} = ?`);
|
||||
values.push(value);
|
||||
});
|
||||
|
||||
values.push(userId);
|
||||
|
||||
const query = `UPDATE User SET ${updateFields.join(", ")} WHERE userId = ?`;
|
||||
const [updateResult] = await db.execute(query, values);
|
||||
|
||||
if (updateResult.affectedRows === 0) {
|
||||
return res.status(404).json({ error: "User not found" });
|
||||
}
|
||||
|
||||
res.json({ success: true, message: "User updated successfully" });
|
||||
} catch (error) {
|
||||
console.error("Error updating user:", error);
|
||||
return res.status(500).json({ error: "Could not update user" });
|
||||
}
|
||||
};
|
||||
|
||||
exports.deleteUser = async (req, res) => {
|
||||
const { userId } = req.body;
|
||||
|
||||
if (!userId) {
|
||||
return res.status(400).json({ error: "User ID is required" });
|
||||
}
|
||||
|
||||
try {
|
||||
// Delete from UserRole first (assuming foreign key constraint)
|
||||
const [result1] = await db.execute(
|
||||
"DELETE FROM UserRole WHERE UserID = ?",
|
||||
[userId]
|
||||
);
|
||||
|
||||
// Then delete from User table
|
||||
const [result2] = await db.execute("DELETE FROM User WHERE UserID = ?", [
|
||||
userId,
|
||||
]);
|
||||
|
||||
if (result2.affectedRows === 0) {
|
||||
return res.status(404).json({ error: "User not found" });
|
||||
}
|
||||
|
||||
res.json({ success: true, message: "User deleted successfully" });
|
||||
} catch (error) {
|
||||
console.error("Error: ", error);
|
||||
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!" });
|
||||
}
|
||||
};
|
||||
56
backend/index.js
Normal file
56
backend/index.js
Normal file
@@ -0,0 +1,56 @@
|
||||
const express = require("express");
|
||||
const cors = require("cors");
|
||||
|
||||
const db = require("./utils/database");
|
||||
|
||||
const userRouter = require("./routes/user");
|
||||
const productRouter = require("./routes/product");
|
||||
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 {
|
||||
// cleanupExpiredCodes,
|
||||
checkDatabaseConnection,
|
||||
} = require("./utils/helper");
|
||||
|
||||
const app = express();
|
||||
|
||||
app.use(cors());
|
||||
app.use(express.json());
|
||||
|
||||
// Configure email transporter for Zoho
|
||||
const transporter = generateEmailTransporter();
|
||||
// Test the email connection
|
||||
transporter
|
||||
.verify()
|
||||
.then(() => {
|
||||
console.log("Email connection successful!");
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("Email connection failed:", error);
|
||||
});
|
||||
|
||||
checkDatabaseConnection(db);
|
||||
|
||||
//Routes
|
||||
app.use("/api/user", userRouter);
|
||||
app.use("/api/product", productRouter);
|
||||
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;
|
||||
// setInterval(cleanupExpiredCodes, clean_up_time);
|
||||
|
||||
app.listen(3030, () => {
|
||||
console.log(`Running Backend on http://localhost:3030/`);
|
||||
console.log(`Send verification code: POST /send-verification`);
|
||||
console.log(`Verify code: POST /verify-code`);
|
||||
});
|
||||
1592
backend/package-lock.json
generated
Normal file
1592
backend/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
28
backend/package.json
Normal file
28
backend/package.json
Normal file
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"name": "server",
|
||||
"version": "1.0.0",
|
||||
"main": "index.js",
|
||||
"type": "commonjs",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"dev": "nodemon index.js"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"description": "",
|
||||
"dependencies": {
|
||||
"body-parser": "^1.20.3",
|
||||
"cors": "^2.8.5",
|
||||
"crypto": "^1.0.1",
|
||||
"dotenv": "^16.4.7",
|
||||
"express": "^4.21.2",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"mysql": "^2.18.1",
|
||||
"mysql2": "^3.12.0",
|
||||
"nodemailer": "^6.10.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"nodemon": "^3.1.9"
|
||||
}
|
||||
}
|
||||
14
backend/routes/category.js
Normal file
14
backend/routes/category.js
Normal file
@@ -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;
|
||||
14
backend/routes/history.js
Normal file
14
backend/routes/history.js
Normal file
@@ -0,0 +1,14 @@
|
||||
// routes/product.js
|
||||
const express = require("express");
|
||||
const {
|
||||
HistoryByUserId,
|
||||
DelHistory,
|
||||
AddHistory,
|
||||
} = require("../controllers/history");
|
||||
const router = express.Router();
|
||||
|
||||
router.post("/getHistory", HistoryByUserId);
|
||||
router.post("/delHistory", DelHistory);
|
||||
router.post("/addHistory", AddHistory);
|
||||
|
||||
module.exports = router;
|
||||
34
backend/routes/product.js
Normal file
34
backend/routes/product.js
Normal file
@@ -0,0 +1,34 @@
|
||||
// routes/product.js
|
||||
const express = require("express");
|
||||
const {
|
||||
addFavorite,
|
||||
getFavorites,
|
||||
removeFavorite,
|
||||
getAllProducts,
|
||||
getProductById,
|
||||
getProductWithPagination,
|
||||
removeProduct,
|
||||
} = require("../controllers/product");
|
||||
const router = express.Router();
|
||||
|
||||
// Add detailed logging middleware
|
||||
router.use((req, res, next) => {
|
||||
console.log(`Incoming ${req.method} request to ${req.path}`);
|
||||
next();
|
||||
});
|
||||
|
||||
router.post("/addFavorite", addFavorite);
|
||||
router.post("/getFavorites", getFavorites);
|
||||
router.post("/delFavorite", removeFavorite);
|
||||
|
||||
router.get("/getProduct", getAllProducts);
|
||||
|
||||
//Get products with pagination
|
||||
router.get("/getProductWithPagination", getProductWithPagination);
|
||||
|
||||
router.get("/:id", getProductById); // Simplified route
|
||||
|
||||
//Remove product
|
||||
router.delete("/:id", removeProduct);
|
||||
|
||||
module.exports = router;
|
||||
8
backend/routes/recommendation.js
Normal file
8
backend/routes/recommendation.js
Normal file
@@ -0,0 +1,8 @@
|
||||
// routes/product.js
|
||||
const express = require("express");
|
||||
const { RecommondationByUserId } = require("../controllers/recommendation");
|
||||
const router = express.Router();
|
||||
|
||||
router.post("/recommended", RecommondationByUserId);
|
||||
|
||||
module.exports = router;
|
||||
9
backend/routes/review.js
Normal file
9
backend/routes/review.js
Normal file
@@ -0,0 +1,9 @@
|
||||
// routes/product.js
|
||||
const express = require("express");
|
||||
const { getReviews, submitReview } = require("../controllers/review");
|
||||
const router = express.Router();
|
||||
|
||||
router.get("/:id", getReviews);
|
||||
router.post("/add", submitReview);
|
||||
|
||||
module.exports = router;
|
||||
14
backend/routes/search.js
Normal file
14
backend/routes/search.js
Normal file
@@ -0,0 +1,14 @@
|
||||
// routes/product.js
|
||||
const express = require("express");
|
||||
const { searchProductsByName } = require("../controllers/search");
|
||||
const router = express.Router();
|
||||
|
||||
// Add detailed logging middleware
|
||||
router.use((req, res, next) => {
|
||||
console.log(`Incoming ${req.method} request to ${req.path}`);
|
||||
next();
|
||||
});
|
||||
|
||||
router.get("/getProduct", searchProductsByName);
|
||||
|
||||
module.exports = router;
|
||||
42
backend/routes/user.js
Normal file
42
backend/routes/user.js
Normal file
@@ -0,0 +1,42 @@
|
||||
const express = require("express");
|
||||
const {
|
||||
sendVerificationCode,
|
||||
verifyCode,
|
||||
completeSignUp,
|
||||
getAllUser,
|
||||
findUserByEmail,
|
||||
updateUser,
|
||||
deleteUser,
|
||||
getUsersWithPagination,
|
||||
isAdmin,
|
||||
} = require("../controllers/user");
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
// Generate and send verification code for signup
|
||||
router.post("/send-verification", sendVerificationCode);
|
||||
|
||||
// Verify the code
|
||||
router.post("/verify-code", verifyCode);
|
||||
|
||||
// Create a users and Complete user registration after verification
|
||||
router.post("/complete-signup", completeSignUp);
|
||||
|
||||
//Fetch all users data:
|
||||
router.get("/fetch_all_users", getAllUser);
|
||||
|
||||
//Fetch user with pagination
|
||||
router.get("/getUserWithPagination", getUsersWithPagination);
|
||||
|
||||
//Fetch One user Data with all fields:
|
||||
router.post("/find_user", findUserByEmail);
|
||||
|
||||
//Update A uses Data:
|
||||
router.post("/update", updateUser);
|
||||
|
||||
//Delete A uses Data:
|
||||
router.post("/delete", deleteUser);
|
||||
|
||||
router.get("/isAdmin/:id", isAdmin);
|
||||
|
||||
module.exports = router;
|
||||
12
backend/utils/database.js
Normal file
12
backend/utils/database.js
Normal file
@@ -0,0 +1,12 @@
|
||||
const mysql = require("mysql2");
|
||||
|
||||
//Create a pool of connections to allow multiple query happen at the same time
|
||||
const pool = mysql.createPool({
|
||||
host: "localhost",
|
||||
user: "root",
|
||||
database: "Marketplace",
|
||||
password: "12345678",
|
||||
});
|
||||
|
||||
//Export a promise for promise-based query
|
||||
module.exports = pool.promise();
|
||||
49
backend/utils/helper.js
Normal file
49
backend/utils/helper.js
Normal file
@@ -0,0 +1,49 @@
|
||||
const { generateEmailTransporter } = require("./mail");
|
||||
const db = require("./database");
|
||||
|
||||
// Helper function to send email
|
||||
async function sendVerificationEmail(email, verificationCode) {
|
||||
const transporter = generateEmailTransporter();
|
||||
|
||||
// Send the email with Zoho
|
||||
await transporter.sendMail({
|
||||
from: "campusplug@zohomailcloud.ca",
|
||||
to: email,
|
||||
subject: "Campus Plug: Signup Verification Code",
|
||||
text: `Your verification code is: ${verificationCode}. This code will expire in 15 minutes.`,
|
||||
html: `<p>Your verification code is: <strong>${verificationCode}</strong></p><p>This code will expire in 15 minutes.</p>`,
|
||||
});
|
||||
|
||||
console.log(`Verification code sent to ${email}`);
|
||||
}
|
||||
|
||||
// Clean up expired verification codes (run this periodically)
|
||||
// function cleanupExpiredCodes() {
|
||||
// db.query(
|
||||
// "DELETE FROM AuthVerification WHERE Date < DATE_SUB(NOW(), INTERVAL 15 MINUTE) AND Authenticated = 0",
|
||||
// (err, result) => {
|
||||
// if (err) {
|
||||
// console.error("Error cleaning up expired codes:", err);
|
||||
// } else {
|
||||
// console.log(`Cleaned up ${result} expired verification codes`);
|
||||
// }
|
||||
// }
|
||||
// );
|
||||
// }
|
||||
|
||||
const checkDatabaseConnection = async (db) => {
|
||||
try {
|
||||
const connection = await db.getConnection();
|
||||
//If no error, release the connection
|
||||
console.log("Connect to database successfully!");
|
||||
connection.release();
|
||||
} catch (error) {
|
||||
console.log("Cannot connect to database: ", error.sqlMessage);
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
sendVerificationEmail,
|
||||
// cleanupExpiredCodes,
|
||||
checkDatabaseConnection,
|
||||
};
|
||||
12
backend/utils/mail.js
Normal file
12
backend/utils/mail.js
Normal file
@@ -0,0 +1,12 @@
|
||||
const nodemailer = require("nodemailer");
|
||||
|
||||
exports.generateEmailTransporter = () =>
|
||||
nodemailer.createTransport({
|
||||
host: "smtp.zohocloud.ca",
|
||||
secure: true,
|
||||
port: 465,
|
||||
auth: {
|
||||
user: "campusplug@zohomailcloud.ca", //Zoho email
|
||||
pass: "e0YRrNSeJZQd", //Zoho password
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user