diff --git a/backend/controllers/userController.js b/backend/controllers/userController.js index 24ddfa96..3e484371 100644 --- a/backend/controllers/userController.js +++ b/backend/controllers/userController.js @@ -89,13 +89,49 @@ export const logoutUser = (_req, res) => { res.status(200).json({ message: 'Logged out successfully' }) } +export const updateUserProfile = async (req, res, next) => { + try { + const user = await User.findById(req.user._id) + + if (user) { + user.name = req.body.name || user.name + user.email = req.body.email || user.email + user.password = req.body.password || user.password + user.defaultAddress = req.body.defaultAddress || user.defaultAddress + user.contactNumber = req.body.contactNumber || user.contact + // user.picture = req.body.picture || user.picture + // user.role = user.role; + // user.password = req.body.password || user.password + + if (req.body.password) { + user.password = req.body.password + } + + const updatedUser = await user.save() + + res.status(201).json({ + message: 'User updated successfully', + _id: updatedUser._id, + name: updatedUser.name, + email: updatedUser.email, + role: updatedUser.role, + contactNumber: updatedUser.contactNumber + }) + } else { + res.status(404) + throw new Error('User not found') + } + } catch (error) { + return next(error) + } +} + // @desc Get user by ID // @route GET /api/users/:id // @access Private/Admin export const getUserById = async (req, res, next) => { if (req.user.role !== 'admin') { - res.status(403) - throw new Error('Unauthorized access') + return res.status(403).json('Unauthorized') } try { @@ -104,8 +140,7 @@ export const getUserById = async (req, res, next) => { if (user) { res.json(user); } else { - res.status(404); - throw new Error('User not found'); + return res.status(404).json('User not found') } } catch (error) { next(error) @@ -133,19 +168,18 @@ export const getUserProfile = async (req, res) => { // @desc Send Verify Email // @route GET /api/users/verify // @access Private - -export const sendVerifyEmail = async (user) => { - - const token = tokenToVerify(user.email); - const body = { - from: `'FarmCart 🌱' <${process.env.EMAIL_USER}>`, - to: `${user.email}`, - subject: 'FarmCart: Email Activation', - html: ` +export const sendVerifyEmail = async (user, res) => { + try { + const token = tokenToVerify(user.email); + const body = { + from: `'FarmCart 🌱' <${process.env.EMAIL_USER}>`, + to: `${user.email}`, + subject: 'FarmCart: Email Activation', + html: `

Hello ${user.name},

Thank you for signing up with FarmCart. Please verify your email address to complete your registration.

-

This link will expire in 2 days.

+

This link will expire in 2 minutes.

Click the button below to activate your account:

Verify Account @@ -154,10 +188,16 @@ export const sendVerifyEmail = async (user) => {

The FarmCart Team

`, - }; + }; - const message = 'Please check your email to verify!'; - sendEmail(body, message); + const message = 'Please check your email to verify!'; + await sendEmail(body, message); + return res.status(200).json({ success: true, message }); + } catch (error) { + console.error(`Error in sending verification email: ${error.message}`); + res.status(500) + throw new Error(`Error in sending verification email`) + } } export const verifyEmail = async (req, res) => { @@ -182,4 +222,47 @@ export const verifyEmail = async (req, res) => { } catch (error) { return res.status(400).json({ success: false, message: error.message }) } -} \ No newline at end of file +} + +// @desc Send Password Reset Email +// @route GET /api/users/forgot-password +// @access Private + +export const forgotPassword = async (req, res) => { + try { + const isAdded = await User.findOne({ email: req.body.verifyEmail }) + if (!isAdded) { + return res.status(404).json({ success: false, message: 'No user found with this email' }) + } + + const token = await tokenToVerify(isAdded.email) + + const body = { + from: `'FarmCart 🌱' <${process.env.EMAIL_USER}>`, + to: `${req.body.verifyEmail}`, + subject: 'FarmCart: Password Reset', + html: `

Hello ${req.body.verifyEmail}

+

A request has been received to change the password for your FarmCart account

+ +

This link will expire in 15 minutes.

+ +

Click this link for reset your password

+ + Reset Password +

If you did not initiate this request, please contact us immediately at support@farmcart.com

+

Thank you

+ FarmCart Team + `, + }; + + const message = 'Please check your email to reset your password!'; + await sendEmail(body); + + return res.status(200).json({ success: true, message }); + } catch (error) { + console.error(`Error in forgotPassword: ${error.message}`); + res.status(500) + throw new Error('Internal server error'); + } +} diff --git a/backend/middlewares/asyncHandler.js b/backend/middlewares/asyncHandler.js new file mode 100644 index 00000000..1e7b079b --- /dev/null +++ b/backend/middlewares/asyncHandler.js @@ -0,0 +1,5 @@ +const asyncHandler = fn => (req, res, next) => { + Promise.resolve(fn(req, res, next)).catch(next); +} + +export default asyncHandler; \ No newline at end of file diff --git a/backend/middlewares/authMiddleware.js b/backend/middlewares/authMiddleware.js index f911ac58..31b07f1e 100644 --- a/backend/middlewares/authMiddleware.js +++ b/backend/middlewares/authMiddleware.js @@ -1,6 +1,5 @@ import jwt from 'jsonwebtoken' -import CustomError from '../utils/customError.js' -import User from '../model/userModel.js' +import User from '../models/userModel.js' const protect = async (req, res, next) => { let token = req.cookies.jwt @@ -12,16 +11,19 @@ const protect = async (req, res, next) => { req.user = await User.findById(decoded.userId).select('-password') if (!req.user) { - throw new CustomError('User not found', 403) + res.status(403) + throw new Error('User not found') } next() } else { - throw new CustomError('Not authorized. No token provided', 401) + res.status(401) + throw new Error('Not authorized. No token provided', 401) } } catch (error) { if (error instanceof jwt.JsonWebTokenError) { - return next(new CustomError('Invalid token signature', 401)) + res.status(401) + return next(new Error('Invalid token signature')) } next(error) } diff --git a/backend/middlewares/errorMiddleware.js b/backend/middlewares/errorMiddleware.js index 1c7cf909..035d246e 100644 --- a/backend/middlewares/errorMiddleware.js +++ b/backend/middlewares/errorMiddleware.js @@ -1,13 +1,25 @@ +const notFound = (req, res, next) => { + const error = new Error(`Not Found -${req.originalUrl}`); + res.status(404); + next(error); +}; + const errorHandler = (err, _req, res, next) => { let statusCode = res.statusCode === 200 ? 500 : res.statusCode let message = err.message + //check for Mongoose bad Object + if(err.name === 'CastError' && err.kind === 'ObjectId') { + message = `Resource not founded`; + statusCode = 404; + } + res.status(statusCode).json({ - message: message, - stack: process.env.NODE_ENV === 'production' ? null : err.stack, + message, + stack: process.env.NODE_ENV === 'production' ? 'null' : err.stack, }) next() } -export { errorHandler } +export { errorHandler, notFound } diff --git a/backend/routes/userRoute.js b/backend/routes/userRoute.js index 9d8857c6..5028a73d 100644 --- a/backend/routes/userRoute.js +++ b/backend/routes/userRoute.js @@ -1,10 +1,15 @@ import express from 'express' -import { authUser, logoutUser, registerUser, verifyEmail } from '../controllers/userController.js' +import { authUser, forgotPassword, getUserById, logoutUser, registerUser, updateUserProfile, verifyEmail } from '../controllers/userController.js' +import protect from '../middlewares/authMiddleware.js' const router = express.Router() -router.route('').post(registerUser) +router.route('').post(registerUser).put(protect, updateUserProfile) router.route('/auth').post(authUser) -router.route('/logout').post(logoutUser) +router.route('/logout').post(protect, logoutUser) router.route('/verify').get(verifyEmail) +router.route('/forgot-password').post(forgotPassword) + +router.route('/:id').get(protect, getUserById) + export default router diff --git a/backend/server.js b/backend/server.js index e7f14649..845bb81f 100644 --- a/backend/server.js +++ b/backend/server.js @@ -3,7 +3,7 @@ import cors from 'cors' import cookieParser from 'cookie-parser' import connectDB from './config/db.js' import userRoute from './routes/userRoute.js' -import { errorHandler } from './middlewares/errorMiddleware.js' +import { errorHandler, notFound } from './middlewares/errorMiddleware.js' // load environment variables const PORT = process.env.PORT || 8000 @@ -21,17 +21,24 @@ app.use(express.urlencoded({ extended: true })) app.use(cookieParser()) // routes +app.get('/', (_req, res) => { + res.send('FarmCart API is Running...') +}) + +// User API routes app.use('/api/users', userRoute) -app.all('*', (_req, res) => { - res.status(404).json({ - message: 'Page not found', - statusCode: 404, - }) -}) +// Shop API routes +// app.use('/api/shops', shopRoute); +// app.use('/api/shops', productRoutes) + +// Middleware to handle 404 errors (route not found) +app.use(notFound) +// Middleware to handle errors and send appropriate responses app.use(errorHandler) +// Start the server and listen on the specified port app.listen(PORT, () => { console.log(`Server currently is running on port ${PORT}`) }).on('error', (error) => { diff --git a/backend/utils/generateToken.js b/backend/utils/generateToken.js index cb12a59c..0aaa50dd 100644 --- a/backend/utils/generateToken.js +++ b/backend/utils/generateToken.js @@ -16,7 +16,7 @@ export const generateToken = (res, userId) => { export const tokenToVerify = (email) => { return jwt.sign({ email }, process.env.JWT_SECRET,{ - expiresIn: '48h' + expiresIn: '15m' }); }; diff --git a/backend/utils/sendEmail.js b/backend/utils/sendEmail.js index 387f0954..dff4fde0 100644 --- a/backend/utils/sendEmail.js +++ b/backend/utils/sendEmail.js @@ -11,6 +11,7 @@ export const sendEmail = async (body) => { }); await transporter.sendMail(body); + return { success: true, message: 'Email sent successfully' }; } catch (err) { return { success: false, message: `Error sending email: ${err.message}` };