From db2fb2185995194d7f5af253f6dc8fb449a69f97 Mon Sep 17 00:00:00 2001 From: jkarenzi Date: Wed, 17 Jul 2024 18:21:07 +0200 Subject: [PATCH] fix(profile-update): return new token upon profile update - return new token upon profile update - implement change full name endpoint [Fixes #!7] --- __tests__/userController.test.ts | 4 +-- src/controllers/authController.ts | 7 +++-- src/controllers/userController.ts | 42 +++++++++++++++++++++---- src/middleware/validators/userSchema.ts | 13 +++++++- src/routes/userRoutes.ts | 4 ++- 5 files changed, 57 insertions(+), 13 deletions(-) diff --git a/__tests__/userController.test.ts b/__tests__/userController.test.ts index 075f65d..356e53e 100644 --- a/__tests__/userController.test.ts +++ b/__tests__/userController.test.ts @@ -114,7 +114,7 @@ describe('User Controller tests', () => { expect(res.status).toBe(200); expect(res.body.message).toBe('Email successfully updated'); - expect(res.body.data.email).toBe('new@example.com'); + expect(res.body.data.token).toBeDefined(); }); it('should return 400 if email validation fails', async () => { @@ -214,6 +214,6 @@ describe('User Controller tests', () => { .delete('/api/users/profileImg') .set('Authorization', `Bearer ${token}`) - expect(res.status).toBe(204); + expect(res.status).toBe(200); }) }); \ No newline at end of file diff --git a/src/controllers/authController.ts b/src/controllers/authController.ts index 5b8381e..15cc6a7 100644 --- a/src/controllers/authController.ts +++ b/src/controllers/authController.ts @@ -175,7 +175,7 @@ const toggleTwoFactorAuth = errorHandler(async (req:Request, res:Response) => { const formData = req.body const validationResult = updateTwoFactorAuthSchema.validate(formData); - + if (validationResult.error) { return res .status(400) @@ -185,9 +185,10 @@ const toggleTwoFactorAuth = errorHandler(async (req:Request, res:Response) => { }); } - await User.findByIdAndUpdate(userId, {'twoFactorAuth.isEnabled':formData.status},{new:true}) + const updatedUser = await User.findByIdAndUpdate(userId, {'twoFactorAuth.isEnabled':formData.status},{new:true}) + const token = await jwt.sign({ user:updatedUser }, jwtSecret, { expiresIn: '1h' }); - return res.status(200).json({status:'success',message:'Update successful'}) + return res.status(200).json({status:'success',message:'Update successful', data:{token}}) }) module.exports = { diff --git a/src/controllers/userController.ts b/src/controllers/userController.ts index 07fde9f..b1793e4 100644 --- a/src/controllers/userController.ts +++ b/src/controllers/userController.ts @@ -1,9 +1,15 @@ import { Request, Response } from 'express'; +import { IUser } from '../custom'; const { errorHandler } = require('../middleware/errorHandler'); const User = require('../models/User') const bcrypt = require('bcrypt') -const {updatePasswordSchema, updateEmailSchema} = require('../middleware/validators/userSchema') +const {updatePasswordSchema, updateEmailSchema, updateFullNameSchema} = require('../middleware/validators/userSchema') const cloudinary = require('../middleware/cloudinary') +const jwt = require('jsonwebtoken') +require('dotenv').config() + + +const jwtSecret = process.env.JWT_SECRET interface cloudinaryUploadResult { public_id: string, @@ -72,9 +78,27 @@ const changeEmail = errorHandler(async (req: Request, res: Response) => { } const updatedDoc = await User.findByIdAndUpdate(userId,{email:formData.newEmail},{new:true}) - return res.status(200).json({status:'success', message: 'Email successfully updated', data:{email:updatedDoc.email}}) + const token = await jwt.sign({ user:updatedDoc }, jwtSecret, { expiresIn: '1h' }); + return res.status(200).json({status:'success', message: 'Email successfully updated', data:{token}}) }); +const changeFullName = errorHandler(async (req: Request, res: Response) => { + const formData = req.body + const userId = req.user!._id + + const validationResult = updateFullNameSchema.validate(formData) + + if(validationResult.error){ + return res.status(400).json({status:'error', message:validationResult.error.details[0].message}) + } + + const updatedUser = await User.findByIdAndUpdate(userId,{fullName:formData.fullName},{new:true}) + const token = await jwt.sign({ user:updatedUser }, jwtSecret, { expiresIn: '1h' }); + + return res.status(200).json({status:'success', data:{token}}) + +}) + const changeProfileImg = errorHandler(async (req: Request, res: Response) => { const userId = req.user!._id const user = await User.findOne({_id:userId}) @@ -108,7 +132,9 @@ const changeProfileImg = errorHandler(async (req: Request, res: Response) => { {new:true} ) - return res.status(200).json({status:'success', message:'Profile Image successfully updated', data:{profileImg:updatedUser.profileImg}}) + const token = await jwt.sign({ user:updatedUser }, jwtSecret, { expiresIn: '1h' }); + + return res.status(200).json({status:'success', message:'Profile Image successfully updated', data:{token}}) }); const removeProfileImg = errorHandler(async (req: Request, res: Response) => { @@ -121,18 +147,21 @@ const removeProfileImg = errorHandler(async (req: Request, res: Response) => { const result = await cloudinary.uploader.destroy(user.profileImg.publicId,{invalidate:true}) + let updatedUser: IUser; if(result.result === 'ok'){ - await User.findByIdAndUpdate( + updatedUser = await User.findByIdAndUpdate( user._id, { profileImg: { publicId: 'default', url: process.env.DEFAULT_PROFILE_IMG } - } + }, + {new:true} ) - return res.status(204).json({}) + const token = await jwt.sign({ user:updatedUser }, jwtSecret, { expiresIn: '1h' }); + return res.status(200).json({status:'success', message:"Profile image successfully removed", data:{token}}) }else{ return res.status(400).json({status:'error', message:'An error occured. Try again later'}) } @@ -151,6 +180,7 @@ const deleteUser = errorHandler(async (req: Request, res: Response) => { module.exports = { getAllUsers, getUser, + changeFullName, changeEmail, changePassword, changeProfileImg, diff --git a/src/middleware/validators/userSchema.ts b/src/middleware/validators/userSchema.ts index a0f73c2..31603ea 100644 --- a/src/middleware/validators/userSchema.ts +++ b/src/middleware/validators/userSchema.ts @@ -26,7 +26,18 @@ const updateEmailSchema = Joi.object({ newEmail: Joi.string().email().required(), }) +const updateFullNameSchema = Joi.object({ + fullName: Joi.string() + .regex(/^[A-Za-z\s]{5,}$/) + .message( + 'fullName can only contain letters and should have atleast 5 characters' + ) + .required() +}) + + module.exports = { updateEmailSchema, - updatePasswordSchema + updatePasswordSchema, + updateFullNameSchema } \ No newline at end of file diff --git a/src/routes/userRoutes.ts b/src/routes/userRoutes.ts index 2f3f445..3e9b56c 100644 --- a/src/routes/userRoutes.ts +++ b/src/routes/userRoutes.ts @@ -5,7 +5,8 @@ const { changePassword, changeProfileImg, removeProfileImg, - deleteUser + deleteUser, + changeFullName } = require('../controllers/userController') import { Router } from 'express'; const authenticateToken = require('../middleware/authenticateToken') @@ -16,6 +17,7 @@ const userRouter = Router(); userRouter.use(authenticateToken) +userRouter.patch('/fullName', changeFullName) userRouter.patch('/email', changeEmail) userRouter.patch('/password', changePassword) userRouter.route('/profileImg').patch(upload.fields([{name:'image'}]), changeProfileImg).delete(removeProfileImg)