Skip to content

add solution#264

Open
GabrielSpelchuk wants to merge 5 commits intomate-academy:masterfrom
GabrielSpelchuk:develop
Open

add solution#264
GabrielSpelchuk wants to merge 5 commits intomate-academy:masterfrom
GabrielSpelchuk:develop

Conversation

@GabrielSpelchuk
Copy link
Copy Markdown

No description provided.

Copy link
Copy Markdown

@mateacademy-ai-mentor mateacademy-ai-mentor left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Summary

This requires changes because several critical, blocking requirements are unimplemented or broken. First, password reset functionality is completely missing, including routes (/reset-password, /reset-password/:token), email service integration, and controller logic — yet it's explicitly required in the task. Second, login does not check if the user is activated, violating the requirement that inactive users must be prompted to activate their email. Third, auth middleware fails to attach req.user, breaking all protected profile routes. Fourth, profile updates lack essential validations: password changes don’t verify the old password or confirm new password matches, and email changes don’t require password verification or notify the old email address. Finally, there’s no 404 handler for undefined routes, and multiple await calls are missing (e.g., Token.destroy() in token.service.js), risking race conditions and runtime errors like res.statusStatus(204) in auth.controller.js. These issues prevent core functionality from working as specified and must be fixed before approval.


✨ 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

  1. Rate AI review example

  2. Re-request AI review example

  3. Add AI review comments example

Comment thread src/index.js
Comment on lines +27 to +32
app.use(authRoute);
app.use(userRouter);

app.get('/', (req, res) => {
res.send('Hello');
});
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The task requires a 404 page for all other pages. You have registered your application routes, but you are missing a catch-all middleware to handle requests that don't match any of them. You should add a middleware here, before the errorMiddleware, that sends a 404 response for any unhandled routes.

import { User } from '../models/user';
import bcrypt from 'bcrypt';

const updateProfile = async (req, res) => {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The requirements state changing password should require an old one, new password and confirmation. Currently accepting only password without validating old password or confirming new password matches


const updateData = { name, email };

if (password) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing validation that new password matches confirmation and verification of old password. The requirements specify these are needed for password changes

if (password) {
updateData.password = await bcrypt.hash(password, 10);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requirements state: to change an email you should type the password, confirm the new email and notify the old email about the change. None of this is implemented here

Comment thread src/controllers/auth.controller.js Outdated
Comment on lines +76 to +92

const login = async (req, res) => {
const { email, password } = req.body;

const user = await userService.findByEmail(email);

if (!user) {
throw ApiError.badRequest('No such user');
}

const isPasswordValid = await bcrypt.compare(password, user.password);

if (!isPasswordValid) {
throw ApiError.badRequest('Wrong password');
}

generateTokens(res, user);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing check for user activation status. According to requirements, if user is not active, they should be asked to activate their email.

Comment thread src/controllers/auth.controller.js Outdated
Comment on lines +141 to +142

res.statusStatus(204);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo: res.statusStatus should be res.status. This will cause a runtime error.

Comment thread src/controllers/auth.controller.js Outdated
Comment on lines +3 to +22
import { User } from '../models/user.js';
import { userService } from '../services/user.service.js';
import { jwtService } from '../services/jwt.service.js';
import { ApiError } from '../exeptions/api.error.js';
import { tokenService } from '../services/token.service.js';

const validateName = (value) => {
if (!value.trim()) {
return 'Name is required';
}

if (value.length < 4) {
return 'At least 4 characters';
}
};

const validateEmail = (value) => {
if (!value) {
return 'Email is required';
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The authMiddleware extracts user data from JWT but doesn't attach it to req.user, so protected routes won't have access to user information.

Comment thread src/controllers/auth.controller.js Outdated
Comment on lines +4 to +16
import { userService } from '../services/user.service.js';
import { jwtService } from '../services/jwt.service.js';
import { ApiError } from '../exeptions/api.error.js';
import { tokenService } from '../services/token.service.js';

const validateName = (value) => {
if (!value.trim()) {
return 'Name is required';
}

if (value.length < 4) {
return 'At least 4 characters';
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Profile update doesn't require old password verification. Requirements specify: 'to change an email you should type the password, confirm the new email and notify the old email about the change'.

Comment on lines +9 to +11
const validateName = (value) => {
if (!value.trim()) {
return 'Name is required';
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Profile update doesn't validate password confirmation. Requirements specify changing password requires an old one, new password and confirmation fields that must be equal.

Comment thread src/middlewares/authMiddleware.js Outdated
Comment on lines +4 to +16
const authorization = req.headers['authorization'] || '';
const [, token] = authorization.split(' ');

if (!authorization || !token) {
res.status(401);

return;
}

const userData = jwtService.verify(token);

if (!userData) {
res.status(401);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Profile update doesn't validate old password when changing password. Per requirements: changing password requires an old one, 'new password' and 'confirmation'. Current implementation only accepts 'password' field.

Comment thread src/middlewares/authMiddleware.js Outdated
Comment on lines +4 to +16
const authorization = req.headers['authorization'] || '';
const [, token] = authorization.split(' ');

if (!authorization || !token) {
res.status(401);

return;
}

const userData = jwtService.verify(token);

if (!userData) {
res.status(401);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Profile update doesn't handle email changes with proper validation. Per requirements: to change an email you should type the password, confirm the new email and notify the old email about the change.

Comment thread src/middlewares/authMiddleware.js Outdated
Comment on lines +7 to +17
if (!authorization || !token) {
res.status(401);

return;
}

const userData = jwtService.verify(token);

if (!userData) {
res.status(401);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

authMiddleware sets 401 status but doesn't send a response or return, which could cause the request to hang.

Comment thread src/models/token.js Outdated
Comment on lines +1 to +16
import { DataTypes } from 'sequelize';
import { client } from '../utils/db.js';
import { User } from './user.js';

export const Token = client.define(
'Token',
{
refreshToken: {
type: DataTypes.STRING,
allowNull: false,
},
},
{
tableName: 'tokens',
},
);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Routes are missing password reset endpoints and user profile routes required by the task (profile page to change name, password, email).

Comment thread src/routes/auth.route.js Outdated
Comment on lines +1 to +16
import express from 'express';
import { authController } from '../controllers/auth.controller.js';
import { catchError } from '../utils/catchError.js';

export const authRoute = new express.Router();

authRoute.post('/registration', catchError(authController.registration));
authRoute.post('/login', catchError(authController.login));

authRoute.get(
'/activation/:activationToken',
catchError(authController.activate),
);

authRoute.get('/refresh', catchError(authController.refresh));
authRoute.post('/logout', catchError(authController.logout));
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Auth routes are missing password reset endpoints. The task requires: password reset (ask for email → show email sent page → reset password confirmation page with password/confirmation fields → success page with link to login). No routes defined for /reset-password, /reset-password/:token, etc.

Comment thread src/routes/auth.route.js Outdated
Comment on lines +1 to +16
import express from 'express';
import { authController } from '../controllers/auth.controller.js';
import { catchError } from '../utils/catchError.js';

export const authRoute = new express.Router();

authRoute.post('/registration', catchError(authController.registration));
authRoute.post('/login', catchError(authController.login));

authRoute.get(
'/activation/:activationToken',
catchError(authController.activate),
);

authRoute.get('/refresh', catchError(authController.refresh));
authRoute.post('/logout', catchError(authController.logout));
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No profile routes defined for authenticated users to update their profile (change name, password, email). This is required by the task: 'Profile page: You can change a name', 'change a password', 'change an email'.

Comment thread src/models/user.js
Comment on lines +22 to +25

activationToken: {
type: DataTypes.STRING,
},
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

User model is missing isActive field. The requirements state 'the user should be activated only after email confirmation' and 'If user is not active ask them to activate their email'. The current approach uses activationToken: null as a proxy for activation status, but a boolean isActive field would be more explicit and reliable.

Comment thread src/models/user.js Outdated
Comment on lines +1 to +16
import { DataTypes } from 'sequelize';
import { client } from '../utils/db.js';

export const User = client.define(
'User',
{
name: {
type: DataTypes.STRING,
allowNull: false,
},

email: {
type: DataTypes.STRING,
allowNull: false,
unique: true,
},
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No routes defined for password reset flow. Requirements explicitly require: 'Ask for an email' → 'Show email sent page' → 'Reset Password confirmation page' → 'Show Success page with link to login'. None of these are implemented.

Comment thread src/models/user.js
type: DataTypes.STRING,
allowNull: false,
unique: true,
},
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No 404 handling for undefined routes. Requirements state: '404 for all the other pages'. Need to add a catch-all route at the end that returns 404.

Comment on lines +20 to +22
throw ApiError.badRequest('User already exist', {
email: 'User already exist',
});
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The token service's remove function is missing await before Token.destroy(). The function is async but doesn't wait for the database operation to complete, which could cause race conditions.

Comment on lines +20 to +21
return token;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Token.destroy() call is not awaited. Since it's an async database operation, it should be await Token.destroy(...) to ensure proper execution.

@@ -0,0 +1,36 @@
import jwt from 'jsonwebtoken';

function sing(user) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The access token expires in 30 seconds which is very short. This will cause users to re-authenticate frequently. Consider increasing to something like '1h' for better UX while maintaining security.

Comment thread src/services/jwt.service.js Outdated
Comment on lines +1 to +35
import jwt from 'jsonwebtoken';

function sing(user) {
const token = jwt.sign(user, process.env.JWT_KEY, { expiresIn: '30s' });

return token;
}

function verify(token) {
try {
return jwt.verify(token, process.env.JWT_KEY);
} catch (error) {
return null;
}
}

function singRefresh(user) {
const token = jwt.sign(user, process.env.JWT_REFRESH_KEY);

return token;
}

function verifyRefresh(token) {
try {
return jwt.verify(token, process.env.JWT_REFRESH_KEY);
} catch (error) {
return null;
}
}

export const jwtService = {
sing,
verify,
singRefresh,
verifyRefresh,
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

User service is missing functions for: password reset (generating reset token, resetting password with validation), and profile update functions (change name, change password with old password verification, change email with notification). These are required by the task.

Comment on lines +20 to +21
function sendActivationEmail(email, token) {
const href = `${process.env.CLIENT_HOST}/activate/${token}`;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The remove function calls Token.destroy which returns a promise, but it's not awaited. This could cause unhandled promise warnings or timing issues.

Comment thread src/services/token.service.js Outdated
Comment on lines +20 to +22

function remove(userId) {
Token.destroy({ where: { userId } });
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The remove function is async but doesn't use await before Token.destroy. This should be await Token.destroy({ where: { userId } }); for proper async handling.

Copy link
Copy Markdown

@mateacademy-ai-mentor mateacademy-ai-mentor left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Summary

Great work on the improvements from the last review, especially with the user activation flow! I am requesting a few changes because some core features are incomplete and have critical bugs that prevent the application from working as required.

Here are the main issues to address:

  1. Password Reset is Incomplete: This feature is not functional. It's missing the routes in src/routes/auth.route.js, the required logic in src/services/user.service.js (updateResetToken, etc.), and a resetToken field in the User model.
  2. Missing Profile Validations: When updating a profile in src/controllers/user.controller.js, you need to add a check for a matching confirmation field when changing a password and send a notification to the user's old email address when their email is changed.
  3. Critical Bugs:
    • In src/services/token.service.js, the remove function is missing await for Token.destroy(), which breaks the logout functionality.
    • The authMiddleware.js hangs on invalid tokens because it doesn't send a response. You should use your error handler to send a proper 401 error.

Additionally, your build is failing because .eslintrc.js should be renamed to .eslintrc.cjs to work with your ES module setup. Addressing these points will get the project fully functional.


✨ 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

  1. Rate AI review example

  2. Re-request AI review example

  3. Add AI review comments example

Comment thread src/controllers/user.controller.js Outdated
Comment on lines +1 to +4
import { ApiError } from '../exeptions/api.error';
import { User } from '../models/user';
import bcrypt from 'bcrypt';
import { userService } from '../services/user.service';
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These import statements are missing the .js file extension. While this might work in some environments, it's best practice in Node.js ESM to include the full file extension for clarity and to avoid potential module resolution issues. Other files in the project correctly include the extension.

Comment thread src/controllers/user.controller.js Outdated
};

const updateProfile = async (req, res) => {
const { name, email, oldPassword, newPassword } = req.body;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

According to the task requirements, when changing a password, the user must provide a new password and a confirmation. The confirmation field is missing from the request body, and there's no logic here to verify that the new password and its confirmation match.

Comment thread src/controllers/user.controller.js Outdated
Comment on lines +37 to +39
if (email && email !== user.email) {
updateData.email = email;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The requirements state that when a user's email is changed, a notification must be sent to their old email address. This notification logic is missing from this block.

Comment thread src/controllers/auth.controller.js Outdated
throw ApiError.badRequest('User with this email does not exist');
}

const resetToken = crypto.randomBytes(32).toString('hex');
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The crypto module is used here but it's not imported. You need to add import crypto from 'crypto'; at the top of the file for this to work.


const resetToken = crypto.randomBytes(32).toString('hex');

await userService.updateResetToken(user.id, resetToken);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The updateResetToken function is called here, but it's not defined or exported in src/services/user.service.js. The same issue exists for findByResetToken and updatePassword used in the resetPassword function. The password reset feature needs to be fully implemented in the service layer, including adding the necessary fields to the User model.

Comment thread src/models/user.js
Comment on lines +23 to +25
activationToken: {
type: DataTypes.STRING,
},
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The task requires a password reset feature, which typically involves storing a temporary token for the user. This model is missing a field, such as resetToken, to hold that value. You might also want to add a resetTokenExpiry field to ensure the token expires after a certain period.

Comment thread src/services/user.service.js Outdated
@@ -0,0 +1,35 @@
import { ApiError } from '../exeptions/api.error.js';
import { User } from '../models/user';
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This import statement is missing the .js file extension. While this might work in some environments, it's inconsistent with other imports in this file (like for ApiError and emailService) and the project as a whole. It's best practice in Node.js ESM to include the full file extension.

Comment thread src/services/user.service.js Outdated
await emailService.sendActivationEmail(email, activationToken);
}

export const userService = { normalize, findByEmail, register };
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This service is missing the implementation for the password reset functionality. The auth.controller.js file calls several functions that should be defined here, such as:

  • updateResetToken
  • findByResetToken
  • updatePassword

These functions are essential for the password reset flow to work as required.

Comment thread src/services/token.service.js Outdated
@@ -0,0 +1,25 @@
import { Token } from '../models/token';
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This import is missing the .js extension. While it might work depending on the Node.js version and configuration, it's a best practice to include it for consistency with other files in your project (like user.service.js) and to prevent potential module resolution issues.

Comment thread src/services/token.service.js Outdated
}

function remove(userId) {
Token.destroy({ where: { userId } });
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Token.destroy() is an asynchronous operation and must be awaited. Without await, the function will return before the token is actually deleted from the database. This means the logout process might not work as expected.

Copy link
Copy Markdown

@mateacademy-ai-mentor mateacademy-ai-mentor left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Summary

I am approving this submission as an educational override to help you progress, but there are a few critical issues to address before the application will run correctly. You've done an excellent job fixing previous bugs and building out the core services! However, the password reset feature is currently non-functional. In src/controllers/auth.controller.js, you're using the crypto module without importing it, and you're calling a userService.updatePassword function that doesn't exist yet in the user service. Additionally, several files like src/services/user.service.js are missing the .js extension on local imports, which will prevent the server from starting. For the profile update, please also add the required emailConfirmation check when a user changes their email. You're very close to finishing—address these points, and you'll have a fully working application!


✨ 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

  1. Rate AI review example

  2. Re-request AI review example

  3. Add AI review comments example

user.password = await bcrypt.hash(password, 10);
}

if (email && email !== user.email) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

According to the task description, when changing an email, you also need to "confirm the new email". This is similar to how you handle password changes. You should expect an emailConfirmation field in the request body and verify that it matches the new email before proceeding with the update.

Comment thread src/controllers/auth.controller.js Outdated
throw ApiError.badRequest('User with this email does not exist');
}

const resetToken = crypto.randomBytes(32).toString('hex');
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The built-in crypto module is used here to generate a token, but it hasn't been imported at the top of the file. This will cause a ReferenceError at runtime.

Comment on lines +117 to +130
const resetPassword = async (req, res) => {
const { token, newPassword } = req.body;
const user = await userService.findByResetToken(token);

if (!user) {
throw ApiError.badRequest('Invalid or expired reset token');
}

const hashedPassword = await bcrypt.hash(newPassword, 10);

await userService.updatePassword(user.id, hashedPassword);

res.send({ message: 'Password has been reset' });
};
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This password reset implementation is missing two key parts from the requirements:

  1. Password Confirmation: You need to get a confirmation field from req.body and verify that it matches newPassword.
  2. Update Logic: The userService.updatePassword function doesn't exist in the user service. You should implement the logic to update the user's password and set their resetToken to null directly in this function, then save the user.

Comment thread src/services/user.service.js Outdated
@@ -0,0 +1,53 @@
import { ApiError } from '../exeptions/api.error.js';
import { User } from '../models/user';
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When using ES modules in Node.js, you need to include the file extension in your relative imports. This should be from '../models/user.js'; to ensure the module is found correctly.

Comment thread src/services/user.service.js Outdated
Comment on lines +47 to +53
export const userService = {
normalize,
findByEmail,
register,
updateResetToken,
findByResetToken,
};
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The resetPassword function in auth.controller.js calls userService.updatePassword, but that function is not implemented or exported in this service. You need to create this function to handle updating the user's password in the database.

Comment thread src/services/token.service.js Outdated
@@ -0,0 +1,25 @@
import { Token } from '../models/token';
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In an ES Modules environment like this project, you need to include the file extension in your relative imports. This should be import { Token } from '../models/token.js'; to match the pattern used in your other files.

Copy link
Copy Markdown

@brespect brespect left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lgtm 🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants