diff --git a/SQL_code/Schema.sql b/SQL_code/Schema.sql index ed116e0..97b93c4 100644 --- a/SQL_code/Schema.sql +++ b/SQL_code/Schema.sql @@ -17,6 +17,12 @@ CREATE TABLE UserRole ( FOREIGN KEY (UserID) REFERENCES User (UserID) ON DELETE CASCADE ); +-- Category Entity (must be created before Product or else error) +CREATE TABLE Category ( + CategoryID INT PRIMARY KEY, + Name VARCHAR(255) NOT NULL +); + -- Product Entity CREATE TABLE Product ( ProductID INT PRIMARY KEY, @@ -35,11 +41,6 @@ CREATE TABLE Image_URL ( FOREIGN KEY (ProductID) REFERENCES Product (ProductID) ) --- Category Entity -CREATE TABLE Category ( - CategoryID INT PRIMARY KEY, - Name VARCHAR(255) NOT NULL -); -- Review Entity (Many-to-One with User, Many-to-One with Product) CREATE TABLE Review ( diff --git a/backend/controllers/product.js b/backend/controllers/product.js new file mode 100644 index 0000000..54f640e --- /dev/null +++ b/backend/controllers/product.js @@ -0,0 +1,70 @@ +const db = require("../utils/database"); + +exports.addToFavorite = async (req, res) => { + const { userID, productsID } = req.body; + + try { + // Use parameterized query to prevent SQL injection + const [result] = await db.execute( + "INSERT INTO Favorites (UserID, ProductID) VALUES (?, ?)", + [userID, productsID] + ); + + 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" }); + } +}; + +//Get all products +exports.getAllProducts = async (req, res) => { + try { + const [data, fields] = await db.execute("SELECT * FROM Product"); + + res.json({ + success: true, + message: "Product added to favorites successfully", + data, + }); + } catch (error) { + console.error("Error finding user:", error); + return res.status(500).json({ + found: false, + error: "Database error occurred", + }); + } +}; + +// 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", +// }); +// }, +// ); diff --git a/backend/controllers/user.js b/backend/controllers/user.js new file mode 100644 index 0000000..33e8a23 --- /dev/null +++ b/backend/controllers/user.js @@ -0,0 +1,263 @@ +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: 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) => { + const { userId, ...updateData } = req.body; + + if (!userId) { + return res.status(400).json({ error: "User ID is required" }); + } + + //query dynamically based on provided fields + const updateFields = []; + const values = []; + + Object.entries(updateData).forEach(([key, value]) => { + // Only include fields that are actually in the User table + if (["Name", "Email", "Password", "Phone", "UCID"].includes(key)) { + updateFields.push(`${key} = ?`); + values.push(value); + } + }); + + if (updateFields.length === 0) { + return res.status(400).json({ error: "No valid fields to update" }); + } + + // Add userId to values array + values.push(userId); + + try { + 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!" }); + } +}; diff --git a/backend/index.js b/backend/index.js index ecf858a..01fc880 100644 --- a/backend/index.js +++ b/backend/index.js @@ -1,41 +1,23 @@ -import express, { json } from "express"; -import cors from "cors"; -import mysql from "mysql2"; -import nodemailer from "nodemailer"; -import crypto from "crypto"; +const express = require("express"); +const cors = require("cors"); +//Get the db connection +const db = require("./utils/database"); + +const userRouter = require("./routes/user"); +const productRouter = require("./routes/product"); +const { generateEmailTransporter } = require("./utils/mail"); +const { + cleanupExpiredCodes, + checkDatabaseConnection, +} = require("./utils/helper"); const app = express(); + app.use(cors()); app.use(express.json()); -//TODO: Connect with the database: -const db_con = mysql.createConnection({ - host: "localhost", - user: "root", - password: "12345678", - database: "marketplace", - enableKeepAlive: true, // false by default. -}); - -db_con.connect((err) => { - if (err) { - console.error("Database connection failed: " + err.stack); - return; - } - console.log("Connected to MySQL database."); -}); - // Configure email transporter for Zoho -const transporter = nodemailer.createTransport({ - host: "smtp.zohocloud.ca", - secure: true, - port: 465, - auth: { - user: "campusplug@zohomailcloud.ca", //Zoho email - pass: "e0YRrNSeJZQd", //Zoho password - }, -}); - +const transporter = generateEmailTransporter(); // Test the email connection transporter .verify() @@ -45,420 +27,16 @@ transporter .catch((error) => { console.error("Email connection failed:", error); }); -// Generate and send verification code for signup -app.post("/send-verification", async (req, res) => { - const { email } = req.body; - if (!email) { - return res.status(400).json({ error: "Email is required" }); - } +//Check database connection +checkDatabaseConnection(db); - 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 - db_con.query( - "SELECT * FROM AuthVerification WHERE Email = ?", - [email], - async (err, results) => { - if (err) { - console.error("Database error:", err); - return res.status(500).json({ error: "Database error" }); - } - - if (results.length > 0) { - // Update existing record - db_con.query( - `UPDATE AuthVerification SET VerificationCode = ?, Authenticated = FALSE, Date = CURRENT_TIMESTAMP - WHERE Email = ?`, - [verificationCode, email], - async (err) => { - if (err) { - console.error("Database error:", err); - return res.status(500).json({ error: "Database error" }); - } - - // Send email and respond - await sendVerificationEmail(email, verificationCode); - res.json({ success: true, message: "Verification code sent" }); - }, - ); - } else { - // Insert new record - db_con.query( - "INSERT INTO AuthVerification (Email, VerificationCode, Authenticated) VALUES (?, ?, FALSE)", - [email, verificationCode], - async (err) => { - if (err) { - console.error("Database error:", err); - return res.status(500).json({ error: "Database error" }); - } - - // 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" }); - } -}); - -// Helper function to send email -async function sendVerificationEmail(email, verificationCode) { - // 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: `

Your verification code is: ${verificationCode}

This code will expire in 15 minutes.

`, - }); - - console.log(`Verification code sent to ${email}`); -} - -// Verify the code -app.post("/verify-code", (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}`); - - // Check verification code - db_con.query( - "SELECT * FROM AuthVerification WHERE Email = ? AND VerificationCode = ? AND Authenticated = 0 AND Date > DATE_SUB(NOW(), INTERVAL 15 MINUTE)", - [email, code], - (err, results) => { - if (err) { - console.error("Database error:", err); - return res.status(500).json({ error: "Database error" }); - } - - 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 - db_con.query( - "UPDATE AuthVerification SET Authenticated = TRUE WHERE Email = ?", - [email], - (err) => { - if (err) { - console.error("Database error:", err); - return res.status(500).json({ error: "Database error" }); - } - - console.log(`Email ${email} successfully verified`); - - res.json({ - success: true, - message: "Verification successful", - userId, - }); - }, - ); - }, - ); -}); - -// Create a users and Complete user registration after verification -app.post("/complete-signup", (req, res) => { - const data = req.body; - - db_con.query( - `SELECT * FROM AuthVerification WHERE Email = '${data.email}' AND Authenticated = 1;`, - (err, results) => { - if (err) { - console.error("Database error:", err); - return res.status(500).json({ error: "Database error" }); - } - if (results.length === 0) { - return res.status(400).json({ error: "Email not verified" }); - } - - // Create the user - db_con.query( - `INSERT INTO User (Name, Email, UCID, Password, Phone, Address) - VALUES ('${data.name}', '${data.email}', '${data.UCID}', '${data.password}', '${data.phone}', '${data.address}')`, - (err, result) => { - if (err) { - console.error("Error creating user:", err); - return res.status(500).json({ error: "Could not create user" }); - } - - // Insert role using the user's ID - db_con.query( - `INSERT INTO UserRole (UserID, Client, Admin) - VALUES (LAST_INSERT_ID(), ${data.client || true}, ${data.admin || false})`, - (roleErr) => { - if (roleErr) { - console.error("Error creating role:", roleErr); - return res.status(500).json({ error: "Could not create role" }); - } - - // Delete verification record - db_con.query( - `DELETE FROM AuthVerification WHERE Email = '${data.email}'`, - (deleteErr) => { - if (deleteErr) { - console.error("Error deleting verification:", deleteErr); - } - res.json({ - success: true, - message: "User registration completed successfully", - name: data.name, - email: data.email, - UCID: data.UCID, - }); - }, - ); - }, - ); - }, - ); - }, - ); -}); - -// Clean up expired verification codes (run this periodically) -function cleanupExpiredCodes() { - db_con.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`); - } - }, - ); -} +//Routes +app.use("/api/user", userRouter); //prefix with /api/user +app.use("/api/product", productRouter); //prefix with /api/product // Set up a scheduler to run cleanup every hour setInterval(cleanupExpiredCodes, 60 * 60 * 1000); -//Fetch all users data: -app.get("/fetch_all_users", (req, res) => { - db_con.query("SELECT * FROM User;", (err, data) => { - if (err) { - console.error("Errors: ", err); - return res.status(500).json({ error: "\nCould not fetch users!" }); - } - res.json({ Users: data }); - }); -}); - -//Fetch One user Data with all fields: -app.post("/find_user", (req, res) => { - const { email } = req.body; - - // Input validation - if (!email) { - return res.status(400).json({ - found: false, - error: "Email is required", - }); - } - - // Query to find user with matching email and password - const query = "SELECT * FROM User WHERE email = ?"; - db_con.query(query, [email], (err, data) => { - if (err) { - console.error("Error finding user:", err); - return res.status(500).json({ - found: false, - error: "Database error occurred", - }); - } - - // 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", - }); - } - }); -}); - -//Update A uses Data: -app.post("/update", (req, res) => { - const { userId, ...updateData } = req.body; - - if (!userId) { - return res.status(400).json({ error: "User ID is required" }); - } - - //query dynamically based on provided fields - const updateFields = []; - const values = []; - - Object.entries(updateData).forEach(([key, value]) => { - // Only include fields that are actually in the User table - if (["Name", "Email", "Password", "Phone", "UCID"].includes(key)) { - updateFields.push(`${key} = ?`); - values.push(value); - } - }); - - if (updateFields.length === 0) { - return res.status(400).json({ error: "No valid fields to update" }); - } - - // Add userId to values array - values.push(userId); - - const query = `UPDATE User SET ${updateFields.join(", ")} WHERE UserID = ?`; - - db_con.query(query, values, (err, result) => { - if (err) { - console.error("Error updating user:", err); - return res.status(500).json({ error: "Could not update user" }); - } - - if (result.affectedRows === 0) { - return res.status(404).json({ error: "User not found" }); - } - - res.json({ success: true, message: "User updated successfully" }); - }); -}); - -//Delete A uses Data: -app.post("/delete", (req, res) => { - const { userId } = req.body; - - if (!userId) { - return res.status(400).json({ error: "User ID is required" }); - } - - // Delete from UserRole first (assuming foreign key constraint) - db_con.query("DELETE FROM UserRole WHERE UserID = ?", [userId], (err) => { - if (err) { - console.error("Error deleting user role:", err); - return res.status(500).json({ error: "Could not delete user role" }); - } - - // Then delete from User table - db_con.query( - "DELETE FROM User WHERE UserID = ?", - [userId], - (err, result) => { - if (err) { - console.error("Error deleting user:", err); - return res.status(500).json({ error: "Could not delete user" }); - } - - if (result.affectedRows === 0) { - return res.status(404).json({ error: "User not found" }); - } - - res.json({ success: true, message: "User deleted successfully" }); - }, - ); - }); -}); - -app.post("/add_fav_product", (req, res) => { - const { userID, productsID } = req.body; - - // Use parameterized query to prevent SQL injection - db_con.query( - "INSERT INTO Favorites (UserID, ProductID) VALUES (?, ?)", - [userID, productsID], - (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", - }); - }, - ); -}); - -app.get("/get_product", (req, res) => { - const query = "SELECT * FROM Product"; - db_con.query(query, (err, data) => { - if (err) { - console.error("Error finding user:", err); - return res.status(500).json({ - found: false, - error: "Database error occurred", - }); - } - res.json({ - success: true, - message: "Product added to favorites successfully", - data, - }); - }); -}); - -// 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", -// }); -// }, -// ); app.listen(3030, () => { console.log(`Running Backend on http://localhost:3030/`); diff --git a/backend/package-lock.json b/backend/package-lock.json index c497432..9f44399 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -18,6 +18,9 @@ "mysql": "^2.18.1", "mysql2": "^3.12.0", "nodemailer": "^6.10.0" + }, + "devDependencies": { + "nodemon": "^3.1.9" } }, "node_modules/accepts": { @@ -33,6 +36,20 @@ "node": ">= 0.6" } }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", @@ -48,6 +65,13 @@ "node": ">= 6.0.0" } }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, "node_modules/bignumber.js": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.0.tgz", @@ -57,6 +81,19 @@ "node": "*" } }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/body-parser": { "version": "1.20.3", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", @@ -81,6 +118,30 @@ "npm": "1.2.8000 || >= 1.4.16" } }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/buffer-equal-constant-time": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", @@ -125,6 +186,38 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, "node_modules/content-disposition": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", @@ -365,6 +458,19 @@ "url": "https://opencollective.com/express" } }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/finalhandler": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", @@ -401,6 +507,21 @@ "node": ">= 0.6" } }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -456,6 +577,19 @@ "node": ">= 0.4" } }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/gopd": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", @@ -468,6 +602,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/has-symbols": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", @@ -520,6 +664,13 @@ "node": ">=0.10.0" } }, + "node_modules/ignore-by-default": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", + "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", + "dev": true, + "license": "ISC" + }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", @@ -535,6 +686,52 @@ "node": ">= 0.10" } }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, "node_modules/is-property": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", @@ -737,6 +934,19 @@ "node": ">= 0.6" } }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -835,6 +1045,70 @@ "node": ">=6.0.0" } }, + "node_modules/nodemon": { + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.9.tgz", + "integrity": "sha512-hdr1oIb2p6ZSxu3PB2JWWYS7ZQ0qvaZsc3hK8DR8f02kRzc8rjYmxAIvdz+aYC+8F2IjNaB7HMcSDg8nQpJxyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": "^3.5.2", + "debug": "^4", + "ignore-by-default": "^1.0.1", + "minimatch": "^3.1.2", + "pstree.remy": "^1.1.8", + "semver": "^7.5.3", + "simple-update-notifier": "^2.0.0", + "supports-color": "^5.5.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.5" + }, + "bin": { + "nodemon": "bin/nodemon.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nodemon" + } + }, + "node_modules/nodemon/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/nodemon/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -883,6 +1157,19 @@ "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", "license": "MIT" }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", @@ -902,6 +1189,13 @@ "node": ">= 0.10" } }, + "node_modules/pstree.remy": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", + "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", + "dev": true, + "license": "MIT" + }, "node_modules/qs": { "version": "6.13.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", @@ -962,6 +1256,19 @@ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "license": "MIT" }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -1137,6 +1444,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/simple-update-notifier": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", + "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/sqlstring": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.3.tgz", @@ -1170,6 +1490,32 @@ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "license": "MIT" }, + "node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, "node_modules/toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", @@ -1179,6 +1525,16 @@ "node": ">=0.6" } }, + "node_modules/touch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz", + "integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==", + "dev": true, + "license": "ISC", + "bin": { + "nodetouch": "bin/nodetouch.js" + } + }, "node_modules/type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", @@ -1192,6 +1548,13 @@ "node": ">= 0.6" } }, + "node_modules/undefsafe": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", + "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", + "dev": true, + "license": "MIT" + }, "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", diff --git a/backend/package.json b/backend/package.json index 7e3cd0d..2eb28df 100644 --- a/backend/package.json +++ b/backend/package.json @@ -2,7 +2,7 @@ "name": "server", "version": "1.0.0", "main": "index.js", - "type": "module", + "type": "commonjs", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "dev": "nodemon index.js" @@ -21,5 +21,8 @@ "mysql": "^2.18.1", "mysql2": "^3.12.0", "nodemailer": "^6.10.0" + }, + "devDependencies": { + "nodemon": "^3.1.9" } } diff --git a/backend/routes/product.js b/backend/routes/product.js new file mode 100644 index 0000000..55d3761 --- /dev/null +++ b/backend/routes/product.js @@ -0,0 +1,10 @@ +const express = require("express"); +const { addToFavorite, getAllProducts } = require("../controllers/product"); + +const router = express.Router(); + +router.post("/add_fav_product", addToFavorite); + +router.get("/get_product", getAllProducts); + +module.exports = router; diff --git a/backend/routes/user.js b/backend/routes/user.js new file mode 100644 index 0000000..3d11102 --- /dev/null +++ b/backend/routes/user.js @@ -0,0 +1,35 @@ +const express = require("express"); +const { + sendVerificationCode, + verifyCode, + completeSignUp, + getAllUser, + findUserByEmail, + updateUser, + deleteUser, +} = 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 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); + +module.exports = router; diff --git a/backend/utils/database.js b/backend/utils/database.js new file mode 100644 index 0000000..020e67c --- /dev/null +++ b/backend/utils/database.js @@ -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(); diff --git a/backend/utils/helper.js b/backend/utils/helper.js new file mode 100644 index 0000000..406a897 --- /dev/null +++ b/backend/utils/helper.js @@ -0,0 +1,48 @@ +const { generateEmailTransporter } = require("./mail"); + +// 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: `

Your verification code is: ${verificationCode}

This code will expire in 15 minutes.

`, + }); + + console.log(`Verification code sent to ${email}`); +} + +// Clean up expired verification codes (run this periodically) +function cleanupExpiredCodes() { + db_con.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, +}; diff --git a/backend/utils/mail.js b/backend/utils/mail.js new file mode 100644 index 0000000..6810672 --- /dev/null +++ b/backend/utils/mail.js @@ -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 + }, + }); diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index a194491..5a65c60 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -59,16 +59,19 @@ function App() { console.log("Sending verification code to:", userData.email); // Make API call to send verification code - const response = await fetch("http://localhost:3030/send-verification", { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - email: userData.email, - // Add any other required fields - }), - }); + const response = await fetch( + "http://localhost:3030/api/user/send-verification", + { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + email: userData.email, + // Add any other required fields + }), + } + ); if (!response.ok) { throw new Error("Failed to send verification code"); @@ -105,16 +108,19 @@ function App() { console.log("Verifying code:", code); // Make API call to verify code - const response = await fetch("http://localhost:3030/verify-code", { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - email: tempUserData.email, - code: code, - }), - }); + const response = await fetch( + "http://localhost:3030/api/user/verify-code", + { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + email: tempUserData.email, + code: code, + }), + } + ); if (!response.ok) { throw new Error("Failed to verify code"); @@ -149,13 +155,16 @@ function App() { console.log(userData); // Make API call to complete signup - const response = await fetch("http://localhost:3030/complete-signup", { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify(userData), - }); + const response = await fetch( + "http://localhost:3030/api/user/complete-signup", + { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(userData), + } + ); if (!response.ok) { throw new Error("Failed to complete signup"); @@ -245,16 +254,19 @@ function App() { }); // Make API call to localhost:3030/find_user - const response = await fetch("http://localhost:3030/find_user", { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - email: formValues.email, - password: formValues.password, - }), - }); + const response = await fetch( + "http://localhost:3030/api/user/find_user", + { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + email: formValues.email, + password: formValues.password, + }), + } + ); if (!response.ok) { throw new Error("Network response was not ok"); @@ -497,8 +509,8 @@ function App() { {isLoading ? "Please wait..." : isSignUp - ? "Create Account" - : "Sign In"} + ? "Create Account" + : "Sign In"} diff --git a/frontend/src/main.jsx b/frontend/src/main.jsx index b9a1a6d..73fc17b 100644 --- a/frontend/src/main.jsx +++ b/frontend/src/main.jsx @@ -1,10 +1,10 @@ -import { StrictMode } from 'react' -import { createRoot } from 'react-dom/client' -import './index.css' -import App from './App.jsx' +import { StrictMode } from "react"; +import { createRoot } from "react-dom/client"; +import "./index.css"; +import App from "./App.jsx"; -createRoot(document.getElementById('root')).render( +createRoot(document.getElementById("root")).render( - , -) + +); diff --git a/frontend/src/pages/Home.jsx b/frontend/src/pages/Home.jsx index 6a4568b..300fd9d 100644 --- a/frontend/src/pages/Home.jsx +++ b/frontend/src/pages/Home.jsx @@ -10,7 +10,9 @@ const Home = () => { useEffect(() => { const fetchProducts = async () => { try { - const response = await fetch("http://localhost:3030/get_product"); + const response = await fetch( + "http://localhost:3030/api/product/get_product" + ); if (!response.ok) throw new Error("Failed to fetch products"); const data = await response.json(); @@ -27,7 +29,7 @@ const Home = () => { seller: "Unknown", // Modify if seller info is available datePosted: "Just now", isFavorite: false, - })), + })) ); } else { throw new Error(data.message || "Error fetching products"); @@ -48,8 +50,8 @@ const Home = () => { prevListings.map((listing) => listing.id === id ? { ...listing, isFavorite: !listing.isFavorite } - : listing, - ), + : listing + ) ); }; diff --git a/frontend/src/pages/Settings.jsx b/frontend/src/pages/Settings.jsx index bb09f74..15695e5 100644 --- a/frontend/src/pages/Settings.jsx +++ b/frontend/src/pages/Settings.jsx @@ -33,15 +33,18 @@ const Settings = () => { } // Make API call to fetch user data - const response = await fetch("http://localhost:3030/find_user", { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - email: storedUser.email, - }), - }); + const response = await fetch( + "http://localhost:3030/api/user/find_user", + { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + email: storedUser.email, + }), + } + ); const data = await response.json(); console.log(data); @@ -67,7 +70,7 @@ const Settings = () => { } catch (error) { console.error("Error fetching user data:", error); setError( - error.message || "An error occurred while loading your profile", + error.message || "An error occurred while loading your profile" ); } finally { setIsLoading(false); @@ -153,7 +156,7 @@ const Settings = () => { const handleDeleteAccount = async () => { if ( window.confirm( - "Are you sure you want to delete your account? This action cannot be undone.", + "Are you sure you want to delete your account? This action cannot be undone." ) ) { try { @@ -166,7 +169,7 @@ const Settings = () => { } // Make API call to delete the account - const response = await fetch("http://localhost:3030/delete", { + const response = await fetch("http://localhost:3030/api/user/delete", { method: "POST", headers: { "Content-Type": "application/json",