diff --git a/.history/server/controllers/productController_20210618082938.js b/.history/server/controllers/productController_20210618082938.js new file mode 100644 index 0000000..04ec95c --- /dev/null +++ b/.history/server/controllers/productController_20210618082938.js @@ -0,0 +1,143 @@ +import asyncHandler from 'express-async-handler'; +import Product from '../models/Product.js'; + +// Get All Products +export const getProducts = asyncHandler(async (req, res) => { + const pageSize = 8; + const page = Number(req.query.pageNumber) || 1; + + const keyword = req.query.keyword + ? { + name: { + $regex: req.query.keyword, + $options: 'i', + }, + } + : {}; + + const count = await Product.countDocuments({ ...keyword }); + const products = await Product.find({ ...keyword }) + .sort([['_id', 'desc']]) + .limit(pageSize) + .skip(pageSize * (page - 1)); + + res.json({ products, page, pages: Math.ceil(count / pageSize) }); +}); + +// Get Product By ID +export const getProductById = asyncHandler(async (req, res) => { + const product = await Product.findById(req.params.id); + res.json(product); +}); + +// Create Product +export const createProduct = asyncHandler(async (req, res) => { + const { + name, + price, + description, + image, + brand, + category, + countInStock, + } = req.body; + + const product = new Product({ + user: req.user._id, + name, + price, + image, + brand, + category, + countInStock, + description, + }); + + const createdProduct = await product.save(); + + res.status(201).json(createdProduct); +}); + +// Update product +export const updateProduct = asyncHandler(async (req, res) => { + const { + name, + price, + description, + image, + brand, + category, + countInStock, + } = req.body; + + const product = await Product.findById(req.params.id); + + if (product) { + product.name = name; + product.price = price; + product.description = description; + product.image = image; + product.brand = brand; + product.category = category; + product.countInStock = countInStock; + + const updatedProduct = await product.save(); + + res.json(updatedProduct); + } else { + res.status(404); + throw new Error('Product not found'); + } +}); + +// Create product review +export const createProductReview = asyncHandler(async (req, res) => { + const { rating, comment } = req.body; + + const product = await Product.findById(req.params.id); + + if (product) { + const alreadyReviewed = product.reviews.find( + r => r.user.toString() === req.user._id.toString() + ); + + if (alreadyReviewed) { + res.status(400); + throw new Error('Product already reviewed'); + } + console.log(req.user); + + const review = { + name: `${req.user.firstName} ${req.user.lastName && req.user.lastName}`, + avatar: req.user.avatar, + comment, + rating: Number(rating), + user: req.user._id, + }; + + product.reviews.push(review); + + product.numReviews = product.reviews.length; + product.rating = + product.reviews.reduce((acc, item) => item.rating + acc, 0) / + product.reviews.length; + + await product.save(); + res.status(201).json({ message: 'Review added' }); + } else { + res.status(404); + throw new Error('Product not found'); + } +}); + +// Delete Product +export const deleteProduct = asyncHandler(async (req, res) => { + const product = await Product.findById(req.params.id); + if (product) { + await product.remove(); + res.json({ message: 'Product Deleted Successfully' }); + } else { + res.status(400); + throw new Error('Product not found'); + } +}); diff --git a/.history/server/controllers/productController_20210618084019.js b/.history/server/controllers/productController_20210618084019.js new file mode 100644 index 0000000..2307289 --- /dev/null +++ b/.history/server/controllers/productController_20210618084019.js @@ -0,0 +1,144 @@ +import asyncHandler from 'express-async-handler'; +import Product from '../models/Product.js'; +import {validationProduct} from '../utils/validationsData'; + +// Get All Products +export const getProducts = asyncHandler(async (req, res) => { + const pageSize = 8; + const page = Number(req.query.pageNumber) || 1; + + const keyword = req.query.keyword + ? { + name: { + $regex: req.query.keyword, + $options: 'i', + }, + } + : {}; + + const count = await Product.countDocuments({ ...keyword }); + const products = await Product.find({ ...keyword }) + .sort([['_id', 'desc']]) + .limit(pageSize) + .skip(pageSize * (page - 1)); + + res.json({ products, page, pages: Math.ceil(count / pageSize) }); +}); + +// Get Product By ID +export const getProductById = asyncHandler(async (req, res) => { + const product = await Product.findById(req.params.id); + res.json(product); +}); + +// Create Product +export const createProduct = asyncHandler(async (req, res) => { + const { + name, + price, + description, + image, + brand, + category, + countInStock, + } = req.body; + + const product = new Product({ + user: req.user._id, + name, + price, + image, + brand, + category, + countInStock, + description, + }); + + const createdProduct = await product.save(); + + res.status(201).json(createdProduct); +}); + +// Update product +export const updateProduct = asyncHandler(async (req, res) => { + const { + name, + price, + description, + image, + brand, + category, + countInStock, + } = req.body; + + const product = await Product.findById(req.params.id); + + if (product) { + product.name = name; + product.price = price; + product.description = description; + product.image = image; + product.brand = brand; + product.category = category; + product.countInStock = countInStock; + + const updatedProduct = await product.save(); + + res.json(updatedProduct); + } else { + res.status(404); + throw new Error('Product not found'); + } +}); + +// Create product review +export const createProductReview = asyncHandler(async (req, res) => { + const { rating, comment } = req.body; + + const product = await Product.findById(req.params.id); + + if (product) { + const alreadyReviewed = product.reviews.find( + r => r.user.toString() === req.user._id.toString() + ); + + if (alreadyReviewed) { + res.status(400); + throw new Error('Product already reviewed'); + } + console.log(req.user); + + const review = { + name: `${req.user.firstName} ${req.user.lastName && req.user.lastName}`, + avatar: req.user.avatar, + comment, + rating: Number(rating), + user: req.user._id, + }; + + product.reviews.push(review); + + product.numReviews = product.reviews.length; + product.rating = + product.reviews.reduce((acc, item) => item.rating + acc, 0) / + product.reviews.length; + + await product.save(); + res.status(201).json({ message: 'Review added' }); + } else { + res.status(404); + throw new Error('Product not found'); + } +}); + +// Delete Product +export const deleteProduct = asyncHandler(async (req, res) => { + const product = await Product.findById(req.params.id); + if (product) { + await product.remove(); + res.json({ message: 'Product Deleted Successfully' }); + } else { + res.status(400); + throw new Error('Product not found'); + } +}); diff --git a/.history/server/controllers/productController_20210618084107.js b/.history/server/controllers/productController_20210618084107.js new file mode 100644 index 0000000..06c0303 --- /dev/null +++ b/.history/server/controllers/productController_20210618084107.js @@ -0,0 +1,148 @@ +import asyncHandler from 'express-async-handler'; +import Product from '../models/Product.js'; +import {validationProduct} from '../utils/validationsData'; + +// Get All Products +export const getProducts = asyncHandler(async (req, res) => { + const pageSize = 8; + const page = Number(req.query.pageNumber) || 1; + + const keyword = req.query.keyword + ? { + name: { + $regex: req.query.keyword, + $options: 'i', + }, + } + : {}; + + const count = await Product.countDocuments({ ...keyword }); + const products = await Product.find({ ...keyword }) + .sort([['_id', 'desc']]) + .limit(pageSize) + .skip(pageSize * (page - 1)); + + res.json({ products, page, pages: Math.ceil(count / pageSize) }); +}); + +// Get Product By ID +export const getProductById = asyncHandler(async (req, res) => { + const product = await Product.findById(req.params.id); + res.json(product); +}); + +// Create Product +export const createProduct = asyncHandler(async (req, res) => { + const {err} = validationProduct(req.body); + if (err) { + return res.status(400).json({messege: err}); + } + const { + name, + price, + description, + image, + brand, + category, + countInStock, + } = req.body; + + const product = new Product({ + user: req.user._id, + name, + price, + image, + brand, + category, + countInStock, + description, + }); + + const createdProduct = await product.save(); + + res.status(201).json(createdProduct); +}); + +// Update product +export const updateProduct = asyncHandler(async (req, res) => { + const { + name, + price, + description, + image, + brand, + category, + countInStock, + } = req.body; + + const product = await Product.findById(req.params.id); + + if (product) { + product.name = name; + product.price = price; + product.description = description; + product.image = image; + product.brand = brand; + product.category = category; + product.countInStock = countInStock; + + const updatedProduct = await product.save(); + + res.json(updatedProduct); + } else { + res.status(404); + throw new Error('Product not found'); + } +}); + +// Create product review +export const createProductReview = asyncHandler(async (req, res) => { + const { rating, comment } = req.body; + + const product = await Product.findById(req.params.id); + + if (product) { + const alreadyReviewed = product.reviews.find( + r => r.user.toString() === req.user._id.toString() + ); + + if (alreadyReviewed) { + res.status(400); + throw new Error('Product already reviewed'); + } + console.log(req.user); + + const review = { + name: `${req.user.firstName} ${req.user.lastName && req.user.lastName}`, + avatar: req.user.avatar, + comment, + rating: Number(rating), + user: req.user._id, + }; + + product.reviews.push(review); + + product.numReviews = product.reviews.length; + product.rating = + product.reviews.reduce((acc, item) => item.rating + acc, 0) / + product.reviews.length; + + await product.save(); + res.status(201).json({ message: 'Review added' }); + } else { + res.status(404); + throw new Error('Product not found'); + } +}); + +// Delete Product +export const deleteProduct = asyncHandler(async (req, res) => { + const product = await Product.findById(req.params.id); + if (product) { + await product.remove(); + res.json({ message: 'Product Deleted Successfully' }); + } else { + res.status(400); + throw new Error('Product not found'); + } +}); diff --git a/.history/server/env_20210418212645.js b/.history/server/env_20210418212645.js new file mode 100644 index 0000000..312f8cd --- /dev/null +++ b/.history/server/env_20210418212645.js @@ -0,0 +1,2 @@ +import dotenv from 'dotenv'; +dotenv.config({ silent: true }); diff --git a/.history/server/env_20210618081741.js b/.history/server/env_20210618081741.js new file mode 100644 index 0000000..2f9fe65 --- /dev/null +++ b/.history/server/env_20210618081741.js @@ -0,0 +1,2 @@ +import dotenv from 'dotenv'; +dotenv.config({ silent: true }); \ No newline at end of file diff --git a/.history/server/utils/validationsData_20210618083205.js b/.history/server/utils/validationsData_20210618083205.js new file mode 100644 index 0000000..e69de29 diff --git a/.history/server/utils/validationsData_20210618083258.js b/.history/server/utils/validationsData_20210618083258.js new file mode 100644 index 0000000..e5bada4 --- /dev/null +++ b/.history/server/utils/validationsData_20210618083258.js @@ -0,0 +1,2 @@ +import Joi from 'joi'; + diff --git a/.history/server/utils/validationsData_20210618083829.js b/.history/server/utils/validationsData_20210618083829.js new file mode 100644 index 0000000..4373220 --- /dev/null +++ b/.history/server/utils/validationsData_20210618083829.js @@ -0,0 +1,13 @@ +import Joi from 'joi'; +import JoiID from 'joi-oid' + +const validationProduct = (data) => { + const sch = Joi.object({ + name: Joi.string().trim().required, + image: Joi.string().trim().required, + brand: Joi.string().trim().required, + description: Joi.string().trim().required, + category: Joi.string().trim().required, + price: Joi.number().trim().required + }) +} \ No newline at end of file diff --git a/.history/server/utils/validationsData_20210618083844.js b/.history/server/utils/validationsData_20210618083844.js new file mode 100644 index 0000000..e7bef33 --- /dev/null +++ b/.history/server/utils/validationsData_20210618083844.js @@ -0,0 +1,14 @@ +import Joi from 'joi'; +import JoiID from 'joi-oid' + +const validationProduct = (data) => { + const sch = Joi.object({ + name: Joi.string().trim().required, + image: Joi.string().trim().required, + brand: Joi.string().trim().required, + description: Joi.string().trim().required, + category: Joi.string().trim().required, + price: Joi.number().trim().required + }) + return sch.validate(data); +} \ No newline at end of file diff --git a/.history/server/utils/validationsData_20210618083850.js b/.history/server/utils/validationsData_20210618083850.js new file mode 100644 index 0000000..b2624c1 --- /dev/null +++ b/.history/server/utils/validationsData_20210618083850.js @@ -0,0 +1,14 @@ +import Joi from 'joi'; +import JoiID from 'joi-oid'; + +const validationProduct = (data) => { + const sch = Joi.object({ + name: Joi.string().trim().required, + image: Joi.string().trim().required, + brand: Joi.string().trim().required, + description: Joi.string().trim().required, + category: Joi.string().trim().required, + price: Joi.number().trim().required + }) + return sch.validate(data); +} \ No newline at end of file diff --git a/.history/server/utils/validationsData_20210618083920.js b/.history/server/utils/validationsData_20210618083920.js new file mode 100644 index 0000000..b2624c1 --- /dev/null +++ b/.history/server/utils/validationsData_20210618083920.js @@ -0,0 +1,14 @@ +import Joi from 'joi'; +import JoiID from 'joi-oid'; + +const validationProduct = (data) => { + const sch = Joi.object({ + name: Joi.string().trim().required, + image: Joi.string().trim().required, + brand: Joi.string().trim().required, + description: Joi.string().trim().required, + category: Joi.string().trim().required, + price: Joi.number().trim().required + }) + return sch.validate(data); +} \ No newline at end of file diff --git a/.history/server/utils/validationsData_20210618083932.js b/.history/server/utils/validationsData_20210618083932.js new file mode 100644 index 0000000..ebdc4db --- /dev/null +++ b/.history/server/utils/validationsData_20210618083932.js @@ -0,0 +1,14 @@ +import Joi from 'joi'; +import JoiID from 'joi-oid'; + +module export const validationProduct = (data) => { + const sch = Joi.object({ + name: Joi.string().trim().required, + image: Joi.string().trim().required, + brand: Joi.string().trim().required, + description: Joi.string().trim().required, + category: Joi.string().trim().required, + price: Joi.number().trim().required + }) + return sch.validate(data); +} \ No newline at end of file diff --git a/.history/server/utils/validationsData_20210618083934.js b/.history/server/utils/validationsData_20210618083934.js new file mode 100644 index 0000000..3637840 --- /dev/null +++ b/.history/server/utils/validationsData_20210618083934.js @@ -0,0 +1,14 @@ +import Joi from 'joi'; +import JoiID from 'joi-oid'; + +module.export const validationProduct = (data) => { + const sch = Joi.object({ + name: Joi.string().trim().required, + image: Joi.string().trim().required, + brand: Joi.string().trim().required, + description: Joi.string().trim().required, + category: Joi.string().trim().required, + price: Joi.number().trim().required + }) + return sch.validate(data); +} \ No newline at end of file diff --git a/.history/server/utils/validationsData_20210618083940.js b/.history/server/utils/validationsData_20210618083940.js new file mode 100644 index 0000000..f7c4f19 --- /dev/null +++ b/.history/server/utils/validationsData_20210618083940.js @@ -0,0 +1,14 @@ +import Joi from 'joi'; +import JoiID from 'joi-oid'; + +export const validationProduct = (data) => { + const sch = Joi.object({ + name: Joi.string().trim().required, + image: Joi.string().trim().required, + brand: Joi.string().trim().required, + description: Joi.string().trim().required, + category: Joi.string().trim().required, + price: Joi.number().trim().required + }) + return sch.validate(data); +} \ No newline at end of file diff --git a/client/package.json b/client/package.json index 12fd92c..707a6e6 100644 --- a/client/package.json +++ b/client/package.json @@ -67,4 +67,4 @@ "postcss": "^7.0.35", "tailwindcss": "npm:@tailwindcss/postcss7-compat@^2.0.4" } -} \ No newline at end of file +} diff --git a/package.json b/package.json index 0c0766b..89275c4 100644 --- a/package.json +++ b/package.json @@ -30,11 +30,14 @@ "express-fileupload": "^1.2.1", "firebase-admin": "^9.6.0", "googleapis": "^70.0.0", + "joi": "^17.4.0", + "joi-oid": "^1.1.2", "jsonwebtoken": "^8.5.1", "mongoose": "^5.12.3", "morgan": "^1.10.0", "node-fetch": "^2.6.1", "nodemailer": "^6.5.0", + "nodemon": "^2.0.7", "stripe": "^8.142.0", "uuid": "^8.3.2" } diff --git a/server/config/db.js b/server/config/db.js index 80e4db1..f839235 100644 --- a/server/config/db.js +++ b/server/config/db.js @@ -2,7 +2,7 @@ import mongoose from 'mongoose'; const connectDB = async () => { try { - const connect = await mongoose.connect(process.env.MONGO_URI, { + const connect = await mongoose.connect("mongodb://localhost:27017/?readPreference=primary&appname=MongoDB%20Compass&ssl=false", { useCreateIndex: true, useFindAndModify: false, useNewUrlParser: true, diff --git a/server/controllers/productController.js b/server/controllers/productController.js index 04ec95c..06c0303 100644 --- a/server/controllers/productController.js +++ b/server/controllers/productController.js @@ -1,5 +1,6 @@ import asyncHandler from 'express-async-handler'; import Product from '../models/Product.js'; +import {validationProduct} from '../utils/validationsData'; // Get All Products export const getProducts = asyncHandler(async (req, res) => { @@ -32,6 +33,10 @@ export const getProductById = asyncHandler(async (req, res) => { // Create Product export const createProduct = asyncHandler(async (req, res) => { + const {err} = validationProduct(req.body); + if (err) { + return res.status(400).json({messege: err}); + } const { name, price, diff --git a/server/env.js b/server/env.js index 312f8cd..2f9fe65 100644 --- a/server/env.js +++ b/server/env.js @@ -1,2 +1,2 @@ import dotenv from 'dotenv'; -dotenv.config({ silent: true }); +dotenv.config({ silent: true }); \ No newline at end of file diff --git a/server/utils/validationsData.js b/server/utils/validationsData.js new file mode 100644 index 0000000..f7c4f19 --- /dev/null +++ b/server/utils/validationsData.js @@ -0,0 +1,14 @@ +import Joi from 'joi'; +import JoiID from 'joi-oid'; + +export const validationProduct = (data) => { + const sch = Joi.object({ + name: Joi.string().trim().required, + image: Joi.string().trim().required, + brand: Joi.string().trim().required, + description: Joi.string().trim().required, + category: Joi.string().trim().required, + price: Joi.number().trim().required + }) + return sch.validate(data); +} \ No newline at end of file