From 1c1a00ae28d743b0bc2b2e14a21130ce257a7105 Mon Sep 17 00:00:00 2001 From: Son Nguyen Date: Sat, 23 Nov 2024 15:33:02 -0500 Subject: [PATCH] Update: Added nodemon and SwaggerJS --- backend/docs/swagger.js | 17 +++-- backend/routes/auth.js | 116 +++++++++++++------------------ src/components/NavigationBar.jsx | 29 ++------ src/components/ProductCard.jsx | 6 +- src/pages/ForgotPassword.jsx | 6 +- src/pages/Home.jsx | 9 +-- src/pages/Login.jsx | 41 +++-------- src/pages/ProductDetails.jsx | 4 +- src/pages/Register.jsx | 63 ++++------------- src/pages/ResetPassword.jsx | 46 +++--------- src/pages/Shop.jsx | 23 ++---- 11 files changed, 111 insertions(+), 249 deletions(-) diff --git a/backend/docs/swagger.js b/backend/docs/swagger.js index 7a79906..615b1ba 100644 --- a/backend/docs/swagger.js +++ b/backend/docs/swagger.js @@ -7,7 +7,8 @@ const swaggerDefinition = { info: { title: 'Fusion E-Commerce Backend APIs', // API title version: '1.1.0', // API version - description: 'API documentation for the Fusion E-Commerce backend server. This documentation provides detailed information on all available endpoints for managing products, users, authentication, and more.', + description: + 'API documentation for the Fusion E-Commerce backend server. This documentation provides detailed information on all available endpoints for managing products, users, authentication, and more.', termsOfService: 'https://mern-stack-ecommerce-app-nine.vercel.app', contact: { name: 'Fusion E-Commerce Website', @@ -27,7 +28,7 @@ const swaggerDefinition = { { url: 'http://localhost:8000', description: 'Development server', - } + }, ], components: { securitySchemes: { @@ -148,10 +149,14 @@ const options = { // Initialize swagger-jsdoc const swaggerSpec = swaggerJSDoc(options); -const setupSwaggerUi = (app) => { - app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerSpec, { - customSiteTitle: 'Fusion E-Commerce API Docs', - })); +const setupSwaggerUi = app => { + app.use( + '/api-docs', + swaggerUi.serve, + swaggerUi.setup(swaggerSpec, { + customSiteTitle: 'Fusion E-Commerce API Docs', + }) + ); }; module.exports = { diff --git a/backend/routes/auth.js b/backend/routes/auth.js index 034d4be..fb6789e 100644 --- a/backend/routes/auth.js +++ b/backend/routes/auth.js @@ -84,7 +84,7 @@ router.post( [ check('name', 'Name is required').not().isEmpty(), check('email', 'Please include a valid email').isEmail(), - check('password', 'Please enter a password with 6 or more characters').isLength({ min: 6 }) + check('password', 'Please enter a password with 6 or more characters').isLength({ min: 6 }), ], async (req, res) => { const errors = validationResult(req); @@ -103,7 +103,7 @@ router.post( user = new User({ name, email, - password + password, }); const salt = await bcrypt.genSalt(10); @@ -113,8 +113,8 @@ router.post( const payload = { user: { - id: user.id - } + id: user.id, + }, }; jwt.sign(payload, JWT_SECRET, { expiresIn: '48h' }, (err, token) => { @@ -166,49 +166,42 @@ router.post( * 500: * description: Server error */ -router.post( - '/login', - [ - check('email', 'Please include a valid email').isEmail(), - check('password', 'Password is required').exists() - ], - async (req, res) => { - const errors = validationResult(req); - - if (!errors.isEmpty()) { - return res.status(400).json({ errors: errors.array() }); - } +router.post('/login', [check('email', 'Please include a valid email').isEmail(), check('password', 'Password is required').exists()], async (req, res) => { + const errors = validationResult(req); - const { email, password } = req.body; - - try { - let user = await User.findOne({ email }); + if (!errors.isEmpty()) { + return res.status(400).json({ errors: errors.array() }); + } - if (!user) { - return res.status(400).json({ msg: 'Invalid credentials' }); - } + const { email, password } = req.body; - const isMatch = await bcrypt.compare(password, user.password); - if (!isMatch) { - return res.status(400).json({ msg: 'Invalid credentials' }); - } + try { + let user = await User.findOne({ email }); - const payload = { - user: { - id: user.id - } - }; + if (!user) { + return res.status(400).json({ msg: 'Invalid credentials' }); + } - jwt.sign(payload, JWT_SECRET, { expiresIn: '48h' }, (err, token) => { - if (err) throw err; - res.json({ token }); - }); - } catch (err) { - console.error(err.message); - res.status(500).send('Server error'); + const isMatch = await bcrypt.compare(password, user.password); + if (!isMatch) { + return res.status(400).json({ msg: 'Invalid credentials' }); } + + const payload = { + user: { + id: user.id, + }, + }; + + jwt.sign(payload, JWT_SECRET, { expiresIn: '48h' }, (err, token) => { + if (err) throw err; + res.json({ token }); + }); + } catch (err) { + console.error(err.message); + res.status(500).send('Server error'); } -); +}); /** * @swagger @@ -236,34 +229,28 @@ router.post( * 500: * description: Server error */ -router.post( - '/verify-email', - [ - check('email', 'Please include a valid email').isEmail() - ], - async (req, res) => { - const errors = validationResult(req); +router.post('/verify-email', [check('email', 'Please include a valid email').isEmail()], async (req, res) => { + const errors = validationResult(req); - if (!errors.isEmpty()) { - return res.status(400).json({ errors: errors.array() }); - } + if (!errors.isEmpty()) { + return res.status(400).json({ errors: errors.array() }); + } - const { email } = req.body; + const { email } = req.body; - try { - let user = await User.findOne({ email }); + try { + let user = await User.findOne({ email }); - if (!user) { - return res.status(400).json({ msg: 'Invalid email. User not found' }); - } - - res.json({ msg: 'Email is valid, you can proceed to reset your password' }); - } catch (err) { - console.error(err.message); - res.status(500).send('Server error'); + if (!user) { + return res.status(400).json({ msg: 'Invalid email. User not found' }); } + + res.json({ msg: 'Email is valid, you can proceed to reset your password' }); + } catch (err) { + console.error(err.message); + res.status(500).send('Server error'); } -); +}); /** * @swagger @@ -297,10 +284,7 @@ router.post( */ router.post( '/reset-password', - [ - check('email', 'Please include a valid email').isEmail(), - check('password', 'Please enter a password with 6 or more characters').isLength({ min: 6 }) - ], + [check('email', 'Please include a valid email').isEmail(), check('password', 'Please enter a password with 6 or more characters').isLength({ min: 6 })], async (req, res) => { const errors = validationResult(req); diff --git a/src/components/NavigationBar.jsx b/src/components/NavigationBar.jsx index 82ca297..1c17865 100644 --- a/src/components/NavigationBar.jsx +++ b/src/components/NavigationBar.jsx @@ -58,7 +58,7 @@ function NavigationBar({ cartItemCount }) { // Debounced function to prevent triggering the search too often const debouncedSearch = React.useCallback( - debounce(async (query) => { + debounce(async query => { if (query.trim() === '') { setSearchResults([]); // Clear search results if the query is empty setLoading(false); @@ -80,7 +80,7 @@ function NavigationBar({ cartItemCount }) { // Event listener to hide search results if clicking outside search bar or results React.useEffect(() => { - const handleClickOutside = (event) => { + const handleClickOutside = event => { // Check if click is outside search bar and search results if ( searchBarRef.current && @@ -206,30 +206,17 @@ function NavigationBar({ cartItemCount }) { {/* Login/Logout and Register */} {isLoggedIn ? ( - ) : ( <> - )} - @@ -257,11 +244,7 @@ function NavigationBar({ cartItemCount }) { width: '300px', // Specify a custom width, no need to match the search bar }} > - + )} diff --git a/src/components/ProductCard.jsx b/src/components/ProductCard.jsx index a235839..1d3eb3e 100644 --- a/src/components/ProductCard.jsx +++ b/src/components/ProductCard.jsx @@ -14,7 +14,7 @@ export default function ProductCard({ product, addToCart }) { navigate(`/product/${product._id}`); }; - const handleCardClick = (event) => { + const handleCardClick = event => { // Prevent navigation if the "Add to Cart" button is clicked if (event.target.tagName !== 'BUTTON') { handleViewDetails(); @@ -55,7 +55,7 @@ export default function ProductCard({ product, addToCart }) {