Merge pull request #1 from MannPatel0/estherBranch
Update backend and frontend
This commit is contained in:
@@ -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 (
|
||||
|
||||
70
backend/controllers/product.js
Normal file
70
backend/controllers/product.js
Normal file
@@ -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",
|
||||
// });
|
||||
// },
|
||||
// );
|
||||
263
backend/controllers/user.js
Normal file
263
backend/controllers/user.js
Normal file
@@ -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!" });
|
||||
}
|
||||
};
|
||||
460
backend/index.js
460
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: `<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}`);
|
||||
}
|
||||
|
||||
// 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/`);
|
||||
|
||||
363
backend/package-lock.json
generated
363
backend/package-lock.json
generated
@@ -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",
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
10
backend/routes/product.js
Normal file
10
backend/routes/product.js
Normal file
@@ -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;
|
||||
35
backend/routes/user.js
Normal file
35
backend/routes/user.js
Normal file
@@ -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;
|
||||
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();
|
||||
48
backend/utils/helper.js
Normal file
48
backend/utils/helper.js
Normal file
@@ -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: `<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_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,
|
||||
};
|
||||
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
|
||||
},
|
||||
});
|
||||
@@ -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"}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@@ -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(
|
||||
<StrictMode>
|
||||
<App />
|
||||
</StrictMode>,
|
||||
)
|
||||
</StrictMode>
|
||||
);
|
||||
|
||||
@@ -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
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user