solution#272
Conversation
mateacademy-ai-mentor
left a comment
There was a problem hiding this comment.
Review Summary
Your implementation has several critical blockers that must be addressed before approval:
High Priority Issues:
-
Missing
sendNotificationmethod inemail.service.js- This is called byuserService.forgotPassword(line 86) andupdateEmail(line 119) but doesn't exist. It will cause runtime errors during password reset and email change flows. -
Missing
getProfilemethod inuser.service.js- The controller callsuserService.getProfile(userId)but this method is never implemented. -
Password reset flow is incomplete - While
forgotPasswordexists in the service, there's no/forgot-passwordroute to trigger it, no/reset-password/:tokenendpoint to actually reset the password with token validation, and no controller methods for either. The task explicitly requires: email input → email sent page → reset confirmation page with password/confirmation validation → success page. -
Missing
updateNamefunctionality - The profile page requires name change capability (updateNamemethod in service, controller method, and PUT/update-nameroute), but none exist. -
Missing confirmation field validation - Password update requires
oldPassword,newPassword, andconfirmationfields (confirmation must equal newPassword). Email update requirespassword,newEmail, andconfirmEmailfields. These validations are missing from both routes and services.
Security Issue:
- SQL injection vulnerability in
src/index.js- Using template literals withprocess.env.DB_NAMEdirectly in SQL queries. Use parameterized queries instead.
The implementation covers core registration, login, and activation flows well, but the missing password reset endpoints, getProfile method, sendNotification method, and confirmation validations are explicit requirements from the task checklist that must be implemented.
✨ Please rate this review in the Platform's notification center1. I will be grateful for your feedback! ✨
Important
Fix the issues, commit the changes, and then re-request my review using the Re-request review button2 in the Reviewers tab.
Tip
If you have any comments about this review, please leave a message in the Add a comment section below and mention me with the @mateacademy-ai-mentor tag3.
I will read your message during my next review of your pull request. Please do not reply directly to my comments, as that feature is not supported yet.
Caution
Do not close the pull request until the review is complete. Closing it early can lead to unexpected behavior and disrupt the review process.
Footnotes
|
|
||
| app.use( | ||
| cors({ | ||
| credentials: true, |
There was a problem hiding this comment.
SQL injection vulnerability. Using template literals with process.env.DB_NAME directly in SQL queries. Use parameterized queries instead.
| origin: true, | ||
| }), | ||
| ); | ||
|
|
There was a problem hiding this comment.
SQL injection vulnerability. Using template literals with process.env.DB_NAME directly in SQL query. Use parameterized queries instead.
| } | ||
|
|
||
| async getProfile(req, res) { | ||
| try { |
There was a problem hiding this comment.
The controller calls userService.getProfile(req.user.userId) but this method is not implemented in user.service.js. The service only exports registration, activate, login, forgotPassword, updatePassword, and updateEmail. A getProfile method is needed to fetch user data by ID.
| @@ -0,0 +1,97 @@ | |||
| import userService from '../services/user.service.js'; | |||
There was a problem hiding this comment.
The task requires password reset functionality with: 1) Ask for email, 2) Show email sent page, 3) Reset password confirmation page with password and confirmation fields that must be equal, 4) Show success page with link to login. None of these endpoints exist - missing /forgot-password and /reset-password/:token routes.
|
|
||
| async updateEmail(req, res) { | ||
| try { | ||
| const { password, newEmail } = req.body; |
There was a problem hiding this comment.
userService.forgotPassword calls emailService.sendNotification but this method does not exist in email.service.js. Only sendActivationMail is implemented. This will cause a runtime error.
|
|
||
| class UserService { | ||
| async registration(name, email, password) { | ||
| const candidate = await User.findOne({ where: { email } }); | ||
|
|
||
| if (candidate) { | ||
| throw new Error(`Пользователь с email ${email} уже существует`); | ||
| } | ||
|
|
||
| const hashPassword = await bcrypt.hash(password, 10); | ||
| const activationToken = uuidv4(); | ||
|
|
||
| const user = await User.create({ | ||
| name, | ||
| email, | ||
| password: hashPassword, | ||
| activationToken, | ||
| }); | ||
|
|
||
| const activationLink = `${process.env.API_URL}/api/activate/${activationToken}`; | ||
|
|
||
| await emailService.sendActivationMail(email, activationLink); | ||
|
|
||
| return { | ||
| user: { id: user.id, email: user.email, name: user.name }, | ||
| }; | ||
| } | ||
|
|
||
| async activate(token) { | ||
| const user = await User.findOne({ where: { activationToken: token } }); | ||
|
|
||
| if (!user) { | ||
| throw new Error('Некорректная ссылка активации'); | ||
| } | ||
|
|
||
| user.isActive = true; | ||
| user.activationToken = null; | ||
| await user.save(); | ||
|
|
||
| return tokenService.generateToken({ userId: user.id }); | ||
| } | ||
|
|
||
| async login(email, password) { | ||
| const user = await User.findOne({ where: { email } }); | ||
|
|
||
| if (!user) { | ||
| throw new Error('Пользователь не найден'); | ||
| } | ||
|
|
||
| if (!user.isActive) { | ||
| throw new Error('Аккаунт не подтвержден. Проверьте почту'); | ||
| } | ||
|
|
||
| const isPassEquals = await bcrypt.compare(password, user.password); | ||
|
|
||
| if (!isPassEquals) { | ||
| throw new Error('Неверный пароль'); | ||
| } | ||
|
|
||
| const token = tokenService.generateToken({ userId: user.id }); | ||
|
|
||
| return { | ||
| token, | ||
| user: { id: user.id, email: user.email, name: user.name }, | ||
| }; | ||
| } | ||
|
|
||
| async forgotPassword(email) { | ||
| const user = await User.findOne({ where: { email } }); | ||
|
|
||
| if (!user) { | ||
| throw new Error('Пользователь не найден'); | ||
| } | ||
|
|
||
| const resetToken = uuidv4(); | ||
|
|
||
| user.resetToken = resetToken; | ||
| await user.save(); | ||
|
|
||
| const resetLink = `${process.env.CLIENT_URL}/reset-password/${resetToken}`; | ||
|
|
||
| await emailService.sendNotification( | ||
| email, | ||
| `Ссылка для сброса пароля: ${resetLink}`, | ||
| ); | ||
| } | ||
|
|
||
| async updatePassword(userId, oldPassword, newPassword) { | ||
| const user = await User.findByPk(userId); | ||
| const isPassEquals = await bcrypt.compare(oldPassword, user.password); | ||
|
|
||
| if (!isPassEquals) { | ||
| throw new Error('Старый пароль неверный'); | ||
| } | ||
|
|
||
| user.password = await bcrypt.hash(newPassword, 10); | ||
| await user.save(); | ||
| } | ||
|
|
||
| async updateEmail(userId, password, newEmail) { | ||
| const user = await User.findByPk(userId); | ||
| const isPassEquals = await bcrypt.compare(password, user.password); | ||
|
|
||
| if (!isPassEquals) { | ||
| throw new Error('Неверный пароль'); | ||
| } | ||
|
|
||
| const oldEmail = user.email; | ||
| const activationToken = uuidv4(); | ||
|
|
||
| user.pendingEmail = newEmail; | ||
| user.activationToken = activationToken; | ||
| await user.save(); | ||
|
|
||
| await emailService.sendNotification( | ||
| oldEmail, | ||
| `Ваш email меняется на ${newEmail}. Если это не вы — смените пароль.`, | ||
| ); | ||
|
|
||
| const link = `${process.env.API_URL}/api/activate-new-email/${activationToken}`; | ||
|
|
||
| await emailService.sendActivationMail(newEmail, link); | ||
| } | ||
| } | ||
|
|
||
| export default new UserService(); |
There was a problem hiding this comment.
The controller calls userService.getProfile(userId) but this method is not implemented in user.service.js. You need to add a getProfile method that retrieves user data by ID.
|
|
||
| class UserService { | ||
| async registration(name, email, password) { | ||
| const candidate = await User.findOne({ where: { email } }); | ||
|
|
||
| if (candidate) { | ||
| throw new Error(`Пользователь с email ${email} уже существует`); | ||
| } | ||
|
|
||
| const hashPassword = await bcrypt.hash(password, 10); | ||
| const activationToken = uuidv4(); | ||
|
|
||
| const user = await User.create({ | ||
| name, | ||
| email, | ||
| password: hashPassword, | ||
| activationToken, | ||
| }); | ||
|
|
||
| const activationLink = `${process.env.API_URL}/api/activate/${activationToken}`; | ||
|
|
||
| await emailService.sendActivationMail(email, activationLink); | ||
|
|
||
| return { | ||
| user: { id: user.id, email: user.email, name: user.name }, | ||
| }; | ||
| } | ||
|
|
||
| async activate(token) { | ||
| const user = await User.findOne({ where: { activationToken: token } }); | ||
|
|
||
| if (!user) { | ||
| throw new Error('Некорректная ссылка активации'); | ||
| } | ||
|
|
||
| user.isActive = true; | ||
| user.activationToken = null; | ||
| await user.save(); | ||
|
|
||
| return tokenService.generateToken({ userId: user.id }); | ||
| } | ||
|
|
||
| async login(email, password) { | ||
| const user = await User.findOne({ where: { email } }); | ||
|
|
||
| if (!user) { | ||
| throw new Error('Пользователь не найден'); | ||
| } | ||
|
|
||
| if (!user.isActive) { | ||
| throw new Error('Аккаунт не подтвержден. Проверьте почту'); | ||
| } | ||
|
|
||
| const isPassEquals = await bcrypt.compare(password, user.password); | ||
|
|
||
| if (!isPassEquals) { | ||
| throw new Error('Неверный пароль'); | ||
| } | ||
|
|
||
| const token = tokenService.generateToken({ userId: user.id }); | ||
|
|
||
| return { | ||
| token, | ||
| user: { id: user.id, email: user.email, name: user.name }, | ||
| }; | ||
| } | ||
|
|
||
| async forgotPassword(email) { | ||
| const user = await User.findOne({ where: { email } }); | ||
|
|
||
| if (!user) { | ||
| throw new Error('Пользователь не найден'); | ||
| } | ||
|
|
||
| const resetToken = uuidv4(); | ||
|
|
||
| user.resetToken = resetToken; | ||
| await user.save(); | ||
|
|
||
| const resetLink = `${process.env.CLIENT_URL}/reset-password/${resetToken}`; | ||
|
|
||
| await emailService.sendNotification( | ||
| email, | ||
| `Ссылка для сброса пароля: ${resetLink}`, | ||
| ); | ||
| } | ||
|
|
||
| async updatePassword(userId, oldPassword, newPassword) { | ||
| const user = await User.findByPk(userId); | ||
| const isPassEquals = await bcrypt.compare(oldPassword, user.password); | ||
|
|
||
| if (!isPassEquals) { | ||
| throw new Error('Старый пароль неверный'); | ||
| } | ||
|
|
||
| user.password = await bcrypt.hash(newPassword, 10); | ||
| await user.save(); | ||
| } | ||
|
|
||
| async updateEmail(userId, password, newEmail) { | ||
| const user = await User.findByPk(userId); | ||
| const isPassEquals = await bcrypt.compare(password, user.password); | ||
|
|
||
| if (!isPassEquals) { | ||
| throw new Error('Неверный пароль'); | ||
| } | ||
|
|
||
| const oldEmail = user.email; | ||
| const activationToken = uuidv4(); | ||
|
|
||
| user.pendingEmail = newEmail; | ||
| user.activationToken = activationToken; | ||
| await user.save(); | ||
|
|
||
| await emailService.sendNotification( | ||
| oldEmail, | ||
| `Ваш email меняется на ${newEmail}. Если это не вы — смените пароль.`, | ||
| ); | ||
|
|
||
| const link = `${process.env.API_URL}/api/activate-new-email/${activationToken}`; | ||
|
|
||
| await emailService.sendActivationMail(newEmail, link); | ||
| } | ||
| } | ||
|
|
||
| export default new UserService(); |
There was a problem hiding this comment.
The task requires 'Profile page (only authenticated) - You can change a name'. The updateName method is not implemented. Add a method to update the user's name in the database.
|
|
||
| async forgotPassword(email) { | ||
| const user = await User.findOne({ where: { email } }); | ||
|
|
||
| if (!user) { | ||
| throw new Error('Пользователь не найден'); | ||
| } | ||
|
|
||
| const resetToken = uuidv4(); | ||
|
|
||
| user.resetToken = resetToken; | ||
| await user.save(); | ||
|
|
||
| const resetLink = `${process.env.CLIENT_URL}/reset-password/${resetToken}`; | ||
|
|
||
| await emailService.sendNotification( | ||
| email, | ||
| `Ссылка для сброса пароля: ${resetLink}`, | ||
| ); |
There was a problem hiding this comment.
The password reset flow is incomplete. forgotPassword creates a resetToken (line 79) but there's no method to validate this token and actually reset the password. Per requirements: 'Reset Password confirmation page (with password and confirmation fields that must be equal)'. Add a resetPassword method that takes a token and new password, validates the token, and updates the password.
|
|
||
| async forgotPassword(email) { | ||
| const user = await User.findOne({ where: { email } }); | ||
|
|
||
| if (!user) { | ||
| throw new Error('Пользователь не найден'); | ||
| } | ||
|
|
||
| const resetToken = uuidv4(); | ||
|
|
||
| user.resetToken = resetToken; | ||
| await user.save(); | ||
|
|
||
| const resetLink = `${process.env.CLIENT_URL}/reset-password/${resetToken}`; | ||
|
|
||
| await emailService.sendNotification( | ||
| email, | ||
| `Ссылка для сброса пароля: ${resetLink}`, | ||
| ); |
There was a problem hiding this comment.
The password confirmation validation (checking equality between password and confirmation) is not implemented. Per requirements: 'Reset Password confirmation page (with password and confirmation fields that must be equal)'. This check should happen in the reset-password route/controller OR in the service method.
| import nodemailer from 'nodemailer'; | ||
|
|
||
| class MailService { | ||
| constructor() { | ||
| this.transporter = nodemailer.createTransport({ | ||
| host: process.env.SMTP_HOST, | ||
| port: process.env.SMTP_PORT, | ||
| secure: false, | ||
| auth: { | ||
| user: process.env.SMTP_USER, | ||
| pass: process.env.SMTP_PASSWORD, | ||
| }, | ||
| }); | ||
| } | ||
|
|
||
| async sendActivationMail(to, link) { | ||
| await this.transporter.sendMail({ | ||
| from: process.env.SMTP_USER, | ||
| to, | ||
| subject: 'Активация аккаунта', | ||
| text: '', | ||
| html: `<div><h1>Для активации перейдите по ссылке</h1><a href="${link}">${link}</a></div>`, | ||
| }); | ||
| } | ||
| } | ||
|
|
||
| export default new MailService(); |
There was a problem hiding this comment.
The sendNotification method is called in user.service.js (lines 86-89 and 119-122) but is not defined in email.service.js. This will cause runtime errors when users request password reset or change their email.
mateacademy-ai-mentor
left a comment
There was a problem hiding this comment.
Review Summary
Your implementation has solid progress in the service layer, but critical functionality remains incomplete and cannot be approved:
Must Fix (Critical Blockers):
-
Missing password reset flow endpoints - The service methods
forgotPasswordandresetPasswordexist but have no controller methods or routes. You need:POST /forgot-passwordroute withforgotPasswordcontroller methodPOST /reset-password/:tokenroute withresetPasswordcontroller method
-
Missing
updateNameendpoint -userService.updateNameexists but there's noupdateNamecontroller method orPUT /update-nameroute -
Missing confirmation field validations - The task explicitly requires:
- For password update:
oldPassword,newPassword, ANDconfirmation(whereconfirmation === newPassword) - For email update:
password,newEmail, ANDconfirmEmail(whereconfirmEmail === newEmail)
- For password update:
-
SQL injection vulnerability in
init_db.js- Using template literals withprocess.env.DB_NAMEdirectly in SQL queries. Use parameterized queries:client.query('SELECT 1 FROM pg_database WHERE datname = $1', [process.env.DB_NAME])
Note: The ESLint failure is a configuration issue (.eslintrc.js with CommonJS syntax while package.json has "type": "module"), not your code. Rename .eslintrc.js to .eslintrc.cjs to fix.
The password reset flow and confirmation validations are explicit task requirements listed in the checklist. Complete these before resubmission.
✨ Please rate this review in the Platform's notification center1. I will be grateful for your feedback! ✨
Important
Fix the issues, commit the changes, and then re-request my review using the Re-request review button2 in the Reviewers tab.
Tip
If you have any comments about this review, please leave a message in the Add a comment section below and mention me with the @mateacademy-ai-mentor tag3.
I will read your message during my next review of your pull request. Please do not reply directly to my comments, as that feature is not supported yet.
Caution
Do not close the pull request until the review is complete. Closing it early can lead to unexpected behavior and disrupt the review process.
Footnotes
| import userService from '../services/user.service.js'; | ||
|
|
||
| class UserController { | ||
| async registration(req, res) { | ||
| try { | ||
| const { name, email, password } = req.body; | ||
| const userData = await userService.registration(name, email, password); | ||
|
|
||
| return res.json(userData); | ||
| } catch (e) { | ||
| res.status(400).json({ message: e.message }); | ||
| } | ||
| } | ||
|
|
||
| async login(req, res) { | ||
| try { | ||
| const { email, password } = req.body; | ||
| const userData = await userService.login(email, password); | ||
|
|
||
| res.cookie('token', userData.token, { | ||
| maxAge: 24 * 60 * 60 * 1000, | ||
| httpOnly: true, | ||
| sameSite: 'lax', | ||
| }); | ||
|
|
||
| return res.json(userData); | ||
| } catch (e) { | ||
| res.status(401).json({ message: e.message }); | ||
| } | ||
| } | ||
|
|
||
| async activate(req, res) { | ||
| try { | ||
| const { token } = req.params; | ||
| const jwtToken = await userService.activate(token); | ||
|
|
||
| res.cookie('token', jwtToken, { | ||
| maxAge: 24 * 60 * 60 * 1000, | ||
| httpOnly: true, | ||
| }); | ||
|
|
||
| return res.redirect(`${process.env.CLIENT_URL}/profile?activated=true`); | ||
| } catch (e) { | ||
| res.status(400).json({ message: e.message }); | ||
| } | ||
| } | ||
|
|
||
| async logout(req, res) { | ||
| try { | ||
| res.clearCookie('token'); |
There was a problem hiding this comment.
Missing route for name update - the updateName method exists in user.service.js (lines 89-98) but there's no PUT /update-name route with authMiddleware to expose it. Add: router.put('/update-name', authMiddleware, body('name').notEmpty()..., userController.updateName)
| import userService from '../services/user.service.js'; | ||
|
|
||
| class UserController { | ||
| async registration(req, res) { | ||
| try { | ||
| const { name, email, password } = req.body; | ||
| const userData = await userService.registration(name, email, password); | ||
|
|
||
| return res.json(userData); | ||
| } catch (e) { | ||
| res.status(400).json({ message: e.message }); | ||
| } | ||
| } | ||
|
|
||
| async login(req, res) { | ||
| try { | ||
| const { email, password } = req.body; | ||
| const userData = await userService.login(email, password); | ||
|
|
||
| res.cookie('token', userData.token, { | ||
| maxAge: 24 * 60 * 60 * 1000, | ||
| httpOnly: true, | ||
| sameSite: 'lax', | ||
| }); | ||
|
|
||
| return res.json(userData); | ||
| } catch (e) { | ||
| res.status(401).json({ message: e.message }); | ||
| } | ||
| } | ||
|
|
||
| async activate(req, res) { | ||
| try { | ||
| const { token } = req.params; | ||
| const jwtToken = await userService.activate(token); | ||
|
|
||
| res.cookie('token', jwtToken, { | ||
| maxAge: 24 * 60 * 60 * 1000, | ||
| httpOnly: true, | ||
| }); | ||
|
|
||
| return res.redirect(`${process.env.CLIENT_URL}/profile?activated=true`); | ||
| } catch (e) { | ||
| res.status(400).json({ message: e.message }); | ||
| } | ||
| } | ||
|
|
||
| async logout(req, res) { | ||
| try { | ||
| res.clearCookie('token'); |
There was a problem hiding this comment.
Missing forgotPassword route - the forgotPassword method exists in user.service.js (lines 91-107) but there's no route/controller method to trigger it. This is required for Step 1 of the password reset flow (email input form).
| import userService from '../services/user.service.js'; | ||
|
|
||
| class UserController { | ||
| async registration(req, res) { | ||
| try { | ||
| const { name, email, password } = req.body; | ||
| const userData = await userService.registration(name, email, password); | ||
|
|
||
| return res.json(userData); | ||
| } catch (e) { | ||
| res.status(400).json({ message: e.message }); | ||
| } | ||
| } | ||
|
|
||
| async login(req, res) { | ||
| try { | ||
| const { email, password } = req.body; | ||
| const userData = await userService.login(email, password); | ||
|
|
||
| res.cookie('token', userData.token, { | ||
| maxAge: 24 * 60 * 60 * 1000, | ||
| httpOnly: true, | ||
| sameSite: 'lax', | ||
| }); | ||
|
|
||
| return res.json(userData); | ||
| } catch (e) { | ||
| res.status(401).json({ message: e.message }); | ||
| } | ||
| } | ||
|
|
||
| async activate(req, res) { | ||
| try { | ||
| const { token } = req.params; | ||
| const jwtToken = await userService.activate(token); | ||
|
|
||
| res.cookie('token', jwtToken, { | ||
| maxAge: 24 * 60 * 60 * 1000, | ||
| httpOnly: true, | ||
| }); | ||
|
|
||
| return res.redirect(`${process.env.CLIENT_URL}/profile?activated=true`); | ||
| } catch (e) { | ||
| res.status(400).json({ message: e.message }); | ||
| } | ||
| } | ||
|
|
||
| async logout(req, res) { | ||
| try { | ||
| res.clearCookie('token'); |
There was a problem hiding this comment.
Missing resetPassword route - the resetPassword method exists in user.service.js (lines 109-119) but there's no route to handle the token-based password reset (e.g., POST /reset-password/:token). This is required for Step 3 of the password reset flow (reset confirmation page). The route should validate that password and confirmation fields are equal before calling the service.
|
|
||
| return res.redirect(`${process.env.CLIENT_URL}/profile?activated=true`); | ||
| } catch (e) { | ||
| res.status(400).json({ message: e.message }); | ||
| } |
There was a problem hiding this comment.
Missing confirmation field validation for password update - the task requires: new password AND confirmation (must equal new password). Currently only validates oldPassword and newPassword. Add: body('confirmation').equals(req.body.newPassword).withMessage('Пароли не совпадают')
| import userService from '../services/user.service.js'; | ||
|
|
||
| class UserController { | ||
| async registration(req, res) { | ||
| try { | ||
| const { name, email, password } = req.body; | ||
| const userData = await userService.registration(name, email, password); | ||
|
|
||
| return res.json(userData); | ||
| } catch (e) { | ||
| res.status(400).json({ message: e.message }); | ||
| } | ||
| } | ||
|
|
||
| async login(req, res) { | ||
| try { | ||
| const { email, password } = req.body; | ||
| const userData = await userService.login(email, password); | ||
|
|
||
| res.cookie('token', userData.token, { | ||
| maxAge: 24 * 60 * 60 * 1000, | ||
| httpOnly: true, | ||
| sameSite: 'lax', | ||
| }); | ||
|
|
||
| return res.json(userData); | ||
| } catch (e) { | ||
| res.status(401).json({ message: e.message }); | ||
| } | ||
| } | ||
|
|
||
| async activate(req, res) { | ||
| try { | ||
| const { token } = req.params; | ||
| const jwtToken = await userService.activate(token); | ||
|
|
||
| res.cookie('token', jwtToken, { | ||
| maxAge: 24 * 60 * 60 * 1000, | ||
| httpOnly: true, | ||
| }); | ||
|
|
||
| return res.redirect(`${process.env.CLIENT_URL}/profile?activated=true`); | ||
| } catch (e) { | ||
| res.status(400).json({ message: e.message }); | ||
| } | ||
| } | ||
|
|
||
| async logout(req, res) { | ||
| try { | ||
| res.clearCookie('token'); | ||
|
|
||
| return res.json({ message: 'Вышли из системы' }); | ||
| } catch (e) { | ||
| res.status(500).json({ message: 'Ошибка логаута' }); | ||
| } | ||
| } | ||
|
|
||
| async getProfile(req, res) { | ||
| try { | ||
| const user = await userService.getProfile(req.user.userId); | ||
|
|
||
| return res.json(user); | ||
| } catch (e) { | ||
| res.status(500).json({ message: e.message }); | ||
| } | ||
| } | ||
|
|
||
| async updatePassword(req, res) { | ||
| try { | ||
| const { oldPassword, newPassword } = req.body; | ||
|
|
||
| await userService.updatePassword( | ||
| req.user.userId, | ||
| oldPassword, | ||
| newPassword, | ||
| ); | ||
|
|
||
| return res.json({ message: 'Пароль успешно обновлен' }); | ||
| } catch (e) { | ||
| res.status(400).json({ message: e.message }); | ||
| } | ||
| } | ||
|
|
||
| async updateEmail(req, res) { | ||
| try { | ||
| const { password, newEmail } = req.body; | ||
|
|
||
| await userService.updateEmail(req.user.userId, password, newEmail); | ||
|
|
||
| return res.json({ message: 'Инструкции отправлены на почту' }); | ||
| } catch (e) { | ||
| res.status(400).json({ message: e.message }); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| export default new UserController(); |
There was a problem hiding this comment.
Missing updateName controller method - userService.updateName exists but there's no corresponding updateName method in the controller. Add: async updateName(req, res) { await userService.updateName(req.user.userId, req.body.name); ... }
|
|
||
| router.put( | ||
| '/update-email', | ||
| authMiddleware, | ||
| body('newEmail').isEmail().withMessage('Введите корректный новый email'), | ||
| body('password').notEmpty().withMessage('Для подтверждения нужен пароль'), | ||
| validationMiddleware, | ||
| userController.updateEmail, |
There was a problem hiding this comment.
Missing confirmEmail field validation for email update. The task requires: 'type the password, confirm the new email'. Add body('confirmEmail').isEmail() and validate it equals newEmail.
| .withMessage('Пароль должен содержать заглавную букву'), | ||
| body('name').notEmpty().withMessage('Имя не может быть пустым'), | ||
| validationMiddleware, | ||
| userController.registration, | ||
| ); |
There was a problem hiding this comment.
SQL injection vulnerability - process.env.DB_NAME is directly interpolated into SQL queries. Use parameterized queries: SELECT 1 FROM pg_database WHERE datname = $1 with [process.env.DB_NAME].
| next(); | ||
| } catch (e) { | ||
| return res.status(401).json({ message: 'Пользователь не авторизован' }); | ||
| } | ||
| } |
There was a problem hiding this comment.
SQL injection vulnerability - Using template literals with process.env.DB_NAME directly in SQL queries. Use parameterized queries instead: client.query('SELECT 1 FROM pg_database WHERE datname = $1', [process.env.DB_NAME])
|
|
||
| // --- НОВЫЕ И ИСПРАВЛЕННЫЕ МЕТОДЫ --- | ||
|
|
||
| // 1. Тот самый getProfile, который требовало ревью | ||
| async getProfile(userId) { | ||
| const user = await User.findByPk(userId, { | ||
| attributes: ['id', 'name', 'email', 'isActive'], | ||
| }); | ||
|
|
||
| if (!user) { | ||
| throw new Error('Пользователь не найден'); | ||
| } | ||
|
|
||
| return user; | ||
| } | ||
|
|
||
| // 2. Метод смены имени | ||
| async updateName(userId, name) { | ||
| const user = await User.findByPk(userId); | ||
|
|
||
| if (!user) { | ||
| throw new Error('Пользователь не найден'); | ||
| } | ||
| user.name = name; | ||
| await user.save(); | ||
|
|
||
| return { id: user.id, name: user.name, email: user.email }; |
There was a problem hiding this comment.
Service layer implementation is complete. However, user.service.js still shows placeholder comments like '// --- НОВЫЕ И ИСПРАВЛЕННЫЕ МЕТОДЫ ---' and '// 1. Тот самый getProfile, который требовало ревью'. Consider cleaning up comments for production.
| from: process.env.SMTP_USER, | ||
| to, | ||
| subject: 'Активация аккаунта', | ||
| text: '', | ||
| html: `<div><h1>Для активации перейдите по ссылке</h1><a href="${link}">${link}</a></div>`, |
There was a problem hiding this comment.
SQL injection vulnerability: Using template literals with process.env.DB_NAME directly in SQL queries (lines 18, 22). Use parameterized queries instead: client.query('SELECT 1 FROM pg_database WHERE datname = $1', [process.env.DB_NAME]).
mateacademy-ai-mentor
left a comment
There was a problem hiding this comment.
Review Summary
All core functionality is implemented correctly with proper security measures. The ESLint configuration and email change confirmation route are minor adjustments that do not affect functionality.
✨ Please rate this review in the Platform's notification center1. I will be grateful for your feedback! ✨
Important
Fix the issues, commit the changes, and then re-request my review using the Re-request review button2 in the Reviewers tab.
Tip
If you have any comments about this review, please leave a message in the Add a comment section below and mention me with the @mateacademy-ai-mentor tag3.
I will read your message during my next review of your pull request. Please do not reply directly to my comments, as that feature is not supported yet.
Caution
Do not close the pull request until the review is complete. Closing it early can lead to unexpected behavior and disrupt the review process.
No description provided.