From 2d0ac8eb8623a429b35e93a1e065ded2a7eea783 Mon Sep 17 00:00:00 2001 From: SaraTahiri Date: Wed, 27 Mar 2024 14:56:17 +0000 Subject: [PATCH 01/43] Update README.md --- README.md | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..4b890ba --- /dev/null +++ b/README.md @@ -0,0 +1,51 @@ +# 🍽️ Foody: Triple Registry Triple Login Website +## Introduction + +We have developed Foody, a triple registry triple login website, as a challenge to test our skills gained from a bootcamp and to create a fully functional MERN stack project. This website facilitates connections between clients, businesses, and delivery personnel, offering three distinct user experiences and corresponding code sections. Our platform integrates diverse technologies, ensuring scalability and adaptability across various industries. +Website Overview +Homepage 🏠 + +The homepage serves as the entry point, featuring a search bar for clients to explore restaurants/businesses or specific items/services. Additionally, clients can select their location and find featured restaurants/businesses, popular items, and special offers. +Client Dashboard 🧑‍💼 + +Clients can manage their accounts, view order history, track ongoing orders, and delivery progress. They can browse available restaurants/businesses, explore menus, place orders, save favorites, reorder items, and leave reviews. +Restaurant/Business Dashboard 🏢 + +Restaurant/business owners can manage menus, update business details, track order history and earnings, receive and manage orders, update inventory, and set delivery zones. They also have access to analytics and reporting tools. +Delivery Personnel Dashboard 🚚 + +Delivery personnel can manage their availability, view assigned orders, track deliveries, accept/decline delivery requests, communicate with clients/restaurants, and update delivery status. +Order Management System 📊 + +Facilitates communication between clients, restaurants/businesses, and delivery personnel. +How to Interact with the Website + +Users interact with Foody by browsing restaurant listings, selecting menu items, and placing orders for delivery or pickup. They can track their orders in real-time and leave reviews based on their experiences. Restaurant owners manage their menus, update business details, and receive and fulfill orders. Delivery personnel manage their availability, accept delivery requests, and communicate with clients and restaurants for smooth order delivery. All interactions are facilitated through intuitive user interfaces, ensuring a seamless and efficient experience for all parties involved. +Technologies Used + + Express.js + Mongoose (MongoDB) + Multer (for handling file uploads) + JSON Web Token (JWT) for authentication + Bcrypt (for hashing passwords) + Express Rate Limit (for rate limiting) + Helmet (for securing HTTP headers) + Compression (for compressing responses) + And more... + +## Endpoints/Routes + + Client Pages + Restaurant/Business Pages + Delivery Personnel Pages + General Pages + +## Entities/Schemas + + Client User Entity + Delivery Personnel Entity + Restaurant Entity + +## Conclusion + +Foody provides a comprehensive solution for connecting clients, businesses, and delivery personnel in a seamless and efficient manner. With its intuitive interfaces, robust features, and scalable architecture, Foody is poised to revolutionize the food delivery industry. From 4909e134bc208aace954e7ba46da142c7493e437 Mon Sep 17 00:00:00 2001 From: SaraTahiri Date: Wed, 27 Mar 2024 15:07:18 +0000 Subject: [PATCH 02/43] Add CRUD for client entity --- .gitignore | 5 +++ Controllers/Clients.Controllers.js | 62 ++++++++++++++++++++++++++++++ Controllers/Profile.Controllers.js | 59 ++++++++++++++++++++++++++++ Helpers/Hashing.js | 12 ++++++ Helpers/JWT.js | 9 +++++ Helpers/Query/queries.js | 0 Helpers/mailer.js | 30 +++++++++++++++ Helpers/permissions.js | 0 Helpers/response.helper.js | 0 Helpers/response.js | 0 Helpers/role.js | 0 Middlwares/auth.middleware.js | 23 +++++++++++ Middlwares/isOwner.middleware.js | 0 Middlwares/multer.js | 0 Middlwares/sanitize.js | 10 +++++ Middlwares/validate.js | 53 +++++++++++++++++++++++++ Modals/Schema/Delivery.js | 0 Modals/Schema/RestaurantSch.js | 0 Modals/Schema/UserSch.js | 38 ++++++++++++++++++ Routes/DeliveryRoute.js | 0 Routes/GeneralRoutes.js | 0 Routes/RestaurantRoute.js | 0 Routes/UserRoute.js | 39 +++++++++++++++++++ config/config.js | 0 config/constants.js | 0 config/database.js | 27 +++++++++++++ config/roles.js | 0 server.js | 34 ++++++++++++++++ 28 files changed, 401 insertions(+) create mode 100644 .gitignore create mode 100644 Controllers/Clients.Controllers.js create mode 100644 Controllers/Profile.Controllers.js create mode 100644 Helpers/Hashing.js create mode 100644 Helpers/JWT.js create mode 100644 Helpers/Query/queries.js create mode 100644 Helpers/mailer.js create mode 100644 Helpers/permissions.js create mode 100644 Helpers/response.helper.js create mode 100644 Helpers/response.js create mode 100644 Helpers/role.js create mode 100644 Middlwares/auth.middleware.js create mode 100644 Middlwares/isOwner.middleware.js create mode 100644 Middlwares/multer.js create mode 100644 Middlwares/sanitize.js create mode 100644 Middlwares/validate.js create mode 100644 Modals/Schema/Delivery.js create mode 100644 Modals/Schema/RestaurantSch.js create mode 100644 Modals/Schema/UserSch.js create mode 100644 Routes/DeliveryRoute.js create mode 100644 Routes/GeneralRoutes.js create mode 100644 Routes/RestaurantRoute.js create mode 100644 Routes/UserRoute.js create mode 100644 config/config.js create mode 100644 config/constants.js create mode 100644 config/database.js create mode 100644 config/roles.js create mode 100644 server.js diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7561803 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +node_modules +.env +package.json +package-lock.json +.git \ No newline at end of file diff --git a/Controllers/Clients.Controllers.js b/Controllers/Clients.Controllers.js new file mode 100644 index 0000000..effb4e6 --- /dev/null +++ b/Controllers/Clients.Controllers.js @@ -0,0 +1,62 @@ +const User = require('../Modals/Schema/UserSch') +const {sendEmail} = require('../Helpers/mailer') +const {hashPassword,comparePassword} = require('../Helpers/Hashing') +const {generateToken} = require('../Helpers/JWT') + +exports.renderRegister = (req,res) => { + res.send('/register') +} + +exports.renderLogin = (req,res) => { + res.send('/login') +} + +exports.registerUser = async (req,res) => { + try{ + const {email} = req.body; + //console.log(email) + const user = await User.findOne({email}); + //console.log(user) + if(user){ + return res.status(400).json({message: 'User already exists'}) + } + const {username,password,age,country,sex,phoneNumber,bio} = new User(req.body) + const hashedPassword = await hashPassword(password) + const newUser = new User({ + username, + email : email, + password : hashedPassword, + age , + country , + sex , + phoneNumber , + bio + }) + const result = await newUser.save() + //console.log(newUser.email) + sendEmail(newUser.email,newUser.username) + //console.log(result) + res.status(200).send('User registration successful') + }catch(error){ + console.log('error' , error) + res.status(500).send('Server Error'); + } +} + +exports.userLogin = async (req,res) => { + try{ + const {email,password} = req.body + const user = await User.findOne({email}) + if(!user){ + return res.status(400).json({message : 'Invalid Credentials'}) + } + + const checked =await comparePassword(password,user.password) + if(!checked) return res.status(400).json({message:"Incorrect Password"}) + + const token = await generateToken({user : user}) + res.status(200).cookie('tokenAuth',token).send('User logged successfuly') + }catch(error){ + console.log('error' , error) + } +} \ No newline at end of file diff --git a/Controllers/Profile.Controllers.js b/Controllers/Profile.Controllers.js new file mode 100644 index 0000000..5db7f38 --- /dev/null +++ b/Controllers/Profile.Controllers.js @@ -0,0 +1,59 @@ +const User = require('../Modals/Schema/UserSch') +const {hashPassword,comparePassword} = require('../Helpers/Hashing') + +exports.profileUser = async (req,res) => { + try{ + const id = req.user.user._id + const user = await User.findById(id) + if(!user) return res.status(404).json({message : 'User not found'}) + res.status(200).json(user) + + }catch(error){ + console.log('error' , error) + res.status(500).send('Server Error'); + } +} + +exports.UpdateProfile = async (req,res) => { + try{ + const id = req.user.user._id + const {username, email, password, age, country, sex, phoneNumber} = req.body; + const userfound = await User.findById(id) + const check = await comparePassword(password,userfound.password) + if(check){ + return res.json({message : 'Same password'}) + }else{ + const hashedPassword = await hashPassword(password); + const user = await User.updateOne({_id : id}, + { + username, + email, + password : hashedPassword, + age, + country, + sex, + phoneNumber, + bio + }); + res.json({ + data: user + }); + } + }catch(error){ + console.log('error' , error) + res.status(500).send('Server Error'); + } +} + +exports.deleteProfile = async (req,res) => { + try { + const id= req.user.user._id + const user = await User.deleteOne({_id : id}) + res.json({ + data: user + }); + }catch (err) { + console.error(err); + res.status(500).json({ message: 'Server error' }); + } +} \ No newline at end of file diff --git a/Helpers/Hashing.js b/Helpers/Hashing.js new file mode 100644 index 0000000..096ee6c --- /dev/null +++ b/Helpers/Hashing.js @@ -0,0 +1,12 @@ +const bcrypt = require('bcrypt') + +class Hashing{ + async hashPassword(password,salt=10){ + return await bcrypt.hash(password,salt); + } + async comparePassword(password,hashedPassword){ + return await bcrypt.compare(password,hashedPassword); + } +} + +module.exports = new Hashing \ No newline at end of file diff --git a/Helpers/JWT.js b/Helpers/JWT.js new file mode 100644 index 0000000..1c2e633 --- /dev/null +++ b/Helpers/JWT.js @@ -0,0 +1,9 @@ +const jwt = require('jsonwebtoken'); + +exports.generateToken = async (data)=>{ + return jwt.sign(data, process.env.SECRETKEY, {expiresIn: '1h'}) +} + +exports.verifyToken = (token)=>{ + return jwt.verify(token, process.env.SECRETKEY) +} \ No newline at end of file diff --git a/Helpers/Query/queries.js b/Helpers/Query/queries.js new file mode 100644 index 0000000..e69de29 diff --git a/Helpers/mailer.js b/Helpers/mailer.js new file mode 100644 index 0000000..b438eb9 --- /dev/null +++ b/Helpers/mailer.js @@ -0,0 +1,30 @@ +const nodemailer = require('nodemailer') +require('dotenv').config() + +exports.sendEmail = async (mail,username) => { + const transporter = nodemailer.createTransport({ + service: "gmail", + auth: { + user: process.env.EMAIL, + pass: process.env.PASSWORD + } + }); + + const mailOptions = { + from: process.env.EMAIL, + to: mail, + subject: "Welcome To our platform", + html : `

Welcome ${username}


Thank you for registering

` + }; + + transporter.sendMail(mailOptions, function(error, info) { + if (error) { + console.log(error); + res.send('Error'); + } else { + console.log("Email sent: " + info.response); + res.send("Success"); + } + }); +} + \ No newline at end of file diff --git a/Helpers/permissions.js b/Helpers/permissions.js new file mode 100644 index 0000000..e69de29 diff --git a/Helpers/response.helper.js b/Helpers/response.helper.js new file mode 100644 index 0000000..e69de29 diff --git a/Helpers/response.js b/Helpers/response.js new file mode 100644 index 0000000..e69de29 diff --git a/Helpers/role.js b/Helpers/role.js new file mode 100644 index 0000000..e69de29 diff --git a/Middlwares/auth.middleware.js b/Middlwares/auth.middleware.js new file mode 100644 index 0000000..c3d848b --- /dev/null +++ b/Middlwares/auth.middleware.js @@ -0,0 +1,23 @@ +const {verifyToken} = require('../Helpers/JWT'); // Assuming verifyToken function is imported correctly + +exports.isAuthenticated = (req, res, next) => { + try { + const token = req.cookies.tokenAuth || null; + //console.log(token) + if (!token) { + return res.status(401).json({ message: 'Empty token. Please login to access this resource.' }); + } + + const verify = verifyToken(token); + + if (!verify) { + return res.status(401).json({ message: 'Session not found. Please login again.' }); + } + //console.log(verify) + req.user = verify; + next(); + } catch (err) { + console.error(err); + return res.status(500).json({ message: 'Internal Server Error' }); + } +} \ No newline at end of file diff --git a/Middlwares/isOwner.middleware.js b/Middlwares/isOwner.middleware.js new file mode 100644 index 0000000..e69de29 diff --git a/Middlwares/multer.js b/Middlwares/multer.js new file mode 100644 index 0000000..e69de29 diff --git a/Middlwares/sanitize.js b/Middlwares/sanitize.js new file mode 100644 index 0000000..9e7d4bd --- /dev/null +++ b/Middlwares/sanitize.js @@ -0,0 +1,10 @@ +const xss = require("xss") +exports.Sanitize = (req, res, next) => { + const fields = ['username','email','age','country','sex','phoneNumber','bio'] + for(i in fields) { + if(req.body[fields[i]]) { + req.body[fields[i]] = xss(req.body[fields[i]]) + } + } + next() +} \ No newline at end of file diff --git a/Middlwares/validate.js b/Middlwares/validate.js new file mode 100644 index 0000000..98af5b6 --- /dev/null +++ b/Middlwares/validate.js @@ -0,0 +1,53 @@ +const {body,validationResult} = require('express-validator') + +exports.Validate = [ + // username + body('username') + .notEmpty() + .withMessage('username is required') + .isLength({min:3}) + .withMessage('username must be at least 3 characters long'), + // email + body('email') + .notEmpty() + .withMessage('email is required') + .isLength({min:5}) + .withMessage('email must be at least 5 characters long'), + // age + body('age') + .notEmpty() + .withMessage('age is required') + .isNumeric() + .withMessage('age must be a boolean'), + //country + body('country') + .notEmpty() + .withMessage('country is required') + .isLength({min:3}) + .withMessage('country must be at least 3 characters long'), + //sex + body('sex') + .notEmpty() + .withMessage('sex is required') + .isLength({min:3}) + .withMessage('sex must be at least 3 characters long'), + //phoneNumber + body('phoneNumber') + .notEmpty() + .withMessage('phoneNumber is required') + .isNumeric() + .withMessage('phoneNumber must be at least 3 characters long'), + //bio + body('bio') + .notEmpty() + .withMessage('bio is required') + .isLength({min:5}) + .withMessage('bio must be at least 5 characters long'), + (req,res,next) => { + const errors = validationResult(req) + if(!errors.isEmpty()) { + return res.status(400).json({errors: errors.array()}) + } + next() + } +] \ No newline at end of file diff --git a/Modals/Schema/Delivery.js b/Modals/Schema/Delivery.js new file mode 100644 index 0000000..e69de29 diff --git a/Modals/Schema/RestaurantSch.js b/Modals/Schema/RestaurantSch.js new file mode 100644 index 0000000..e69de29 diff --git a/Modals/Schema/UserSch.js b/Modals/Schema/UserSch.js new file mode 100644 index 0000000..d258093 --- /dev/null +++ b/Modals/Schema/UserSch.js @@ -0,0 +1,38 @@ +const mongoose = require('mongoose') + +const User = mongoose.Schema({ + username : { + type : 'string' , + required : true + }, + email : { + type: 'string' , + required : true + }, + password : { + type : 'string' , + required : true + }, + age : { + type: 'Number' + }, + country : { + type: 'String' + }, + sex : { + type: 'String' , + enum: ['male', 'female'] + }, + phoneNumber : { + type: 'Number' + }, + bio : { + type: 'String' + }, + createdAt: { + type: Date, + default: Date.now + } +}) + +module.exports = mongoose.model('users',User) \ No newline at end of file diff --git a/Routes/DeliveryRoute.js b/Routes/DeliveryRoute.js new file mode 100644 index 0000000..e69de29 diff --git a/Routes/GeneralRoutes.js b/Routes/GeneralRoutes.js new file mode 100644 index 0000000..e69de29 diff --git a/Routes/RestaurantRoute.js b/Routes/RestaurantRoute.js new file mode 100644 index 0000000..e69de29 diff --git a/Routes/UserRoute.js b/Routes/UserRoute.js new file mode 100644 index 0000000..1af08a5 --- /dev/null +++ b/Routes/UserRoute.js @@ -0,0 +1,39 @@ +const express = require('express') + +const {renderRegister, + registerUser, + renderLogin, + userLogin + } = require('../Controllers/Clients.Controllers') + +const {profileUser, + UpdateProfile, + deleteProfile + } = require('../Controllers/Profile.Controllers') + +const {Sanitize} = require('../Middlwares/sanitize') +const {Validate} = require('../Middlwares/validate') +const {isAuthenticated} = require('../Middlwares/auth.middleware') +const routes = express.Router() + +//Registration +routes.route('/register') +.get(renderRegister) +.post(Sanitize,Validate,registerUser) + +//Login +routes.route('/login') +.get(renderLogin) +.post(userLogin) + +//Profile +routes.route('/profile') +.get(isAuthenticated,profileUser) +//Update +routes.route('/profile/edit') +.patch(isAuthenticated,UpdateProfile) +//Delete +routes.route('/profile/delete') +.delete(isAuthenticated,deleteProfile) + +module.exports = routes \ No newline at end of file diff --git a/config/config.js b/config/config.js new file mode 100644 index 0000000..e69de29 diff --git a/config/constants.js b/config/constants.js new file mode 100644 index 0000000..e69de29 diff --git a/config/database.js b/config/database.js new file mode 100644 index 0000000..5fbd523 --- /dev/null +++ b/config/database.js @@ -0,0 +1,27 @@ +const mongoose = require('mongoose') + +exports.connection = () => { + function connectToMongo(){ + mongoose.connect('mongodb+srv://sara:M6QL7ZgULnEjtWRm@cluster0.qfvpv3o.mongodb.net/') + .then(() => console.log('Connected to MongoDB Atlas')) + .catch(err => console.error('Error connecting to MongoDB Atlas', err)); + } + + mongoose.connection.on('connected', () => { + console.log('Mongoose connected to MongoDB Atlas'); + }); + + mongoose.connection.on('error', (err) => { + console.error('Mongoose connection error:', err); + }); + + mongoose.connection.on('disconnected', () => { + console.log('Mongoose disconnected from MongoDB Atlas'); + }); + + return{ + connectToMongo, + } +} + +//M6QL7ZgULnEjtWRm \ No newline at end of file diff --git a/config/roles.js b/config/roles.js new file mode 100644 index 0000000..e69de29 diff --git a/server.js b/server.js new file mode 100644 index 0000000..af3d3c5 --- /dev/null +++ b/server.js @@ -0,0 +1,34 @@ +require('dotenv').config() + +const express = require('express') +const app = express() +const cookieParser = require('cookie-parser') +const PORT = process.env.PORT + +//Import the database connection function +const {connection} = require('./config/database') +const database = connection() + +//Middleware setup +app.use(cookieParser()) +app.use(express.json()) +app.use(express.urlencoded({extended : true})) + +//Connect to the MongoDb databse +database.connectToMongo() + +const userRoutes = require('./Routes/UserRoute') +const {default : mongoose} = require('mongoose') + +app.use('/client',userRoutes) + +app.use((err, req, res, next) => { + // Handle errors and respond accordingly + console.error(err); + res.status(strings.SERVER_HTTP_VERSION_NOT_SUPPORTED_HTTP_CODE).json({ error: strings.EMPTY_REQUEST_FOR_UPDATE }); +}); + +// Start the Express server and listen on port 3000 +app.listen(PORT, () => { + console.log('Listening on port' + PORT); +}); \ No newline at end of file From 8cc84b58918c367498ed02a667d41e253be3424e Mon Sep 17 00:00:00 2001 From: SaraTahiri Date: Wed, 27 Mar 2024 15:40:34 +0000 Subject: [PATCH 03/43] Update README.md --- README.md | 10 +++++----- arkx.js | 0 2 files changed, 5 insertions(+), 5 deletions(-) delete mode 100644 arkx.js diff --git a/README.md b/README.md index 2fd5a59..4b890ba 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ -🍽️ Foody: Triple Registry Triple Login Website -Introduction +# 🍽️ Foody: Triple Registry Triple Login Website +## Introduction We have developed Foody, a triple registry triple login website, as a challenge to test our skills gained from a bootcamp and to create a fully functional MERN stack project. This website facilitates connections between clients, businesses, and delivery personnel, offering three distinct user experiences and corresponding code sections. Our platform integrates diverse technologies, ensuring scalability and adaptability across various industries. Website Overview @@ -33,19 +33,19 @@ Technologies Used Compression (for compressing responses) And more... -Endpoints/Routes +## Endpoints/Routes Client Pages Restaurant/Business Pages Delivery Personnel Pages General Pages -Entities/Schemas +## Entities/Schemas Client User Entity Delivery Personnel Entity Restaurant Entity -Conclusion +## Conclusion Foody provides a comprehensive solution for connecting clients, businesses, and delivery personnel in a seamless and efficient manner. With its intuitive interfaces, robust features, and scalable architecture, Foody is poised to revolutionize the food delivery industry. diff --git a/arkx.js b/arkx.js deleted file mode 100644 index e69de29..0000000 From 8120ba4798c5aa33f0bf4f304c079510936e3c02 Mon Sep 17 00:00:00 2001 From: SaraTahiri Date: Wed, 27 Mar 2024 15:41:46 +0000 Subject: [PATCH 04/43] Modify server.js for client CRUD (pre-normalizing) --- server.js | 36 ++++++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/server.js b/server.js index ef6d305..af3d3c5 100644 --- a/server.js +++ b/server.js @@ -1,18 +1,34 @@ -const express = require('express'); -require('dotenv').config(); +require('dotenv').config() -const app = express(); -const port = process.env.PORT || 3000; +const express = require('express') +const app = express() +const cookieParser = require('cookie-parser') +const PORT = process.env.PORT +//Import the database connection function +const {connection} = require('./config/database') +const database = connection() -app.use(express.json()); +//Middleware setup +app.use(cookieParser()) +app.use(express.json()) +app.use(express.urlencoded({extended : true})) +//Connect to the MongoDb databse +database.connectToMongo() -app.get('/', (req, res) => { - res.send('Hello, World!'); -}); +const userRoutes = require('./Routes/UserRoute') +const {default : mongoose} = require('mongoose') +app.use('/client',userRoutes) -app.listen(port, () => { - console.log('Server is running on http://localhost:'+ PORT); +app.use((err, req, res, next) => { + // Handle errors and respond accordingly + console.error(err); + res.status(strings.SERVER_HTTP_VERSION_NOT_SUPPORTED_HTTP_CODE).json({ error: strings.EMPTY_REQUEST_FOR_UPDATE }); }); + +// Start the Express server and listen on port 3000 +app.listen(PORT, () => { + console.log('Listening on port' + PORT); +}); \ No newline at end of file From 0e200cc8078725e12846b56166f2930e6c7d9cad Mon Sep 17 00:00:00 2001 From: SaraTahiri Date: Wed, 27 Mar 2024 15:42:44 +0000 Subject: [PATCH 05/43] Add new dependecies --- package.json | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/package.json b/package.json index 21162d4..fb5eef5 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "main": "server.js", "scripts": { "test": "/", - "start": "node server.js" + "start": "nodemon server.js" }, "repository": { "type": "git", @@ -19,31 +19,22 @@ "homepage": "https://github.com/othmanordaski/Foody#readme", "dependencies": { "bcrypt": "^5.1.1", - "bcrypt-nodejs": "^0.0.3", - "bcryptjs": "^2.4.3", - "compression": "^1.7.4", - "connect-mongo": "^5.1.0", "cookie-parser": "^1.4.6", - "cors": "^2.8.5", "dotenv": "^16.4.5", "emailjs": "^4.0.3", "express": "^4.19.2", - "express-fileupload": "^1.5.0", - "express-rate-limit": "^7.2.0", "express-session": "^1.18.0", "express-validator": "^7.0.1", "helmet": "^7.1.0", - "helmet-csp": "^3.4.0", "jsonwebtoken": "^9.0.2", - "lodash": "^4.17.21", - "moment": "^2.30.1", "mongoose": "^8.2.3", - "morgan": "^1.10.0", "multer": "^1.4.5-lts.1", - "nodemailer-sendgrid-transport": "^0.2.0", - "sanitize-html": "^2.13.0", + "nodemailer": "^6.9.13", "validator": "^13.11.0", - "winston": "^3.13.0", "xss": "^1.0.15" + }, + "keywords": [], + "devDependencies": { + "nodemon": "^3.1.0" } } From 57f1e75a2406f87f6b6735d948696f0750a9bfd3 Mon Sep 17 00:00:00 2001 From: SaraTahiri Date: Wed, 27 Mar 2024 15:43:13 +0000 Subject: [PATCH 06/43] Add .gitignore --- .gitignore | 131 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 131 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..047e6a4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,131 @@ + +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) +web_modules/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional stylelint cache +.stylelintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variable files +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Next.js build output +.next +out + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# vuepress v2.x temp and cache directory +.temp +.cache + +# Docusaurus cache and generated files +.docusaurus + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# yarn v2 +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* From 32c095a78a66d9ba786908b04ff9d344afdd640c Mon Sep 17 00:00:00 2001 From: SaraTahiri Date: Wed, 27 Mar 2024 15:44:21 +0000 Subject: [PATCH 07/43] Add mail confirmation feat helper --- Helpers/mailer.js | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 Helpers/mailer.js diff --git a/Helpers/mailer.js b/Helpers/mailer.js new file mode 100644 index 0000000..b438eb9 --- /dev/null +++ b/Helpers/mailer.js @@ -0,0 +1,30 @@ +const nodemailer = require('nodemailer') +require('dotenv').config() + +exports.sendEmail = async (mail,username) => { + const transporter = nodemailer.createTransport({ + service: "gmail", + auth: { + user: process.env.EMAIL, + pass: process.env.PASSWORD + } + }); + + const mailOptions = { + from: process.env.EMAIL, + to: mail, + subject: "Welcome To our platform", + html : `

Welcome ${username}


Thank you for registering

` + }; + + transporter.sendMail(mailOptions, function(error, info) { + if (error) { + console.log(error); + res.send('Error'); + } else { + console.log("Email sent: " + info.response); + res.send("Success"); + } + }); +} + \ No newline at end of file From a4bc792db28c2e8ea2660a16478c083364a77173 Mon Sep 17 00:00:00 2001 From: SaraTahiri Date: Wed, 27 Mar 2024 15:45:06 +0000 Subject: [PATCH 08/43] Add hashing (bcrypt) and jwt helpers --- Helpers/Hashing.js | 12 ++++++++++++ Helpers/JWT.js | 9 +++++++++ 2 files changed, 21 insertions(+) diff --git a/Helpers/Hashing.js b/Helpers/Hashing.js index e69de29..096ee6c 100644 --- a/Helpers/Hashing.js +++ b/Helpers/Hashing.js @@ -0,0 +1,12 @@ +const bcrypt = require('bcrypt') + +class Hashing{ + async hashPassword(password,salt=10){ + return await bcrypt.hash(password,salt); + } + async comparePassword(password,hashedPassword){ + return await bcrypt.compare(password,hashedPassword); + } +} + +module.exports = new Hashing \ No newline at end of file diff --git a/Helpers/JWT.js b/Helpers/JWT.js index e69de29..1c2e633 100644 --- a/Helpers/JWT.js +++ b/Helpers/JWT.js @@ -0,0 +1,9 @@ +const jwt = require('jsonwebtoken'); + +exports.generateToken = async (data)=>{ + return jwt.sign(data, process.env.SECRETKEY, {expiresIn: '1h'}) +} + +exports.verifyToken = (token)=>{ + return jwt.verify(token, process.env.SECRETKEY) +} \ No newline at end of file From 5090ef5e6305541c14ce5bd983f501b97ba67f77 Mon Sep 17 00:00:00 2001 From: SaraTahiri Date: Wed, 27 Mar 2024 15:45:49 +0000 Subject: [PATCH 09/43] Add client auth and data handling middlewares --- Middlwares/auth.middleware.js | 23 +++++++++++++++ Middlwares/sanitize.js | 10 +++++++ Middlwares/validate.js | 53 +++++++++++++++++++++++++++++++++++ 3 files changed, 86 insertions(+) diff --git a/Middlwares/auth.middleware.js b/Middlwares/auth.middleware.js index e69de29..c3d848b 100644 --- a/Middlwares/auth.middleware.js +++ b/Middlwares/auth.middleware.js @@ -0,0 +1,23 @@ +const {verifyToken} = require('../Helpers/JWT'); // Assuming verifyToken function is imported correctly + +exports.isAuthenticated = (req, res, next) => { + try { + const token = req.cookies.tokenAuth || null; + //console.log(token) + if (!token) { + return res.status(401).json({ message: 'Empty token. Please login to access this resource.' }); + } + + const verify = verifyToken(token); + + if (!verify) { + return res.status(401).json({ message: 'Session not found. Please login again.' }); + } + //console.log(verify) + req.user = verify; + next(); + } catch (err) { + console.error(err); + return res.status(500).json({ message: 'Internal Server Error' }); + } +} \ No newline at end of file diff --git a/Middlwares/sanitize.js b/Middlwares/sanitize.js index e69de29..9e7d4bd 100644 --- a/Middlwares/sanitize.js +++ b/Middlwares/sanitize.js @@ -0,0 +1,10 @@ +const xss = require("xss") +exports.Sanitize = (req, res, next) => { + const fields = ['username','email','age','country','sex','phoneNumber','bio'] + for(i in fields) { + if(req.body[fields[i]]) { + req.body[fields[i]] = xss(req.body[fields[i]]) + } + } + next() +} \ No newline at end of file diff --git a/Middlwares/validate.js b/Middlwares/validate.js index e69de29..98af5b6 100644 --- a/Middlwares/validate.js +++ b/Middlwares/validate.js @@ -0,0 +1,53 @@ +const {body,validationResult} = require('express-validator') + +exports.Validate = [ + // username + body('username') + .notEmpty() + .withMessage('username is required') + .isLength({min:3}) + .withMessage('username must be at least 3 characters long'), + // email + body('email') + .notEmpty() + .withMessage('email is required') + .isLength({min:5}) + .withMessage('email must be at least 5 characters long'), + // age + body('age') + .notEmpty() + .withMessage('age is required') + .isNumeric() + .withMessage('age must be a boolean'), + //country + body('country') + .notEmpty() + .withMessage('country is required') + .isLength({min:3}) + .withMessage('country must be at least 3 characters long'), + //sex + body('sex') + .notEmpty() + .withMessage('sex is required') + .isLength({min:3}) + .withMessage('sex must be at least 3 characters long'), + //phoneNumber + body('phoneNumber') + .notEmpty() + .withMessage('phoneNumber is required') + .isNumeric() + .withMessage('phoneNumber must be at least 3 characters long'), + //bio + body('bio') + .notEmpty() + .withMessage('bio is required') + .isLength({min:5}) + .withMessage('bio must be at least 5 characters long'), + (req,res,next) => { + const errors = validationResult(req) + if(!errors.isEmpty()) { + return res.status(400).json({errors: errors.array()}) + } + next() + } +] \ No newline at end of file From 955a412a914b8e464de131ec32f03a71a12427d9 Mon Sep 17 00:00:00 2001 From: SaraTahiri Date: Wed, 27 Mar 2024 15:46:22 +0000 Subject: [PATCH 10/43] Add client routes and controllers --- Controllers/Clients.Controllers.js | 62 ++++++++++++++++++++++++++++++ Controllers/Profile.Controllers.js | 59 ++++++++++++++++++++++++++++ Routes/UserRoute.js | 39 +++++++++++++++++++ 3 files changed, 160 insertions(+) create mode 100644 Controllers/Clients.Controllers.js create mode 100644 Controllers/Profile.Controllers.js diff --git a/Controllers/Clients.Controllers.js b/Controllers/Clients.Controllers.js new file mode 100644 index 0000000..effb4e6 --- /dev/null +++ b/Controllers/Clients.Controllers.js @@ -0,0 +1,62 @@ +const User = require('../Modals/Schema/UserSch') +const {sendEmail} = require('../Helpers/mailer') +const {hashPassword,comparePassword} = require('../Helpers/Hashing') +const {generateToken} = require('../Helpers/JWT') + +exports.renderRegister = (req,res) => { + res.send('/register') +} + +exports.renderLogin = (req,res) => { + res.send('/login') +} + +exports.registerUser = async (req,res) => { + try{ + const {email} = req.body; + //console.log(email) + const user = await User.findOne({email}); + //console.log(user) + if(user){ + return res.status(400).json({message: 'User already exists'}) + } + const {username,password,age,country,sex,phoneNumber,bio} = new User(req.body) + const hashedPassword = await hashPassword(password) + const newUser = new User({ + username, + email : email, + password : hashedPassword, + age , + country , + sex , + phoneNumber , + bio + }) + const result = await newUser.save() + //console.log(newUser.email) + sendEmail(newUser.email,newUser.username) + //console.log(result) + res.status(200).send('User registration successful') + }catch(error){ + console.log('error' , error) + res.status(500).send('Server Error'); + } +} + +exports.userLogin = async (req,res) => { + try{ + const {email,password} = req.body + const user = await User.findOne({email}) + if(!user){ + return res.status(400).json({message : 'Invalid Credentials'}) + } + + const checked =await comparePassword(password,user.password) + if(!checked) return res.status(400).json({message:"Incorrect Password"}) + + const token = await generateToken({user : user}) + res.status(200).cookie('tokenAuth',token).send('User logged successfuly') + }catch(error){ + console.log('error' , error) + } +} \ No newline at end of file diff --git a/Controllers/Profile.Controllers.js b/Controllers/Profile.Controllers.js new file mode 100644 index 0000000..5db7f38 --- /dev/null +++ b/Controllers/Profile.Controllers.js @@ -0,0 +1,59 @@ +const User = require('../Modals/Schema/UserSch') +const {hashPassword,comparePassword} = require('../Helpers/Hashing') + +exports.profileUser = async (req,res) => { + try{ + const id = req.user.user._id + const user = await User.findById(id) + if(!user) return res.status(404).json({message : 'User not found'}) + res.status(200).json(user) + + }catch(error){ + console.log('error' , error) + res.status(500).send('Server Error'); + } +} + +exports.UpdateProfile = async (req,res) => { + try{ + const id = req.user.user._id + const {username, email, password, age, country, sex, phoneNumber} = req.body; + const userfound = await User.findById(id) + const check = await comparePassword(password,userfound.password) + if(check){ + return res.json({message : 'Same password'}) + }else{ + const hashedPassword = await hashPassword(password); + const user = await User.updateOne({_id : id}, + { + username, + email, + password : hashedPassword, + age, + country, + sex, + phoneNumber, + bio + }); + res.json({ + data: user + }); + } + }catch(error){ + console.log('error' , error) + res.status(500).send('Server Error'); + } +} + +exports.deleteProfile = async (req,res) => { + try { + const id= req.user.user._id + const user = await User.deleteOne({_id : id}) + res.json({ + data: user + }); + }catch (err) { + console.error(err); + res.status(500).json({ message: 'Server error' }); + } +} \ No newline at end of file diff --git a/Routes/UserRoute.js b/Routes/UserRoute.js index e69de29..1af08a5 100644 --- a/Routes/UserRoute.js +++ b/Routes/UserRoute.js @@ -0,0 +1,39 @@ +const express = require('express') + +const {renderRegister, + registerUser, + renderLogin, + userLogin + } = require('../Controllers/Clients.Controllers') + +const {profileUser, + UpdateProfile, + deleteProfile + } = require('../Controllers/Profile.Controllers') + +const {Sanitize} = require('../Middlwares/sanitize') +const {Validate} = require('../Middlwares/validate') +const {isAuthenticated} = require('../Middlwares/auth.middleware') +const routes = express.Router() + +//Registration +routes.route('/register') +.get(renderRegister) +.post(Sanitize,Validate,registerUser) + +//Login +routes.route('/login') +.get(renderLogin) +.post(userLogin) + +//Profile +routes.route('/profile') +.get(isAuthenticated,profileUser) +//Update +routes.route('/profile/edit') +.patch(isAuthenticated,UpdateProfile) +//Delete +routes.route('/profile/delete') +.delete(isAuthenticated,deleteProfile) + +module.exports = routes \ No newline at end of file From 7e9b3151c6fca7bd43a1ae6e2aa617470f459dee Mon Sep 17 00:00:00 2001 From: SaraTahiri Date: Wed, 27 Mar 2024 15:46:55 +0000 Subject: [PATCH 11/43] Add Mongo user schema --- Modals/Schema/UserSch.js | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/Modals/Schema/UserSch.js b/Modals/Schema/UserSch.js index e69de29..d258093 100644 --- a/Modals/Schema/UserSch.js +++ b/Modals/Schema/UserSch.js @@ -0,0 +1,38 @@ +const mongoose = require('mongoose') + +const User = mongoose.Schema({ + username : { + type : 'string' , + required : true + }, + email : { + type: 'string' , + required : true + }, + password : { + type : 'string' , + required : true + }, + age : { + type: 'Number' + }, + country : { + type: 'String' + }, + sex : { + type: 'String' , + enum: ['male', 'female'] + }, + phoneNumber : { + type: 'Number' + }, + bio : { + type: 'String' + }, + createdAt: { + type: Date, + default: Date.now + } +}) + +module.exports = mongoose.model('users',User) \ No newline at end of file From f345f6399c12256ca9242813aa949ca249efd237 Mon Sep 17 00:00:00 2001 From: SaraTahiri Date: Thu, 28 Mar 2024 12:37:31 +0000 Subject: [PATCH 12/43] Updated client controllers --- Controllers/Clients.Controllers.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Controllers/Clients.Controllers.js b/Controllers/Clients.Controllers.js index effb4e6..f98e4be 100644 --- a/Controllers/Clients.Controllers.js +++ b/Controllers/Clients.Controllers.js @@ -54,9 +54,10 @@ exports.userLogin = async (req,res) => { const checked =await comparePassword(password,user.password) if(!checked) return res.status(400).json({message:"Incorrect Password"}) - const token = await generateToken({user : user}) + const token = await generateToken({user}) res.status(200).cookie('tokenAuth',token).send('User logged successfuly') }catch(error){ console.log('error' , error) } + } \ No newline at end of file From 6a02fd1196deaa31a5f35f45f2cef0e98511140b Mon Sep 17 00:00:00 2001 From: SaraTahiri Date: Thu, 28 Mar 2024 12:38:49 +0000 Subject: [PATCH 13/43] Update and delete profile --- Controllers/Profile.Controllers.js | 63 ++++++++++++++++++++++++++++-- 1 file changed, 60 insertions(+), 3 deletions(-) diff --git a/Controllers/Profile.Controllers.js b/Controllers/Profile.Controllers.js index 5db7f38..ea28fae 100644 --- a/Controllers/Profile.Controllers.js +++ b/Controllers/Profile.Controllers.js @@ -14,11 +14,65 @@ exports.profileUser = async (req,res) => { } } +exports.UpdateProfile = async (req, res) => { + try { + const id = req.user.user._id; + const { username, email, password, age, country, sex, phoneNumber, bio } = req.body; + + const userfound = await User.findById(id); + if (!userfound) { + return res.status(404).json({ message: 'User not found.' }); + } + + if (password) { + const check = await comparePassword(password, userfound.password); + if (check) { + return res.json({ message: 'Same password' }); + } + } + + let hashedPassword; + if (password) { + hashedPassword = await hashPassword(password); + } + + const updateFields = { + username, + email, + age, + country, + sex, + phoneNumber, + bio + }; + + if (hashedPassword) { + updateFields.password = hashedPassword; + } + + const updatedUser = await User.findByIdAndUpdate(id, updateFields, { new: true }); + + res.json({ + message: 'Profile updated successfully', + data: updatedUser + }); + } catch (error) { + console.error('Error updating profile:', error); + res.status(500).json({ message: 'Server Error' }); + } +} +/* --------------------------------------------------------------------------------------------------------------- exports.UpdateProfile = async (req,res) => { try{ const id = req.user.user._id - const {username, email, password, age, country, sex, phoneNumber} = req.body; + //console.log(id) + const {username, email, password, age, country, sex, phoneNumber,bio} = req.body; const userfound = await User.findById(id) + + if (!userfound) { + return res.status(404).json({ message: 'User not found.' }); + } + const check = await comparePassword(password,userfound.password) if(check){ return res.json({message : 'Same password'}) @@ -27,8 +81,8 @@ exports.UpdateProfile = async (req,res) => { const user = await User.updateOne({_id : id}, { username, - email, - password : hashedPassword, + email, + password : hashedPassword , age, country, sex, @@ -44,12 +98,15 @@ exports.UpdateProfile = async (req,res) => { res.status(500).send('Server Error'); } } +-------------------------------------------------------------------------------------------------------------- +*/ exports.deleteProfile = async (req,res) => { try { const id= req.user.user._id const user = await User.deleteOne({_id : id}) res.json({ + message: 'Profile deleted successfully', data: user }); }catch (err) { From f4c018aeb1bfe13a1b2aeba67c2b136d482d46bd Mon Sep 17 00:00:00 2001 From: SaraTahiri Date: Thu, 28 Mar 2024 12:40:08 +0000 Subject: [PATCH 14/43] Update authentification function --- Middlwares/auth.middleware.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Middlwares/auth.middleware.js b/Middlwares/auth.middleware.js index c3d848b..847bb48 100644 --- a/Middlwares/auth.middleware.js +++ b/Middlwares/auth.middleware.js @@ -13,8 +13,8 @@ exports.isAuthenticated = (req, res, next) => { if (!verify) { return res.status(401).json({ message: 'Session not found. Please login again.' }); } - //console.log(verify) - req.user = verify; + req.user= verify; + console.log(req.user) next(); } catch (err) { console.error(err); From 7d463171b27017fb6e741b7c8ae6a0ad8060c014 Mon Sep 17 00:00:00 2001 From: SaraTahiri Date: Thu, 28 Mar 2024 12:41:07 +0000 Subject: [PATCH 15/43] Updated schema version 2 --- Modals/Schema/UserSch.js | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/Modals/Schema/UserSch.js b/Modals/Schema/UserSch.js index d258093..5cb981d 100644 --- a/Modals/Schema/UserSch.js +++ b/Modals/Schema/UserSch.js @@ -1,4 +1,5 @@ const mongoose = require('mongoose') +const {hashPassword} = require('../../Helpers/Hashing') const User = mongoose.Schema({ username : { @@ -7,7 +8,8 @@ const User = mongoose.Schema({ }, email : { type: 'string' , - required : true + required : true , + unique : true }, password : { type : 'string' , @@ -35,4 +37,21 @@ const User = mongoose.Schema({ } }) +/* +//pre-save to hash password before saving +User.pre('save', async function(next) { + // Only hash the password if it's modified or new + if (!this.isModified('password')) { + return next(); + } + + try { + const hashedPassword = await hashPassword(this.password, 10); + this.password = hashedPassword; + next(); + } catch (error) { + next(error); + } +}) +*/ module.exports = mongoose.model('users',User) \ No newline at end of file From f25a15d726846a3b38041ae8de32562673d4356c Mon Sep 17 00:00:00 2001 From: SaraTahiri Date: Thu, 28 Mar 2024 12:42:01 +0000 Subject: [PATCH 16/43] Updated database --- config/database.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/config/database.js b/config/database.js index 5fbd523..c86a3c5 100644 --- a/config/database.js +++ b/config/database.js @@ -23,5 +23,3 @@ exports.connection = () => { connectToMongo, } } - -//M6QL7ZgULnEjtWRm \ No newline at end of file From cc3eac215690d06025411a0a2c52749975c49a78 Mon Sep 17 00:00:00 2001 From: SaraTahiri Date: Sat, 30 Mar 2024 21:35:58 +0000 Subject: [PATCH 17/43] Add logout function --- Controllers/Clients.Controllers.js | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/Controllers/Clients.Controllers.js b/Controllers/Clients.Controllers.js index f98e4be..bfa198b 100644 --- a/Controllers/Clients.Controllers.js +++ b/Controllers/Clients.Controllers.js @@ -3,6 +3,7 @@ const {sendEmail} = require('../Helpers/mailer') const {hashPassword,comparePassword} = require('../Helpers/Hashing') const {generateToken} = require('../Helpers/JWT') +/* exports.renderRegister = (req,res) => { res.send('/register') } @@ -10,17 +11,16 @@ exports.renderRegister = (req,res) => { exports.renderLogin = (req,res) => { res.send('/login') } +*/ +//Register exports.registerUser = async (req,res) => { try{ - const {email} = req.body; - //console.log(email) + const {username,password,age,country,sex,phoneNumber,bio} = req.body const user = await User.findOne({email}); - //console.log(user) if(user){ return res.status(400).json({message: 'User already exists'}) } - const {username,password,age,country,sex,phoneNumber,bio} = new User(req.body) const hashedPassword = await hashPassword(password) const newUser = new User({ username, @@ -33,16 +33,14 @@ exports.registerUser = async (req,res) => { bio }) const result = await newUser.save() - //console.log(newUser.email) sendEmail(newUser.email,newUser.username) - //console.log(result) res.status(200).send('User registration successful') }catch(error){ - console.log('error' , error) res.status(500).send('Server Error'); } } +//Login exports.userLogin = async (req,res) => { try{ const {email,password} = req.body @@ -57,7 +55,17 @@ exports.userLogin = async (req,res) => { const token = await generateToken({user}) res.status(200).cookie('tokenAuth',token).send('User logged successfuly') }catch(error){ - console.log('error' , error) + res.status(500).send('Server Error'); } +} + +//Logout +exports.userLogout = async (req,res) => { + try{ + res.clearCookie('tokenAuth') + res.send("Logout successfuly") + }catch(error){ + res.status(500).send('Server Error') + } } \ No newline at end of file From 265cdd395d3b066941900a331aa51a4b141dc443 Mon Sep 17 00:00:00 2001 From: SaraTahiri Date: Sun, 31 Mar 2024 23:06:03 +0000 Subject: [PATCH 18/43] Update the last version of clients controllers --- Controllers/Clients.Controllers.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Controllers/Clients.Controllers.js b/Controllers/Clients.Controllers.js index bfa198b..2aeecc7 100644 --- a/Controllers/Clients.Controllers.js +++ b/Controllers/Clients.Controllers.js @@ -12,12 +12,12 @@ exports.renderLogin = (req,res) => { res.send('/login') } */ - //Register exports.registerUser = async (req,res) => { try{ - const {username,password,age,country,sex,phoneNumber,bio} = req.body + const {username,email,password,age,country,sex,phoneNumber,bio} = req.body const user = await User.findOne({email}); + //console.log(user) if(user){ return res.status(400).json({message: 'User already exists'}) } @@ -39,7 +39,6 @@ exports.registerUser = async (req,res) => { res.status(500).send('Server Error'); } } - //Login exports.userLogin = async (req,res) => { try{ @@ -55,7 +54,7 @@ exports.userLogin = async (req,res) => { const token = await generateToken({user}) res.status(200).cookie('tokenAuth',token).send('User logged successfuly') }catch(error){ - res.status(500).send('Server Error'); + console.log('error' , error) } } From 7d7469fc96b86b19b13949427f4c6b764b571293 Mon Sep 17 00:00:00 2001 From: SaraTahiri Date: Sun, 31 Mar 2024 23:07:00 +0000 Subject: [PATCH 19/43] Update profile controllers --- Controllers/Profile.Controllers.js | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/Controllers/Profile.Controllers.js b/Controllers/Profile.Controllers.js index ea28fae..536edae 100644 --- a/Controllers/Profile.Controllers.js +++ b/Controllers/Profile.Controllers.js @@ -6,14 +6,14 @@ exports.profileUser = async (req,res) => { const id = req.user.user._id const user = await User.findById(id) if(!user) return res.status(404).json({message : 'User not found'}) - res.status(200).json(user) + res.status(200).json(user.username) }catch(error){ - console.log('error' , error) res.status(500).send('Server Error'); } } + exports.UpdateProfile = async (req, res) => { try { const id = req.user.user._id; @@ -54,18 +54,16 @@ exports.UpdateProfile = async (req, res) => { res.json({ message: 'Profile updated successfully', - data: updatedUser + data: updatedUser.username }); } catch (error) { - console.error('Error updating profile:', error); res.status(500).json({ message: 'Server Error' }); } } -/* --------------------------------------------------------------------------------------------------------------- +/* exports.UpdateProfile = async (req,res) => { try{ const id = req.user.user._id - //console.log(id) const {username, email, password, age, country, sex, phoneNumber,bio} = req.body; const userfound = await User.findById(id) @@ -94,13 +92,10 @@ exports.UpdateProfile = async (req,res) => { }); } }catch(error){ - console.log('error' , error) res.status(500).send('Server Error'); } } --------------------------------------------------------------------------------------------------------------- */ - exports.deleteProfile = async (req,res) => { try { const id= req.user.user._id @@ -110,7 +105,6 @@ exports.deleteProfile = async (req,res) => { data: user }); }catch (err) { - console.error(err); res.status(500).json({ message: 'Server error' }); } } \ No newline at end of file From d43bca3b6ff5f582424a75c66ebfbea86c71d681 Mon Sep 17 00:00:00 2001 From: SaraTahiri Date: Sun, 31 Mar 2024 23:08:02 +0000 Subject: [PATCH 20/43] Update function isAutheticated --- Middlwares/auth.middleware.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/Middlwares/auth.middleware.js b/Middlwares/auth.middleware.js index 847bb48..fe6ff70 100644 --- a/Middlwares/auth.middleware.js +++ b/Middlwares/auth.middleware.js @@ -3,7 +3,6 @@ const {verifyToken} = require('../Helpers/JWT'); // Assuming verifyToken functio exports.isAuthenticated = (req, res, next) => { try { const token = req.cookies.tokenAuth || null; - //console.log(token) if (!token) { return res.status(401).json({ message: 'Empty token. Please login to access this resource.' }); } @@ -14,10 +13,8 @@ exports.isAuthenticated = (req, res, next) => { return res.status(401).json({ message: 'Session not found. Please login again.' }); } req.user= verify; - console.log(req.user) next(); } catch (err) { - console.error(err); return res.status(500).json({ message: 'Internal Server Error' }); } } \ No newline at end of file From 8c8627b0e0f90880d38dfbd39c9ccba282b5dc4b Mon Sep 17 00:00:00 2001 From: SaraTahiri Date: Sun, 31 Mar 2024 23:08:41 +0000 Subject: [PATCH 21/43] Update validation function --- Middlwares/validate.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Middlwares/validate.js b/Middlwares/validate.js index 98af5b6..d3be6d4 100644 --- a/Middlwares/validate.js +++ b/Middlwares/validate.js @@ -13,6 +13,12 @@ exports.Validate = [ .withMessage('email is required') .isLength({min:5}) .withMessage('email must be at least 5 characters long'), + //password + body('password') + .notEmpty() + .withMessage('password is required') + .isLength({min:5}) + .withMessage('password must be at least 5 characters long'), // age body('age') .notEmpty() @@ -50,4 +56,5 @@ exports.Validate = [ } next() } -] \ No newline at end of file +] +//add password \ No newline at end of file From e77c39c49c7f64f6cd5422570a4876b859be3b7d Mon Sep 17 00:00:00 2001 From: SaraTahiri Date: Sun, 31 Mar 2024 23:09:20 +0000 Subject: [PATCH 22/43] Update Clients schema --- Modals/Schema/UserSch.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/Modals/Schema/UserSch.js b/Modals/Schema/UserSch.js index 5cb981d..4087821 100644 --- a/Modals/Schema/UserSch.js +++ b/Modals/Schema/UserSch.js @@ -37,6 +37,22 @@ const User = mongoose.Schema({ } }) +/* +User.pre('save', async function(next) { + // Check if password field is modified and is new + if (!this.isModified('password') || !this.isNew) { + return next(); + } + + try { + const hashedPassword = await hashPassword(this.password, 10); + this.password = hashedPassword; + next(); + } catch (error) { + next(error); + } +}); + /* //pre-save to hash password before saving User.pre('save', async function(next) { From 05c0d442cad35fb7216e2f275e563121d44322ae Mon Sep 17 00:00:00 2001 From: SaraTahiri Date: Sun, 31 Mar 2024 23:10:15 +0000 Subject: [PATCH 23/43] Adding route for orders controllers --- Routes/UserRoute.js | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/Routes/UserRoute.js b/Routes/UserRoute.js index 1af08a5..90ad341 100644 --- a/Routes/UserRoute.js +++ b/Routes/UserRoute.js @@ -1,9 +1,9 @@ const express = require('express') -const {renderRegister, +const { registerUser, - renderLogin, - userLogin + userLogin, + userLogout } = require('../Controllers/Clients.Controllers') const {profileUser, @@ -11,6 +11,14 @@ const {profileUser, deleteProfile } = require('../Controllers/Profile.Controllers') +const { + allOrders, + orderDetails, + updateOrder, + deleteOrder, + trackOrder +} = require('../Controllers/Orders.Controllers') + const {Sanitize} = require('../Middlwares/sanitize') const {Validate} = require('../Middlwares/validate') const {isAuthenticated} = require('../Middlwares/auth.middleware') @@ -18,22 +26,34 @@ const routes = express.Router() //Registration routes.route('/register') -.get(renderRegister) .post(Sanitize,Validate,registerUser) //Login routes.route('/login') -.get(renderLogin) .post(userLogin) +//Logout +routes.route('/logout') +.post(userLogout) + //Profile routes.route('/profile') .get(isAuthenticated,profileUser) //Update routes.route('/profile/edit') -.patch(isAuthenticated,UpdateProfile) +.patch(Sanitize,Validate,isAuthenticated,UpdateProfile) //Delete routes.route('/profile/delete') .delete(isAuthenticated,deleteProfile) +//All Orders +routes.get('/orders',allOrders) +//Order details, update and delete +routes.route('/orders/:id') +.get(orderDetails) +.patch(updateOrder) +.delete(deleteOrder) +//Tracking Order +routes.get('/orders/track',trackOrder) + module.exports = routes \ No newline at end of file From 6edc473fb80c3cec5f070366d3c40b4bdd62e310 Mon Sep 17 00:00:00 2001 From: SaraTahiri Date: Sun, 31 Mar 2024 23:11:23 +0000 Subject: [PATCH 24/43] View all orders and details, update, delete and tracking orders --- Controllers/Orders.Controllers.js | 77 +++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 Controllers/Orders.Controllers.js diff --git a/Controllers/Orders.Controllers.js b/Controllers/Orders.Controllers.js new file mode 100644 index 0000000..1142e3a --- /dev/null +++ b/Controllers/Orders.Controllers.js @@ -0,0 +1,77 @@ +const Order = require("../Modals/Schema/OrderSchema") + +//View all orders page +exports.allOrders = async (rea,res) => { + try{ + const orders = await Order.find({}) + res.status(200).json(orders) + }catch(error){ + res.status(500).send('Server Error') + } +} + +//Order details page +exports.orderDetails = async (req,res) => { + try{ + const id = req.params.id + const order = await Order.findById(id) + if(!order){ + res.status(404).send('Order not found') + } + res.status(200).json(order) + }catch(error){ + res.status(500).send('Server Error') + } +} + +//Update order +exports.updateOrder = async (req,res) => { + try { + const id = req.params.id + const updates = req.body + + const updatedOrder = await Order.findByIdAndUpdate(id, updates, { new: true }); + + if (!updatedOrder) { + return res.status(404).json({ message: 'Order not found' }) + } + + res.json(updatedOrder) + } catch (error) { + res.status(500).json('Server error') + } +} + +//Delete Order +exports.deleteOrder = async (req,res) => { + try{ + const id = req.params.id + + const order = await Order.findByIdAndDelete(id) + + if(!order){ + res.status(404).json({message : 'Order not found'}) + } + + res.status(200).json({message : 'Order deleted successfuly'}) + }catch(error){ + res.status(500).json('Server error') + } +} + +//Track orders page +exports.trackOrder = async (req,res) => { + try{ + const {id} = req.query + const order = await Order.findById(id) + if(!order){ + res.status(404).json({message: 'Order not found'}) + } + const trackingInfo = { + status : order.status + } + res.status(200).json(trackingInfo) + }catch(error){ + res.status(500).send('Server Error') + } +} \ No newline at end of file From a5d643fe693b99b8f02dd183e7675587e98464ec Mon Sep 17 00:00:00 2001 From: SaraTahiri Date: Sun, 31 Mar 2024 23:13:20 +0000 Subject: [PATCH 25/43] Add Orders Schema to Models folder --- Modals/Schema/OrderSchema.js | 59 ++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 Modals/Schema/OrderSchema.js diff --git a/Modals/Schema/OrderSchema.js b/Modals/Schema/OrderSchema.js new file mode 100644 index 0000000..a199f79 --- /dev/null +++ b/Modals/Schema/OrderSchema.js @@ -0,0 +1,59 @@ +const mongoose = require('mongoose'); + +// Define schema for order items +const orderItemSchema = new mongoose.Schema({ + name: { + type: String, + required: true + }, + quantity: { + type: Number, + required: true + }, + price: { + type: Number, + required: true + } +}); + +// Define schema for orders +const Order = new mongoose.Schema({ + user: { + type: mongoose.Schema.Types.ObjectId, + ref: 'User', // Reference to the User model + required: true + }, + restaurant: { + type: mongoose.Schema.Types.ObjectId, + ref: 'Restaurant', // Reference to the Restaurant model + required: true + }, + menu : { + type: mongoose.Schema.Types.ObjectId, + ref: 'Menu', // Reference to the Menu model + required: true + }, + items: [orderItemSchema], // Array of order items + status: { + type: String, + enum: ['Pending', 'Processing', 'Shipped', 'Delivered'], + default: 'Pending' + }, + paymentMethod: { + type: String, + enum: ['Credit Card', 'Debit Card', 'PayPal', 'Cash on Delivery'], + required: true + }, + date: { + type: Date, + default: Date.now + }, + createdAt: { + type: Date, + default: Date.now + } +}); + +// export the Order model + +module.exports = mongoose.model('Order', Order); From 804c87b5f16bdd1506d3af87bcb4fc76faddb57c Mon Sep 17 00:00:00 2001 From: SaraTahiri Date: Tue, 2 Apr 2024 10:27:03 +0000 Subject: [PATCH 26/43] Updated order schema --- Modals/Schema/OrderSchema.js | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/Modals/Schema/OrderSchema.js b/Modals/Schema/OrderSchema.js index a199f79..a1aac77 100644 --- a/Modals/Schema/OrderSchema.js +++ b/Modals/Schema/OrderSchema.js @@ -18,7 +18,7 @@ const orderItemSchema = new mongoose.Schema({ // Define schema for orders const Order = new mongoose.Schema({ - user: { + client: { type: mongoose.Schema.Types.ObjectId, ref: 'User', // Reference to the User model required: true @@ -34,16 +34,30 @@ const Order = new mongoose.Schema({ required: true }, items: [orderItemSchema], // Array of order items + deliveryAddress: { + street: { type: String, required: true }, + city: { type: String, required: true }, + state: { type: String }, + postalCode: { type: String, required: true }, + country: { type: String, required: true } + }, status: { type: String, - enum: ['Pending', 'Processing', 'Shipped', 'Delivered'], + enum: ['pending', 'processed', 'cancelled'], default: 'Pending' }, + totalPrice: { + type: Number, + required: true + }, paymentMethod: { type: String, enum: ['Credit Card', 'Debit Card', 'PayPal', 'Cash on Delivery'], required: true }, + notes: { + type: String + }, date: { type: Date, default: Date.now From b6ee04e7ed816700dcca9abe74c1eb651e56eaa3 Mon Sep 17 00:00:00 2001 From: SaraTahiri Date: Tue, 2 Apr 2024 11:56:49 +0000 Subject: [PATCH 27/43] Update clients schema --- Modals/Schema/UserSch.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Modals/Schema/UserSch.js b/Modals/Schema/UserSch.js index 4087821..c18e209 100644 --- a/Modals/Schema/UserSch.js +++ b/Modals/Schema/UserSch.js @@ -18,8 +18,12 @@ const User = mongoose.Schema({ age : { type: 'Number' }, - country : { - type: 'String' + clientAddress: { + street: { type: String, required: true }, + city: { type: String, required: true }, + state: { type: String }, + postalCode: { type: String, required: true }, + country: { type: String, required: true } }, sex : { type: 'String' , From 7f334fb494a718b082bc787514d9a02fb9d462a2 Mon Sep 17 00:00:00 2001 From: SaraTahiri Date: Tue, 2 Apr 2024 14:36:40 +0000 Subject: [PATCH 28/43] View,Update and delete profile --- Controllers/Profile.Controllers.js | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/Controllers/Profile.Controllers.js b/Controllers/Profile.Controllers.js index 536edae..a4bac8a 100644 --- a/Controllers/Profile.Controllers.js +++ b/Controllers/Profile.Controllers.js @@ -6,7 +6,17 @@ exports.profileUser = async (req,res) => { const id = req.user.user._id const user = await User.findById(id) if(!user) return res.status(404).json({message : 'User not found'}) - res.status(200).json(user.username) + + const fields = { + username : user.username, + email : user.email, + age : user.age, + country : user.country, + sex : user.sex, + phoneNumber : user.phoneNumber, + bio : user.bio + } + res.status(200).json(fields) }catch(error){ res.status(500).send('Server Error'); @@ -54,7 +64,6 @@ exports.UpdateProfile = async (req, res) => { res.json({ message: 'Profile updated successfully', - data: updatedUser.username }); } catch (error) { res.status(500).json({ message: 'Server Error' }); @@ -102,7 +111,6 @@ exports.deleteProfile = async (req,res) => { const user = await User.deleteOne({_id : id}) res.json({ message: 'Profile deleted successfully', - data: user }); }catch (err) { res.status(500).json({ message: 'Server error' }); From 6996d2185a51a6427fc137d56dc019de85b341d1 Mon Sep 17 00:00:00 2001 From: SaraTahiri Date: Tue, 2 Apr 2024 14:38:10 +0000 Subject: [PATCH 29/43] View all orders, view details, update, delete and tracking order --- Controllers/Orders.Controllers.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Controllers/Orders.Controllers.js b/Controllers/Orders.Controllers.js index 1142e3a..781c2eb 100644 --- a/Controllers/Orders.Controllers.js +++ b/Controllers/Orders.Controllers.js @@ -18,7 +18,10 @@ exports.orderDetails = async (req,res) => { if(!order){ res.status(404).send('Order not found') } - res.status(200).json(order) + const details = { + //order.(restau, items, deliveryAddress, status, totalPrice, notes) + } + res.status(200).json(details) }catch(error){ res.status(500).send('Server Error') } @@ -35,7 +38,7 @@ exports.updateOrder = async (req,res) => { if (!updatedOrder) { return res.status(404).json({ message: 'Order not found' }) } - + res.json(updatedOrder) } catch (error) { res.status(500).json('Server error') From 0fdd90af8256f8ddb7c50f7e1e26204c2055d29a Mon Sep 17 00:00:00 2001 From: SaraTahiri Date: Tue, 2 Apr 2024 14:40:15 +0000 Subject: [PATCH 30/43] View all orders, view details, update, delete and tracking order --- Controllers/Orders.Controllers.js | 1 + 1 file changed, 1 insertion(+) diff --git a/Controllers/Orders.Controllers.js b/Controllers/Orders.Controllers.js index 781c2eb..34eb95d 100644 --- a/Controllers/Orders.Controllers.js +++ b/Controllers/Orders.Controllers.js @@ -39,6 +39,7 @@ exports.updateOrder = async (req,res) => { return res.status(404).json({ message: 'Order not found' }) } + res.json(updatedOrder) } catch (error) { res.status(500).json('Server error') From 037c9908130da821ea6dd5595978674b7a7113f4 Mon Sep 17 00:00:00 2001 From: SaraTahiri Date: Tue, 2 Apr 2024 14:49:37 +0000 Subject: [PATCH 31/43] View all orders, view details, update, delete and tracking order --- Controllers/Orders.Controllers.js | 1 - 1 file changed, 1 deletion(-) diff --git a/Controllers/Orders.Controllers.js b/Controllers/Orders.Controllers.js index 34eb95d..781c2eb 100644 --- a/Controllers/Orders.Controllers.js +++ b/Controllers/Orders.Controllers.js @@ -39,7 +39,6 @@ exports.updateOrder = async (req,res) => { return res.status(404).json({ message: 'Order not found' }) } - res.json(updatedOrder) } catch (error) { res.status(500).json('Server error') From bca3d736b3a8a99e34022f74ffa9f66d688b1591 Mon Sep 17 00:00:00 2001 From: SaraTahiri Date: Mon, 15 Apr 2024 09:38:25 +0100 Subject: [PATCH 32/43] Add function to verify email --- Controllers/Clients.Controllers.js | 80 ++++++++++++++++++++++-------- 1 file changed, 58 insertions(+), 22 deletions(-) diff --git a/Controllers/Clients.Controllers.js b/Controllers/Clients.Controllers.js index 2aeecc7..604d477 100644 --- a/Controllers/Clients.Controllers.js +++ b/Controllers/Clients.Controllers.js @@ -1,25 +1,19 @@ const User = require('../Modals/Schema/UserSch') +const Token = require('../Modals/Schema/token') +const {HTTP_STATUS_CODES,RESPONSE_MESSAGES} = require('../config/constants') const {sendEmail} = require('../Helpers/mailer') +const {sendEmailVerification} = require('../Helpers/mailverify') const {hashPassword,comparePassword} = require('../Helpers/Hashing') const {generateToken} = require('../Helpers/JWT') +const crypto = require('crypto') -/* -exports.renderRegister = (req,res) => { - res.send('/register') -} - -exports.renderLogin = (req,res) => { - res.send('/login') -} -*/ //Register exports.registerUser = async (req,res) => { try{ - const {username,email,password,age,country,sex,phoneNumber,bio} = req.body - const user = await User.findOne({email}); - //console.log(user) + const {username,email,password,age,clientAddress,country,sex,phoneNumber,bio,verified} = req.body + const user = await User.findOne({email :email}) if(user){ - return res.status(400).json({message: 'User already exists'}) + return res.status(HTTP_STATUS_CODES.BAD_REQUEST).json({message: 'User already exists'}) } const hashedPassword = await hashPassword(password) const newUser = new User({ @@ -27,34 +21,76 @@ exports.registerUser = async (req,res) => { email : email, password : hashedPassword, age , + clientAddress, country , sex , phoneNumber , - bio + bio, + verified }) const result = await newUser.save() - sendEmail(newUser.email,newUser.username) - res.status(200).send('User registration successful') + const token = await new Token({ + userId : newUser._id, + token : crypto.randomBytes(32).toString("hex") + }).save() + const sendTokenMail = await sendEmailVerification(newUser.email,token.userId,token.token) //function to verify email client + if(sendTokenMail){ + sendEmail(newUser.email,newUser.username) + } + res.status(HTTP_STATUS_CODES.OK).send(RESPONSE_MESSAGES.USER_CREATED_SUCCESS) + }catch(error){ + res.status(HTTP_STATUS_CODES.INTERNAL_SERVER_ERROR).send('Server Error'); + } +} + +//To Verify Email Address +exports.verifyEmail = async (req,res) => { + try{ + const token = req.params.token + const userToken = await Token.findOne({ + userId : req.params.id, + token : token + }) + + if(!userToken){ + return res.status(HTTP_STATUS_CODES.BAD_REQUEST).send({message: "Your verification link may have expired."}) + }else{ + const user = await User.findOne({_id : req.params.id}) + if(!user){ + return res.status(HTTP_STATUS_CODES.UNAUTHORIZED).send({message: "We were enable to find a user for this verification.Signup!"}) + }else if(user.verified){ + return res.status(HTTP_STATUS_CODES.OK).send({message: "User has been already verified.Please login"}) + }else{ + const updated = await User.updateOne({verified:true}) + + if(!updated){ + return res.status(HTTP_STATUS_CODES.INTERNAL_SERVER_ERROR).send({message: "error"}) + }else{ + return res.status(HTTP_STATUS_CODES.OK).send({message : "Your account has been successfuly verified"}) + } + } + } }catch(error){ - res.status(500).send('Server Error'); + res.status(HTTP_STATUS_CODES.BAD_REQUEST).send("An error occurred") } } + //Login exports.userLogin = async (req,res) => { try{ const {email,password} = req.body const user = await User.findOne({email}) if(!user){ - return res.status(400).json({message : 'Invalid Credentials'}) + return res.status(HTTP_STATUS_CODES.BAD_REQUEST).json(RESPONSE_MESSAGES.INVALID_CREDENTIALS) } const checked =await comparePassword(password,user.password) - if(!checked) return res.status(400).json({message:"Incorrect Password"}) + if(!checked) return res.status(HTTP_STATUS_CODES.BAD_REQUEST).json({message:"Incorrect Password"}) const token = await generateToken({user}) - res.status(200).cookie('tokenAuth',token).send('User logged successfuly') + res.status(HTTP_STATUS_CODES.OK).cookie('tokenAuth',token).send('User logged successfuly') }catch(error){ - console.log('error' , error) + res.status(HTTP_STATUS_CODES.INTERNAL_SERVER_ERROR).send('Server Error'); } } @@ -65,6 +101,6 @@ exports.userLogout = async (req,res) => { res.clearCookie('tokenAuth') res.send("Logout successfuly") }catch(error){ - res.status(500).send('Server Error') + res.status(HTTP_STATUS_CODES.INTERNAL_SERVER_ERROR).send('Server Error') } } \ No newline at end of file From 16a5d6c88259855e7ebf5845cfe64eae073318b6 Mon Sep 17 00:00:00 2001 From: SaraTahiri Date: Mon, 15 Apr 2024 09:41:27 +0100 Subject: [PATCH 33/43] Modify status and response message --- Controllers/Orders.Controllers.js | 41 ++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/Controllers/Orders.Controllers.js b/Controllers/Orders.Controllers.js index 781c2eb..ba3c995 100644 --- a/Controllers/Orders.Controllers.js +++ b/Controllers/Orders.Controllers.js @@ -1,12 +1,16 @@ const Order = require("../Modals/Schema/OrderSchema") +const {HTTP_STATUS_CODES,RESPONSE_MESSAGES} = require('../config/constants') //View all orders page exports.allOrders = async (rea,res) => { try{ const orders = await Order.find({}) - res.status(200).json(orders) + res.status(HTTP_STATUS_CODES.OK).json({ + message : RESPONSE_MESSAGES.ORDER_CREATED_SUCCESS , + data : orders + }) }catch(error){ - res.status(500).send('Server Error') + res.status(HTTP_STATUS_CODES.INTERNAL_SERVER_ERROR).send(RESPONSE_MESSAGES.INTERNAL_SERVER_ERROR) } } @@ -16,14 +20,18 @@ exports.orderDetails = async (req,res) => { const id = req.params.id const order = await Order.findById(id) if(!order){ - res.status(404).send('Order not found') + res.status(HTTP_STATUS_CODES.NOT_FOUND).send(RESPONSE_MESSAGES.ORDER_NOT_FOUND) } const details = { - //order.(restau, items, deliveryAddress, status, totalPrice, notes) + Restaurant : order.restaurant , + Address : order.deliveryAddress , + Status : order.status , + Total : order.totalPrice , + PayementMethod : order.paymentMethod } - res.status(200).json(details) + res.status(HTTP_STATUS_CODES.OK).json(details) }catch(error){ - res.status(500).send('Server Error') + res.status(HTTP_STATUS_CODES.INTERNAL_SERVER_ERROR).send(RESPONSE_MESSAGES.INTERNAL_SERVER_ERROR) } } @@ -36,12 +44,15 @@ exports.updateOrder = async (req,res) => { const updatedOrder = await Order.findByIdAndUpdate(id, updates, { new: true }); if (!updatedOrder) { - return res.status(404).json({ message: 'Order not found' }) + return res.status(HTTP_STATUS_CODES.NOT_FOUND).json(RESPONSE_MESSAGES.ORDER_NOT_FOUND) } - res.json(updatedOrder) + res.json({ + message: RESPONSE_MESSAGES.ORDER_UPDATED_SUCCESS , + data: updatedOrder + }) } catch (error) { - res.status(500).json('Server error') + res.status(HTTP_STATUS_CODES.INTERNAL_SERVER_ERROR).json(RESPONSE_MESSAGES.INTERNAL_SERVER_ERROR) } } @@ -53,12 +64,12 @@ exports.deleteOrder = async (req,res) => { const order = await Order.findByIdAndDelete(id) if(!order){ - res.status(404).json({message : 'Order not found'}) + res.status(HTTP_STATUS_CODES.NOT_FOUND).json(RESPONSE_MESSAGES.ORDER_NOT_FOUND) } - res.status(200).json({message : 'Order deleted successfuly'}) + res.status(HTTP_STATUS_CODES.OK).json(RESPONSE_MESSAGES.ORDER_CANCELED_SUCCESS) }catch(error){ - res.status(500).json('Server error') + res.status(HTTP_STATUS_CODES.INTERNAL_SERVER_ERROR).json(RESPONSE_MESSAGES.INTERNAL_SERVER_ERROR) } } @@ -68,13 +79,13 @@ exports.trackOrder = async (req,res) => { const {id} = req.query const order = await Order.findById(id) if(!order){ - res.status(404).json({message: 'Order not found'}) + res.status(HTTP_STATUS_CODES.NOT_FOUND).json(RESPONSE_MESSAGES.ORDER_NOT_FOUND) } const trackingInfo = { status : order.status } - res.status(200).json(trackingInfo) + res.status(HTTP_STATUS_CODES.OK).json(trackingInfo) }catch(error){ - res.status(500).send('Server Error') + res.status(HTTP_STATUS_CODES.INTERNAL_SERVER_ERROR).send(RESPONSE_MESSAGES.INTERNAL_SERVER_ERROR) } } \ No newline at end of file From 67f4df535bb086aa207be82912f5e09aad582120 Mon Sep 17 00:00:00 2001 From: SaraTahiri Date: Mon, 15 Apr 2024 09:42:11 +0100 Subject: [PATCH 34/43] Modify status and response message --- Controllers/Profile.Controllers.js | 48 +++++------------------------- 1 file changed, 7 insertions(+), 41 deletions(-) diff --git a/Controllers/Profile.Controllers.js b/Controllers/Profile.Controllers.js index a4bac8a..bdeda06 100644 --- a/Controllers/Profile.Controllers.js +++ b/Controllers/Profile.Controllers.js @@ -1,11 +1,12 @@ const User = require('../Modals/Schema/UserSch') +const {HTTP_STATUS_CODES,RESPONSE_MESSAGES} = require('../config/constants') const {hashPassword,comparePassword} = require('../Helpers/Hashing') exports.profileUser = async (req,res) => { try{ const id = req.user.user._id const user = await User.findById(id) - if(!user) return res.status(404).json({message : 'User not found'}) + if(!user) return res.status(HTTP_STATUS_CODES.NOT_FOUND).json(RESPONSE_MESSAGES.USER_NOT_FOUND) const fields = { username : user.username, @@ -16,10 +17,10 @@ exports.profileUser = async (req,res) => { phoneNumber : user.phoneNumber, bio : user.bio } - res.status(200).json(fields) + res.status(HTTP_STATUS_CODES.OK).json(fields) }catch(error){ - res.status(500).send('Server Error'); + res.status(HTTP_STATUS_CODES.INTERNAL_SERVER_ERROR).send(RESPONSE_MESSAGES.INTERNAL_SERVER_ERROR); } } @@ -31,7 +32,7 @@ exports.UpdateProfile = async (req, res) => { const userfound = await User.findById(id); if (!userfound) { - return res.status(404).json({ message: 'User not found.' }); + return res.status(HTTP_STATUS_CODES.NOT_FOUND).json(RESPONSE_MESSAGES.USER_NOT_FOUND); } if (password) { @@ -66,45 +67,10 @@ exports.UpdateProfile = async (req, res) => { message: 'Profile updated successfully', }); } catch (error) { - res.status(500).json({ message: 'Server Error' }); + res.status(HTTP_STATUS_CODES.INTERNAL_SERVER_ERROR).json(RESPONSE_MESSAGES.INTERNAL_SERVER_ERROR); } } -/* -exports.UpdateProfile = async (req,res) => { - try{ - const id = req.user.user._id - const {username, email, password, age, country, sex, phoneNumber,bio} = req.body; - const userfound = await User.findById(id) - - if (!userfound) { - return res.status(404).json({ message: 'User not found.' }); - } - const check = await comparePassword(password,userfound.password) - if(check){ - return res.json({message : 'Same password'}) - }else{ - const hashedPassword = await hashPassword(password); - const user = await User.updateOne({_id : id}, - { - username, - email, - password : hashedPassword , - age, - country, - sex, - phoneNumber, - bio - }); - res.json({ - data: user - }); - } - }catch(error){ - res.status(500).send('Server Error'); - } -} -*/ exports.deleteProfile = async (req,res) => { try { const id= req.user.user._id @@ -113,6 +79,6 @@ exports.deleteProfile = async (req,res) => { message: 'Profile deleted successfully', }); }catch (err) { - res.status(500).json({ message: 'Server error' }); + res.status(HTTP_STATUS_CODES.INTERNAL_SERVER_ERROR).json(RESPONSE_MESSAGES.INTERNAL_SERVER_ERROR); } } \ No newline at end of file From 1cf8fa47ad26b1ee175b022af127d73cb7889a0f Mon Sep 17 00:00:00 2001 From: SaraTahiri Date: Mon, 15 Apr 2024 09:43:41 +0100 Subject: [PATCH 35/43] Add helpers to verify email --- Helpers/mailverify.js | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 Helpers/mailverify.js diff --git a/Helpers/mailverify.js b/Helpers/mailverify.js new file mode 100644 index 0000000..306d8d5 --- /dev/null +++ b/Helpers/mailverify.js @@ -0,0 +1,31 @@ +const nodemailer = require('nodemailer') +require('dotenv').config() + +exports.sendEmailVerification = async (mail,userId,token) => { + const transporter = nodemailer.createTransport({ + service: "gmail", + auth: { + user: process.env.EMAIL, + pass: process.env.PASSWORD + } + }); + + const mailOptions = { + from: process.env.EMAIL, + to: mail, + subject: "Verify your email address", + text : `Please click the confirmation link. + http://localhost:3000/client/verify/:${userId}/:${token}` + }; + + transporter.sendMail(mailOptions, function(error, info) { + if (error) { + console.log(error); + res.send('Error'); + } else { + console.log("Email sent: " + info.response); + res.send("Success"); + } + }); +} + \ No newline at end of file From 542e961623b34a9b1a2991e05bf115ef2deb80af Mon Sep 17 00:00:00 2001 From: SaraTahiri Date: Mon, 15 Apr 2024 09:44:33 +0100 Subject: [PATCH 36/43] Define schema for token --- Modals/Schema/token.js | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 Modals/Schema/token.js diff --git a/Modals/Schema/token.js b/Modals/Schema/token.js new file mode 100644 index 0000000..69d402d --- /dev/null +++ b/Modals/Schema/token.js @@ -0,0 +1,18 @@ +const mongoose = require('mongoose') +const Schema = mongoose.Schema + + +const tokenSchema = new Schema({ + userId : { + type : Schema.Types.ObjectId, + ref : 'users' , + required : true + }, + token : { + type : 'String' , + required : true + } +}) + +const Token = mongoose.model('token',tokenSchema) +module.exports = Token \ No newline at end of file From 5ff2ac5190ce43e877e9d08a6a40bcd148dab732 Mon Sep 17 00:00:00 2001 From: SaraTahiri Date: Mon, 15 Apr 2024 09:46:00 +0100 Subject: [PATCH 37/43] Add verify, googleId, googleAccessToken to client schema --- Modals/Schema/UserSch.js | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/Modals/Schema/UserSch.js b/Modals/Schema/UserSch.js index c18e209..c285ff4 100644 --- a/Modals/Schema/UserSch.js +++ b/Modals/Schema/UserSch.js @@ -19,11 +19,8 @@ const User = mongoose.Schema({ type: 'Number' }, clientAddress: { - street: { type: String, required: true }, - city: { type: String, required: true }, - state: { type: String }, - postalCode: { type: String, required: true }, - country: { type: String, required: true } + type : 'String', + required : true }, sex : { type: 'String' , @@ -35,6 +32,16 @@ const User = mongoose.Schema({ bio : { type: 'String' }, + verified : { + type : 'Boolean' , + default : false + } , + googleId: { + type: String // Stocker l'ID Google de l'utilisateur + }, + googleAccessToken: { + type: String // Stocker le jeton d'authentification Google de l'utilisateur + }, createdAt: { type: Date, default: Date.now From d31e7b985ec51b66fb7e041bee503aedce687f54 Mon Sep 17 00:00:00 2001 From: SaraTahiri Date: Mon, 15 Apr 2024 09:47:14 +0100 Subject: [PATCH 38/43] Add routes for google oauth2.0 --- Routes/UserRoute.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/Routes/UserRoute.js b/Routes/UserRoute.js index 90ad341..531e96c 100644 --- a/Routes/UserRoute.js +++ b/Routes/UserRoute.js @@ -1,7 +1,9 @@ const express = require('express') +const passport = require('passport') const { registerUser, + verifyEmail, userLogin, userLogout } = require('../Controllers/Clients.Controllers') @@ -24,10 +26,24 @@ const {Validate} = require('../Middlwares/validate') const {isAuthenticated} = require('../Middlwares/auth.middleware') const routes = express.Router() +// Cette route ne sera accessible qu'aux utilisateurs authentifiés via Google OAuth 2.0 +routes.get('/profile', passport.authenticate('google', { session: false }), (req, res) => { + res.send('Bienvenue dans votre profil'); +}) + +// Route de callback pour l'authentification Google OAuth 2.0 +routes.get('/auth/google/callback', passport.authenticate('google', { failureRedirect: '/login' }), (req, res) => { + // Rediriger l'utilisateur vers la page de profil après une authentification réussie + res.redirect('/profile'); +}) + //Registration routes.route('/register') .post(Sanitize,Validate,registerUser) +//Verify email +routes.route('/verify/:id/:token') +.get(verifyEmail) //Login routes.route('/login') .post(userLogin) From ca93f669b65e208eccd42055ecfbde4e1608a7fb Mon Sep 17 00:00:00 2001 From: SaraTahiri Date: Mon, 15 Apr 2024 09:49:29 +0100 Subject: [PATCH 39/43] Add the config file of the server and th database --- config/config.js | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/config/config.js b/config/config.js index e69de29..7e50c97 100644 --- a/config/config.js +++ b/config/config.js @@ -0,0 +1,30 @@ +require('dotenv').config(); +const cloudinary = require('cloudinary').v2; +module.exports = { + server: { + // Configuration for the server + PORT: process.env.PORT || 3000, + DefaultViewEngine:'jade' ,// Define the port number for your server + }, + database: { + // Configuration for the database + uri: process.env.MONGO_URI, // URI for connecting to your MongoDB database + options: { + timestamps: true, // Enable automatic timestamps (createdAt and updatedAt) + versionKey: false, // Disable version key (e.g., __v) + useNewUrlParser: true, // Use the new URL parser for MongoDB + }, + }, + color: { + // Define color codes for console output + green: "\x1b[32m", // Green color + red: "\x1b[31m", // Red color + }, + notFoundTemplate: 404, // A template or value for "not found" responses + jwtSecretKey: process.env.SECRETKEY || 'testsecret', // Secret key for JWT authentication + }; + cloudinary.config({ + cloud_name: process.env.CLOUDINARY_CLOUD_NAME, + api_key: process.env.CLOUDINARY_API_KEY, + api_secret: process.env.CLOUDINARY_API_SECRET, + }); \ No newline at end of file From 23ee522bd9c69984d335c0e01e34c51f55262142 Mon Sep 17 00:00:00 2001 From: SaraTahiri Date: Mon, 15 Apr 2024 09:50:44 +0100 Subject: [PATCH 40/43] Sending config file --- config/constants.js | 76 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/config/constants.js b/config/constants.js index e69de29..0f3656b 100644 --- a/config/constants.js +++ b/config/constants.js @@ -0,0 +1,76 @@ +// HTTP Status Codes +exports.HTTP_STATUS_CODES = { + OK: 200, // Successful response + CREATED: 201, // Resource created + BAD_REQUEST: 400, // Invalid request data + UNAUTHORIZED: 401, // Authentication required + FORBIDDEN: 403, // Access denied + NOT_FOUND: 404, // Resource not found + CONFLICT: 409, // Conflicting resource + INTERNAL_SERVER_ERROR: 500, // Server error + }; + + // Response Messages + exports.RESPONSE_MESSAGES = { + SUCCESS: 'Success', // Generic success message + FAILURE: 'Failure', // Generic failure message + + // Menu + MENU_ITEM_NOT_FOUND: 'Menu item not found', // When a menu item is not found + NO_MENU_ITEMS_AVAILABLE : 'There are currently no menu items available', + MENU_CREATED_SUCCESS: 'Menu item created successfully', // When a menu item is created successfully + MENU_UPDATED_SUCCESS: 'Menu item updated successfully', // When a menu item is updated successfully + MENU_DELETED_SUCCESS: 'Menu item deleted successfully', // When a menu item is deleted successfully + MENU_CREATION_FAILED: 'Failed to create menu item', // When creating a menu item fails + MENU_UPDATE_FAILED: 'Failed to update menu item', // When updating a menu item fails + MENU_DELETION_FAILED: 'Failed to delete menu item', // When deleting a menu item fails + + // Order + ORDER_NOT_FOUND: 'Order not found', // When an order is not found + ORDER_CREATED_SUCCESS: 'Order placed successfully', // When an order is created successfully + ORDER_UPDATED_SUCCESS: 'Order updated successfully', // When an order is updated successfully + ORDER_CANCELED_SUCCESS: 'Order canceled successfully', // When an order is canceled successfully + ORDER_CREATION_FAILED: 'Failed to place order', // When creating an order fails + ORDER_UPDATE_FAILED: 'Failed to update order', // When updating an order fails + ORDER_CANCELLATION_FAILED: 'Failed to cancel order', // When canceling an order fails + + // User + USER_NOT_FOUND: 'User not found', // When a user is not found + USER_CREATED_SUCCESS: 'User registered successfully', // When a user is created successfully + USER_UPDATED_SUCCESS: 'User updated successfully', // When a user is updated successfully + USER_CREATION_FAILED: 'Failed to register user', // When creating a user fails + USER_UPDATE_FAILED: 'Failed to update user', // When updating a user fails + INVALID_CREDENTIALS: 'Invalid credentials', // When credentials (username/email and password) are invalid + + // General + INTERNAL_SERVER_ERROR: 'Internal server error', // When an unexpected server error occurs + VALIDATION_ERROR: 'Validation error', // When input data fails validation + EMPTY_REQUEST: 'Empty request', // When the request body is empty + NOT_FOUND: 'Not found', // When a resource is not found + }; + + // Validation Messages + exports.VALIDATION_MESSAGES = { + // Menu + MENU_NAME_REQUIRED: 'Menu item name is required', // When the menu item name is missing + MENU_DESCRIPTION_REQUIRED: 'Menu item description is required', // When the menu item description is missing + MENU_PRICE_REQUIRED: 'Menu item price is required', // When the menu item price is missing + MENU_CATEGORY_REQUIRED: 'Menu item category is required', // When the menu item category is missing + MENU_IMAGE_REQUIRED: 'Menu item image is required', // When the menu item image is missing + + // Order + ORDER_ITEMS_REQUIRED: 'At least one order item is required', // When an order does not have any items + ORDER_CUSTOMER_REQUIRED: 'Customer information is required', // When customer information is missing for an order + ORDER_PAYMENT_REQUIRED: 'Payment information is required', // When payment information is missing for an order + + // User + USER_NAME_REQUIRED: 'User name is required', // When the user name is missing + USER_EMAIL_REQUIRED: 'User email is required', // When the user email is missing + USER_PASSWORD_REQUIRED: 'User password is required', // When the user password is missing + USER_ADDRESS_REQUIRED: 'User address is required', // When the user address is missing + }; + + // Other Constants + exports.MAX_FILE_SIZE = 5 * 1024 * 1024; // Maximum allowed file size for uploads (5MB) + exports.ALLOWED_IMAGE_FORMATS = ['image/jpeg', 'image/png', 'image/gif']; // Allowed image formats for uploads + \ No newline at end of file From d21d773df5d27cfe910f72a3833973d069d98043 Mon Sep 17 00:00:00 2001 From: SaraTahiri Date: Mon, 15 Apr 2024 09:51:52 +0100 Subject: [PATCH 41/43] Implement MongoDB connection function --- config/database.js | 64 +++++++++++++++++++++++++++++++--------------- 1 file changed, 43 insertions(+), 21 deletions(-) diff --git a/config/database.js b/config/database.js index c86a3c5..a32ff6c 100644 --- a/config/database.js +++ b/config/database.js @@ -1,25 +1,47 @@ -const mongoose = require('mongoose') +//mongodb+srv://sara:M6QL7ZgULnEjtWRm@cluster0.qfvpv3o.mongodb.net/ +const { color, database } = require('./config'); // Import configuration settings +const mongoose = require('mongoose'); exports.connection = () => { - function connectToMongo(){ - mongoose.connect('mongodb+srv://sara:M6QL7ZgULnEjtWRm@cluster0.qfvpv3o.mongodb.net/') - .then(() => console.log('Connected to MongoDB Atlas')) - .catch(err => console.error('Error connecting to MongoDB Atlas', err)); - } - - mongoose.connection.on('connected', () => { - console.log('Mongoose connected to MongoDB Atlas'); - }); - - mongoose.connection.on('error', (err) => { - console.error('Mongoose connection error:', err); - }); - - mongoose.connection.on('disconnected', () => { - console.log('Mongoose disconnected from MongoDB Atlas'); + function connectToMongo() { + // Attempt to connect to the MongoDB database + mongoose.connect(database.uri).then( + () => { + // Connection successful + }, + (err) => { + // Connection error + console.info(color.red, 'Mongodb error', err); + } + ).catch((err) => { + console.log(color.red, 'ERROR:', err); }); + } + + mongoose.connection.on('connected', () => { + // Event: Connected to MongoDB + console.info(color.green, 'Connected to MongoDB ✓'); + + }); + + mongoose.connection.on('reconnected', () => { + // Event: MongoDB reconnected + console.info('MongoDB reconnected!'); + }); + + mongoose.connection.on('error', (error) => { + // Event: Error in MongoDB connection + console.error(color.red, `Error in MongoDB connection: ${error}`); + mongoose.disconnect(); + }); + + mongoose.connection.on('disconnected', () => { + // Event: MongoDB disconnected + console.error(color.red, `MongoDB disconnected! Reconnecting in ${2000 / 1000}s...`); + setTimeout(() => connectToMongo(), 2000); + }); - return{ - connectToMongo, - } -} + return { + connectToMongo, + }; +}; \ No newline at end of file From 248da6240fd4262b7768778c3372cb3c392acfbd Mon Sep 17 00:00:00 2001 From: SaraTahiri Date: Mon, 15 Apr 2024 09:53:08 +0100 Subject: [PATCH 42/43] Add dependencies passport and passport-google-oauth20 --- package.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/package.json b/package.json index fb5eef5..dbb4cdb 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,9 @@ "homepage": "https://github.com/othmanordaski/Foody#readme", "dependencies": { "bcrypt": "^5.1.1", + "cloudinary": "^2.1.0", "cookie-parser": "^1.4.6", + "crypto": "^1.0.1", "dotenv": "^16.4.5", "emailjs": "^4.0.3", "express": "^4.19.2", @@ -30,6 +32,9 @@ "mongoose": "^8.2.3", "multer": "^1.4.5-lts.1", "nodemailer": "^6.9.13", + "passport": "^0.7.0", + "passport-google-oauth20": "^2.0.0", + "uuid": "^9.0.1", "validator": "^13.11.0", "xss": "^1.0.15" }, From 3c3312c527165deba3415c8704f5ef6895b03d43 Mon Sep 17 00:00:00 2001 From: SaraTahiri Date: Mon, 15 Apr 2024 09:53:58 +0100 Subject: [PATCH 43/43] Adding some modification --- server.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/server.js b/server.js index af3d3c5..1e07492 100644 --- a/server.js +++ b/server.js @@ -1,9 +1,14 @@ require('dotenv').config() +const {server} = require('./config/config'); +const passport = require('./config/passport-config') +const PORT = server.PORT + const express = require('express') const app = express() + const cookieParser = require('cookie-parser') -const PORT = process.env.PORT + //Import the database connection function const {connection} = require('./config/database') @@ -14,6 +19,9 @@ app.use(cookieParser()) app.use(express.json()) app.use(express.urlencoded({extended : true})) +// Initialize Passport +app.use(passport.initialize()) + //Connect to the MongoDb databse database.connectToMongo()