diff --git a/backend/user/controller/user-controller.js b/backend/user/controller/user-controller.js index 01b3d24312..8008244619 100644 --- a/backend/user/controller/user-controller.js +++ b/backend/user/controller/user-controller.js @@ -52,11 +52,18 @@ export async function createUser(req, res) { try { const { username, email, password } = req.body; if (username && email && password) { - const existingUser = await _findUserByUsernameOrEmail(username, email); - if (existingUser) { + const existingUsername = await _findUserByUsername(username); + if (existingUsername) { return res .status(409) - .json({ message: "username or email already exists" }); + .json({ message: "username already exists" }); + } + + const existingEmail = await _findUserByEmail(email); + if (existingEmail) { + return res + .status(409) + .json({ message: "email already exists" }); } const salt = bcrypt.genSaltSync(10); @@ -166,6 +173,11 @@ export async function updateUser(req, res) { hashedPassword = bcrypt.hashSync(password, salt); } + let isVerified = user.isVerified; + if (email && email !== user.email) { + isVerified = false; + } + const updatedUser = await _updateUserById( userId, username, @@ -174,7 +186,9 @@ export async function updateUser(req, res) { bio, linkedin, github, - profilePictureUrl + profilePictureUrl, + isVerified, + user.verificationCode, ); return res.status(200).json({ message: `Updated data for user ${userId}`, @@ -183,7 +197,7 @@ export async function updateUser(req, res) { } else { return res.status(400).json({ message: - "No field to update: username and email and password are all missing!", + "No field to update!", }); } } catch (err) { @@ -251,7 +265,54 @@ export async function deleteUser(req, res) { } } -// send email to user +export async function requestVerificationEmail(req, res) { + try { + const { email } = req.body; + if (email) { + const user = await _findUserByEmail(email); + if (!user) { + return res + .status(404) + .json({ message: `User not found with email ${email}` }); + } + + // For email verification, you can generate a random verification code and save it in the database + const verificationCode = + Math.random().toString(36).substring(2, 15) + + Math.random().toString(36).substring(2, 15); + + const updatedUser = await _updateUserById( + user.id, + user.username, + user.email, + user.password, + user.bio, + user.linkedin, + user.github, + user.profilePictureUrl, + user.isVerified, + verificationCode + ); + + // Send email to user + await sendVerificationEmail(email, user.username, verificationCode); + + return res + .status(200) + .json({ + message: `Sent verification email to user ${user.username}`, + }); + } else { + return res.status(400).json({ message: "email is missing!" }); + } + } catch (err) { + console.error(err); + return res + .status(500) + .json({ message: "Unknown error when requesting verification email!" }); + } +} + export async function verifyUser(req, res) { try { const { code } = req.query; @@ -367,7 +428,10 @@ export async function resetPasswordUsingCode(req, res) { hashedPassword, user.bio, user.linkedin, - user.github + user.github, + user.profilePictureUrl, + user.isVerified, + user.verificationCode ); return res.status(200).json({ message: `Reset password for user ${user.username}`, diff --git a/backend/user/model/repository.js b/backend/user/model/repository.js index 0d1b69caf3..6bebcf3fb8 100644 --- a/backend/user/model/repository.js +++ b/backend/user/model/repository.js @@ -53,7 +53,9 @@ export async function updateUserById( bio, linkedin, github, - profilePictureUrl + profilePictureUrl, + isVerified, + verificationCode ) { return UserModel.findByIdAndUpdate( userId, @@ -66,6 +68,8 @@ export async function updateUserById( linkedin, github, profilePictureUrl, + isVerified, + verificationCode }, }, { new: true } // return the updated user diff --git a/backend/user/routes/user-routes.js b/backend/user/routes/user-routes.js index 837bec6977..e461f83b8a 100644 --- a/backend/user/routes/user-routes.js +++ b/backend/user/routes/user-routes.js @@ -11,6 +11,7 @@ import { updateUser, updateUserPrivilege, getFileUrl, + requestVerificationEmail, verifyUser, } from "../controller/user-controller.js"; import { @@ -30,6 +31,8 @@ router.patch( updateUserPrivilege ); +router.post("/request-verification-email", requestVerificationEmail); + router.get("/verify", verifyUser); router.post("/request-password-reset", requestPasswordReset); diff --git a/frontend/src/api/user.tsx b/frontend/src/api/user.tsx index 3715b43468..3b3b46cfc1 100644 --- a/frontend/src/api/user.tsx +++ b/frontend/src/api/user.tsx @@ -330,3 +330,44 @@ export const resetPasswordWithCode = async (code: string, password: string) => { return true; }; + +export const requestVerificationEmail = async (email: string) => { + if (!email) { + toast.fire({ + icon: "error", + title: "Please provide an email", + }); + return false; + } + + toast.fire({ + icon: "info", + title: "Requesting verification email...", + }); + const response = await fetch( + `${NEXT_PUBLIC_IAM_USER_SERVICE}/request-verification-email`, + { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ email }), + } + ); + const data = await response.json(); + + if (response.status !== 200) { + toast.fire({ + icon: "error", + title: data.message, + }); + return false; + } + + toast.fire({ + icon: "success", + title: "Verification email sent. Please check your email.", + }); + + return true; +} \ No newline at end of file diff --git a/frontend/src/app/(user)/user/[id]/page.tsx b/frontend/src/app/(user)/user/[id]/page.tsx index 6d1c0c6236..ebb5efaa0d 100644 --- a/frontend/src/app/(user)/user/[id]/page.tsx +++ b/frontend/src/app/(user)/user/[id]/page.tsx @@ -29,26 +29,38 @@ const ProfilePage = () => { }; return ( -
{user?.email}
-{user?.email}
+