From d56613bf8fb8b4157b98b233424b39de3611f0dd Mon Sep 17 00:00:00 2001 From: Tymur Date: Tue, 2 Jun 2026 19:34:58 +0300 Subject: [PATCH 1/4] add task solution --- src/controllers/auth.controller.js | 183 +++++++++++++++++++++++++++++ src/controllers/user.controller.js | 132 +++++++++++++++++++++ src/exceptions/api.error.js | 32 +++++ src/index.js | 30 +++++ src/middlewares/authMiddleware.js | 21 ++++ src/middlewares/errorMiddleware.js | 18 +++ src/models/token.js | 13 ++ src/models/user.js | 30 +++++ src/routers/auth.route.js | 22 ++++ src/routers/user.route.js | 35 ++++++ src/services/email.service.js | 64 ++++++++++ src/services/jwt.service.js | 38 ++++++ src/services/token.service.js | 29 +++++ src/services/user.service.js | 117 ++++++++++++++++++ src/utils/catchError.js | 9 ++ src/utils/db.js | 21 ++++ 16 files changed, 794 insertions(+) create mode 100644 src/controllers/auth.controller.js create mode 100644 src/controllers/user.controller.js create mode 100644 src/exceptions/api.error.js create mode 100644 src/middlewares/authMiddleware.js create mode 100644 src/middlewares/errorMiddleware.js create mode 100644 src/models/token.js create mode 100644 src/models/user.js create mode 100644 src/routers/auth.route.js create mode 100644 src/routers/user.route.js create mode 100644 src/services/email.service.js create mode 100644 src/services/jwt.service.js create mode 100644 src/services/token.service.js create mode 100644 src/services/user.service.js create mode 100644 src/utils/catchError.js create mode 100644 src/utils/db.js diff --git a/src/controllers/auth.controller.js b/src/controllers/auth.controller.js new file mode 100644 index 00000000..02e2fc99 --- /dev/null +++ b/src/controllers/auth.controller.js @@ -0,0 +1,183 @@ +import { ApiError } from '../exceptions/api.error.js'; +import { jwtService } from '../services/jwt.service.js'; +import { tokenService } from '../services/token.service.js'; +import { userService } from '../services/user.service.js'; +import bcrypt from 'bcrypt'; + +export function validateEmail(email) { + const emailPattern = /^[\w.+-]+@([\w-]+\.){1,3}[\w-]{2,}$/; + + if (!email) { + return 'Email is required'; + } + + if (!emailPattern.test(email)) { + return 'Email is not valid'; + } +} + +export function validatePassword(password) { + if (!password) { + return 'Password is required'; + } + + if (password.length < 6) { + return 'At least 6 characters'; + } +} + +const register = async (req, res, next) => { + const { name, email, password } = req.body; + + const errors = { + email: validateEmail(email), + password: validatePassword(password), + }; + + if (Object.values(errors).some((error) => error)) { + throw ApiError.badRequest('Validation error', errors); + } + + const hashedPass = await bcrypt.hash(password, 10); + + await userService.register(name, email, hashedPass); + + res.send({ + message: 'OK', + }); +}; + +const activate = async (req, res, next) => { + const { activationToken } = req.params; + const user = await userService.findByActivationToken(activationToken); + + if (!user) { + throw ApiError.notFound({ email: 'this email doesn`t exist' }); + } + + user.activationToken = null; + await user.save(); + + res.send({ + message: 'OK', + }); +}; + +const login = async (req, res, next) => { + const { email, password } = req.body; + + const user = await userService.findByEmail(email); + + if (!user) { + throw ApiError.unauthorized({ email: 'this email doesn`t exist' }); + } + + const isPasswordValid = await bcrypt.compare(password, user.password); + + if (!isPasswordValid) { + throw ApiError.unauthorized({ password: 'wrong password' }); + } + + if (user.activationToken) { + throw ApiError.unauthorized({ email: 'please activate the email' }); + } + + await generateTokens(res, user); +}; + +const refresh = async (req, res, next) => { + const { refreshToken } = req.cookies; + + const userData = jwtService.verifyRefresh(refreshToken); + const token = await tokenService.getByToken(refreshToken); + + if (!userData || !token) { + throw ApiError.unauthorized(); + } + + const user = await userService.findByEmail(userData.email); + + await generateTokens(res, user); +}; + +const generateTokens = async (res, user) => { + const normalizedUser = userService.normalize(user); + const accessToken = jwtService.sign(normalizedUser); + const refreshToken = jwtService.signRefresh(normalizedUser); + + await tokenService.save(normalizedUser.id, refreshToken); + + res.cookie('refreshToken', refreshToken, { + maxAge: 30 * 24 * 60 * 60 * 1000, + httpOnly: true, + }); + + res.send({ + user: normalizedUser, + accessToken, + }); +}; + +const logout = async (req, res, next) => { + res.clearCookie('refreshToken'); + + await tokenService.remove(req.user.id); + + res.send({ + message: 'OK', + }); +}; + +const sendResetMessage = async (req, res, next) => { + const { email } = req.body; + + await userService.sendResetMessage(email); + + res.send({ + message: 'OK', + }); +}; + +const resetPassword = async (req, res, next) => { + const { resetPasswordToken } = req.params; + const { newPassword, confirmPassword } = req.body; + + const errors = { + password: validatePassword(newPassword), + }; + + const user = await userService.findByResetPasswordToken(resetPasswordToken); + + if (newPassword !== confirmPassword) { + throw ApiError.badRequest('password and confirmation fields must be equal'); + } + + if (!user) { + throw ApiError.notFound({ email: 'this email doesn`t exist' }); + } + + if (Object.values(errors).some((error) => error)) { + throw ApiError.badRequest('Validation error', errors); + } + + const hashedPass = await bcrypt.hash(newPassword, 10); + + await userService.changePassword(user.email, hashedPass); + + user.resetPasswordToken = null; + await user.save(); + + res.send({ + message: 'OK', + }); +}; + +export const authController = { + register, + activate, + login, + refresh, + logout, + sendResetMessage, + resetPassword, +}; diff --git a/src/controllers/user.controller.js b/src/controllers/user.controller.js new file mode 100644 index 00000000..d2801378 --- /dev/null +++ b/src/controllers/user.controller.js @@ -0,0 +1,132 @@ +import { ApiError } from '../exceptions/api.error.js'; +import { userService } from '../services/user.service.js'; +import { validateEmail, validatePassword } from './auth.controller.js'; +import bcrypt from 'bcrypt'; + +const getOneActivated = async (req, res, next) => { + const id = req.user.id; + + const user = await userService.findActivatedById(id); + + res.send(user); +}; + +const changePasswordAuthenticated = async (req, res, next) => { + const { oldPassword, newPassword, confirmPassword } = req.body; + const userData = req.user; + + if (!userData) { + throw ApiError.unauthorized(); + } + + const user = await userService.findByEmail(userData.email); + + if (newPassword !== confirmPassword) { + throw ApiError.badRequest('password and confirmation fields must be equal'); + } + + const isPasswordValid = await bcrypt.compare(oldPassword, user.password); + + if (!isPasswordValid) { + throw ApiError.unauthorized({ password: 'wrong password' }); + } + + const errors = { + newPassword: validatePassword(newPassword), + }; + + if (Object.values(errors).some((error) => error)) { + throw ApiError.badRequest('Validation error', errors); + } + + const hashedPass = await bcrypt.hash(newPassword, 10); + + await userService.changePassword(userData.email, hashedPass); + + res.send({ + message: 'OK', + }); +}; + +const changeName = async (req, res, next) => { + const { name } = req.body; + const userData = req.user; + + if (!userData) { + throw ApiError.unauthorized(); + } + + await userService.changeName(userData.email, name); + + res.send({ + message: 'OK', + }); +}; + +const changeEmail = async (req, res, next) => { + const { oldEmail, newEmail, password } = req.body; + const userData = req.user; + + if (!userData) { + throw ApiError.unauthorized(); + } + + const user = await userService.findByEmail(userData.email); + + const isPasswordValid = await bcrypt.compare(password, user.password); + + if (!isPasswordValid) { + throw ApiError.unauthorized({ password: 'wrong password' }); + } + + const errors = { + email: validateEmail(newEmail), + }; + + if (Object.values(errors).some((error) => error)) { + throw ApiError.badRequest('Validation error', errors); + } + + if (oldEmail === newEmail) { + throw ApiError.badRequest('new email must be different'); + } + + await userService.changeEmail(userData.email, newEmail); + + res.send({ + message: 'OK', + }); +}; + +const activateChangedEmail = async (req, res, next) => { + const { emailChangeToken } = req.params; + const user = await userService.findByEmailChangeToken(emailChangeToken); + + if (!user) { + throw ApiError.notFound({ email: 'this email doesn`t exist' }); + } + + const existingUser = await userService.findByEmail(user.pendingEmail); + + if (existingUser) { + throw ApiError.badRequest('email already exists'); + } + + user.email = user.pendingEmail; + user.pendingEmail = null; + user.emailChangeToken = null; + + await user.save(); + + res.send({ + message: 'OK', + }); +}; + +export const userController = { + getOneActivated, + changeName, + changeEmail, + changePasswordAuthenticated, + activateChangedEmail, +}; diff --git a/src/exceptions/api.error.js b/src/exceptions/api.error.js new file mode 100644 index 00000000..48fdc66d --- /dev/null +++ b/src/exceptions/api.error.js @@ -0,0 +1,32 @@ +export class ApiError extends Error { + constructor({ message, status, errors = {} }) { + super(message); + + this.status = status; + this.errors = errors; + } + + static badRequest(message, errors = {}) { + return new ApiError({ + message, + errors, + status: 400, + }); + } + + static unauthorized(errors = {}) { + return new ApiError({ + message: 'unauthorized user', + errors, + status: 401, + }); + } + + static notFound(errors) { + return new ApiError({ + message: 'not found', + errors, + status: 404, + }); + } +} diff --git a/src/index.js b/src/index.js index ad9a93a7..53fb733f 100644 --- a/src/index.js +++ b/src/index.js @@ -1 +1,31 @@ +/* eslint-disable no-console */ 'use strict'; + +import express from 'express'; +import 'dotenv/config'; +import { authRouter } from './routers/auth.route.js'; +import { userRouter } from './routers/user.route.js'; +import { errorMiddleware } from './middlewares/errorMiddleware.js'; +import cookieParser from 'cookie-parser'; + +const PORT = process.env.PORT || 3005; + +const app = express(); + +app.use(express.json()); +app.use(cookieParser()); + +app.use(authRouter); +app.use('/user', userRouter); + +app.use((req, res) => { + res.status(404).send(` +

Page not found

+ `); +}); + +app.use(errorMiddleware); + +app.listen(PORT, () => { + console.log('server is running'); +}); diff --git a/src/middlewares/authMiddleware.js b/src/middlewares/authMiddleware.js new file mode 100644 index 00000000..6202077f --- /dev/null +++ b/src/middlewares/authMiddleware.js @@ -0,0 +1,21 @@ +import { ApiError } from '../exceptions/api.error.js'; +import { jwtService } from '../services/jwt.service.js'; + +export const authMiddleware = (req, res, next) => { + const authorization = req.headers['authorization'] || ''; + const [, token] = authorization.split(' '); + + if (!authorization || !token) { + throw ApiError.unauthorized(); + } + + const userData = jwtService.verify(token); + + if (!userData) { + throw ApiError.unauthorized(); + } + + req.user = userData; + + next(); +}; diff --git a/src/middlewares/errorMiddleware.js b/src/middlewares/errorMiddleware.js new file mode 100644 index 00000000..239913ba --- /dev/null +++ b/src/middlewares/errorMiddleware.js @@ -0,0 +1,18 @@ +import { ApiError } from '../exceptions/api.error.js'; + +export const errorMiddleware = (error, req, res, next) => { + if (res.headersSent) { + return next(error); + } + + if (error instanceof ApiError) { + return res.status(error.status || 400).json({ + message: error.message, + errors: error.errors || null, + }); + } + + return res.status(500).json({ + message: 'Server error', + }); +}; diff --git a/src/models/token.js b/src/models/token.js new file mode 100644 index 00000000..a9c33f91 --- /dev/null +++ b/src/models/token.js @@ -0,0 +1,13 @@ +import { DataTypes } from 'sequelize'; +import { sequelize } from '../utils/db.js'; +import { User } from './user.js'; + +export const Token = sequelize.define('token', { + refreshToken: { + type: DataTypes.STRING, + allowNull: false, + }, +}); + +Token.belongsTo(User); +User.hasOne(Token); diff --git a/src/models/user.js b/src/models/user.js new file mode 100644 index 00000000..e93184ae --- /dev/null +++ b/src/models/user.js @@ -0,0 +1,30 @@ +import { DataTypes } from 'sequelize'; +import { sequelize } from '../utils/db.js'; + +export const User = sequelize.define('user', { + email: { + type: DataTypes.STRING, + allowNull: false, + unique: true, + }, + name: { + type: DataTypes.STRING, + allowNull: false, + }, + password: { + type: DataTypes.STRING, + allowNull: false, + }, + activationToken: { + type: DataTypes.STRING, + }, + resetPasswordToken: { + type: DataTypes.STRING, + }, + pendingEmail: { + type: DataTypes.STRING, + }, + emailChangeToken: { + type: DataTypes.STRING, + }, +}); diff --git a/src/routers/auth.route.js b/src/routers/auth.route.js new file mode 100644 index 00000000..dc35c165 --- /dev/null +++ b/src/routers/auth.route.js @@ -0,0 +1,22 @@ +import express from 'express'; +import { authController } from '../controllers/auth.controller.js'; +import { catchError } from '../utils/catchError.js'; +import { authMiddleware } from '../middlewares/authMiddleware.js'; + +export const authRouter = express.Router(); + +authRouter.post('/registration', catchError(authController.register)); + +authRouter.get( + '/activation/:activationToken', + catchError(authController.activate), +); +authRouter.post('/login', catchError(authController.login)); +authRouter.post('/refresh', catchError(authController.refresh)); +authRouter.post('/logout', authMiddleware, catchError(authController.logout)); +authRouter.post('/password/reset', catchError(authController.sendResetMessage)); + +authRouter.post( + '/password/reset/:resetPasswordToken', + catchError(authController.resetPassword), +); diff --git a/src/routers/user.route.js b/src/routers/user.route.js new file mode 100644 index 00000000..e6360f73 --- /dev/null +++ b/src/routers/user.route.js @@ -0,0 +1,35 @@ +import express from 'express'; +import { userController } from '../controllers/user.controller.js'; +import { authMiddleware } from '../middlewares/authMiddleware.js'; +import { catchError } from '../utils/catchError.js'; + +export const userRouter = express.Router(); + +userRouter.get( + '/profile', + authMiddleware, + catchError(userController.getOneActivated), +); + +userRouter.patch( + '/profile/name/change', + authMiddleware, + catchError(userController.changeName), +); + +userRouter.patch( + '/profile/password/change', + authMiddleware, + catchError(userController.changePasswordAuthenticated), +); + +userRouter.patch( + '/profile/email/change', + authMiddleware, + catchError(userController.changeEmail), +); + +userRouter.get( + '/profile/email/change/activation/:emailChangeToken', + catchError(userController.activateChangedEmail), +); diff --git a/src/services/email.service.js b/src/services/email.service.js new file mode 100644 index 00000000..0ad34335 --- /dev/null +++ b/src/services/email.service.js @@ -0,0 +1,64 @@ +import nodemailer from 'nodemailer'; + +const transporter = nodemailer.createTransport({ + service: 'gmail', + auth: { + user: process.env.EMAIL, + pass: process.env.EMAIL_PASSWORD, + }, +}); + +function send(email, subject, html) { + return transporter.sendMail({ + from: 'Auth API', + to: email, + subject, + html, + }); +} + +function sendActivationEmail(email, token) { + const href = `${process.env.CLIENT_HOST}/activation/${token}`; + const html = ` +

Activate

+ ${href} + `; + + return send(email, 'Activate', html); +} + +function sendNotifyEmail(email) { + const html = ` +

Your email has been changed

+ `; + + return send(email, 'Your email has been changed', html); +} + +function sendResetPasswordEmail(email, token) { + const href = `${process.env.CLIENT_HOST}/password/reset/${token}`; + const html = ` +

Reset password

+ ${href} + `; + + return send(email, 'Reset password', html); +} + +function sendChangeEmail(email, token) { + const href = `${process.env.CLIENT_HOST}/user/profile/email/change/activation/${token}`; + const html = ` +

Confirm change email

+ ${href} + `; + + return send(email, 'Confirm change email', html); +} + +export const mailer = { + send, + sendActivationEmail, + sendResetPasswordEmail, + sendNotifyEmail, + sendChangeEmail, +}; diff --git a/src/services/jwt.service.js b/src/services/jwt.service.js new file mode 100644 index 00000000..6a7bbad8 --- /dev/null +++ b/src/services/jwt.service.js @@ -0,0 +1,38 @@ +import jwt from 'jsonwebtoken'; + +function sign(user) { + const token = jwt.sign(user, process.env.JWT_KEY, { + expiresIn: '120s', + }); + + return token; +} + +function verify(token) { + try { + return jwt.verify(token, process.env.JWT_KEY); + } catch (error) { + return null; + } +} + +function signRefresh(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 = { + sign, + verify, + signRefresh, + verifyRefresh, +}; diff --git a/src/services/token.service.js b/src/services/token.service.js new file mode 100644 index 00000000..01b8aab5 --- /dev/null +++ b/src/services/token.service.js @@ -0,0 +1,29 @@ +import { Token } from '../models/token.js'; + +async function save(userId, newToken) { + const token = await Token.findOne({ where: { userId } }); + + if (!token) { + await Token.create({ userId, refreshToken: newToken }); + + return; + } + + token.refreshToken = newToken; + + await token.save(); +} + +async function getByToken(refreshToken) { + return Token.findOne({ where: { refreshToken } }); +} + +async function remove(userId) { + return Token.destroy({ where: { userId } }); +} + +export const tokenService = { + save, + getByToken, + remove, +}; diff --git a/src/services/user.service.js b/src/services/user.service.js new file mode 100644 index 00000000..02c5cd72 --- /dev/null +++ b/src/services/user.service.js @@ -0,0 +1,117 @@ +import { ApiError } from '../exceptions/api.error.js'; +import { User } from '../models/user.js'; +import { mailer } from '../services/email.service.js'; +import { v4 as uuidv4 } from 'uuid'; + +function findByEmail(email) { + return User.findOne({ where: { email } }); +} + +function findActivatedById(id) { + return User.findOne({ + where: { + id, + activationToken: null, + }, + }); +} + +function findByActivationToken(activationToken) { + return User.findOne({ where: { activationToken } }); +} + +function findByResetPasswordToken(resetPasswordToken) { + return User.findOne({ where: { resetPasswordToken } }); +} + +function findByEmailChangeToken(emailChangeToken) { + return User.findOne({ where: { emailChangeToken } }); +} + +function normalize({ id, email }) { + return { id, email }; +} + +async function register(name, email, password) { + const activationToken = uuidv4(); + + const existUser = await findByEmail(email); + + if (existUser) { + throw ApiError.badRequest('User already exist', { + email: 'User already exist', + }); + } + + await User.create({ + name, + email, + password, + activationToken, + }); + + await mailer.sendActivationEmail(email, activationToken); +} + +async function sendResetMessage(email) { + const resetPasswordToken = uuidv4(); + + const existUser = await findByEmail(email); + + if (!existUser) { + throw ApiError.badRequest('User does not exist, please sign up', { + email: 'User does not exist', + }); + } + + await User.update({ resetPasswordToken }, { where: { email } }); + + await mailer.sendResetPasswordEmail(email, resetPasswordToken); +} + +async function changePassword(email, password) { + await User.update({ password }, { where: { email } }); +} + +async function changeName(email, name) { + await User.update({ name }, { where: { email } }); +} + +async function changeEmail(oldEmail, newEmail) { + const existingUser = await findByEmail(newEmail); + + if (existingUser) { + throw ApiError.badRequest('this email is exist', { + email: 'this email is exist', + }); + } + + const emailChangeToken = uuidv4(); + + await mailer.sendChangeEmail(newEmail, emailChangeToken); + await mailer.sendNotifyEmail(oldEmail); + + await User.update( + { + pendingEmail: newEmail, + emailChangeToken, + }, + { + where: { email: oldEmail }, + }, + ); +} + +export const userService = { + normalize, + findByEmail, + findActivatedById, + findByActivationToken, + findByResetPasswordToken, + findByEmailChangeToken, + register, + sendResetMessage, + changePassword, + changeName, + changeEmail, +}; diff --git a/src/utils/catchError.js b/src/utils/catchError.js new file mode 100644 index 00000000..0e1e7d8f --- /dev/null +++ b/src/utils/catchError.js @@ -0,0 +1,9 @@ +export const catchError = (action) => { + return async function (req, res, next) { + try { + await action(req, res, next); + } catch (error) { + next(error); + } + }; +}; diff --git a/src/utils/db.js b/src/utils/db.js new file mode 100644 index 00000000..c8819e03 --- /dev/null +++ b/src/utils/db.js @@ -0,0 +1,21 @@ +'use strict'; + +import { Sequelize } from 'sequelize'; +import 'dotenv/config'; + +const { + POSTGRES_HOST, + POSTGRES_PORT, + POSTGRES_USER, + POSTGRES_PASSWORD, + POSTGRES_DB, +} = process.env; + +export const sequelize = new Sequelize({ + database: POSTGRES_DB || 'postgres', + username: POSTGRES_USER || 'postgres', + host: POSTGRES_HOST || 'localhost', + dialect: 'postgres', + port: POSTGRES_PORT || 5432, + password: POSTGRES_PASSWORD || '123', +}); From d2c68beba600d5e507dce5b94fb1a9bbd83e52d3 Mon Sep 17 00:00:00 2001 From: Tymur Date: Tue, 2 Jun 2026 19:48:16 +0300 Subject: [PATCH 2/4] add task solution --- src/controllers/auth.controller.js | 11 ----------- src/controllers/user.controller.js | 12 ++++++++++++ src/middlewares/guestMiddleware.js | 19 +++++++++++++++++++ src/routers/auth.route.js | 21 ++++++++++++++++----- src/routers/user.route.js | 2 ++ src/services/jwt.service.js | 2 +- 6 files changed, 50 insertions(+), 17 deletions(-) create mode 100644 src/middlewares/guestMiddleware.js diff --git a/src/controllers/auth.controller.js b/src/controllers/auth.controller.js index 02e2fc99..0c282f8a 100644 --- a/src/controllers/auth.controller.js +++ b/src/controllers/auth.controller.js @@ -118,16 +118,6 @@ const generateTokens = async (res, user) => { }); }; -const logout = async (req, res, next) => { - res.clearCookie('refreshToken'); - - await tokenService.remove(req.user.id); - - res.send({ - message: 'OK', - }); -}; - const sendResetMessage = async (req, res, next) => { const { email } = req.body; @@ -177,7 +167,6 @@ export const authController = { activate, login, refresh, - logout, sendResetMessage, resetPassword, }; diff --git a/src/controllers/user.controller.js b/src/controllers/user.controller.js index d2801378..7df30bb5 100644 --- a/src/controllers/user.controller.js +++ b/src/controllers/user.controller.js @@ -1,4 +1,5 @@ import { ApiError } from '../exceptions/api.error.js'; +import { tokenService } from '../services/token.service.js'; import { userService } from '../services/user.service.js'; import { validateEmail, validatePassword } from './auth.controller.js'; import bcrypt from 'bcrypt'; @@ -123,10 +124,21 @@ const activateChangedEmail = async (req, res, next) => { }); }; +const logout = async (req, res, next) => { + res.clearCookie('refreshToken'); + + await tokenService.remove(req.user.id); + + res.send({ + message: 'OK', + }); +}; + export const userController = { getOneActivated, changeName, changeEmail, changePasswordAuthenticated, activateChangedEmail, + logout, }; diff --git a/src/middlewares/guestMiddleware.js b/src/middlewares/guestMiddleware.js new file mode 100644 index 00000000..74661694 --- /dev/null +++ b/src/middlewares/guestMiddleware.js @@ -0,0 +1,19 @@ +import { ApiError } from '../exceptions/api.error.js'; +import { jwtService } from '../services/jwt.service.js'; + +export const guestMiddleware = (req, res, next) => { + const authorization = req.headers['authorization'] || ''; + const [, token] = authorization.split(' '); + + if (authorization || token) { + throw ApiError.badRequest('this function only for non authenticated users'); + } + + const userData = jwtService.verify(token); + + if (userData) { + throw ApiError.badRequest('this function only for non authenticated users'); + } + + next(); +}; diff --git a/src/routers/auth.route.js b/src/routers/auth.route.js index dc35c165..bfcb21fa 100644 --- a/src/routers/auth.route.js +++ b/src/routers/auth.route.js @@ -1,7 +1,7 @@ import express from 'express'; import { authController } from '../controllers/auth.controller.js'; import { catchError } from '../utils/catchError.js'; -import { authMiddleware } from '../middlewares/authMiddleware.js'; +import { guestMiddleware } from '../middlewares/guestMiddleware.js'; export const authRouter = express.Router(); @@ -9,14 +9,25 @@ authRouter.post('/registration', catchError(authController.register)); authRouter.get( '/activation/:activationToken', + guestMiddleware, catchError(authController.activate), ); -authRouter.post('/login', catchError(authController.login)); -authRouter.post('/refresh', catchError(authController.refresh)); -authRouter.post('/logout', authMiddleware, catchError(authController.logout)); -authRouter.post('/password/reset', catchError(authController.sendResetMessage)); +authRouter.post('/login', guestMiddleware, catchError(authController.login)); + +authRouter.post( + '/refresh', + guestMiddleware, + catchError(authController.refresh), +); + +authRouter.post( + '/password/reset', + guestMiddleware, + catchError(authController.sendResetMessage), +); authRouter.post( '/password/reset/:resetPasswordToken', + guestMiddleware, catchError(authController.resetPassword), ); diff --git a/src/routers/user.route.js b/src/routers/user.route.js index e6360f73..b9eafad3 100644 --- a/src/routers/user.route.js +++ b/src/routers/user.route.js @@ -33,3 +33,5 @@ userRouter.get( '/profile/email/change/activation/:emailChangeToken', catchError(userController.activateChangedEmail), ); + +userRouter.post('/logout', authMiddleware, catchError(userController.logout)); diff --git a/src/services/jwt.service.js b/src/services/jwt.service.js index 6a7bbad8..9d665c48 100644 --- a/src/services/jwt.service.js +++ b/src/services/jwt.service.js @@ -2,7 +2,7 @@ import jwt from 'jsonwebtoken'; function sign(user) { const token = jwt.sign(user, process.env.JWT_KEY, { - expiresIn: '120s', + expiresIn: '900s', }); return token; From 189b01fbe74df503b49de26a2f236df53e4d22e7 Mon Sep 17 00:00:00 2001 From: Tymur Date: Tue, 2 Jun 2026 19:57:31 +0300 Subject: [PATCH 3/4] add task solution --- src/middlewares/guestMiddleware.js | 19 +++++++++++++------ src/routers/auth.route.js | 12 ++++++------ src/routers/user.route.js | 1 + 3 files changed, 20 insertions(+), 12 deletions(-) diff --git a/src/middlewares/guestMiddleware.js b/src/middlewares/guestMiddleware.js index 74661694..fa73d241 100644 --- a/src/middlewares/guestMiddleware.js +++ b/src/middlewares/guestMiddleware.js @@ -1,18 +1,25 @@ -import { ApiError } from '../exceptions/api.error.js'; -import { jwtService } from '../services/jwt.service.js'; +import { ApiError } from '../exceptions/api.error'; +import { jwtService } from '../services/jwt.service'; export const guestMiddleware = (req, res, next) => { - const authorization = req.headers['authorization'] || ''; + const authorization = req.headers.authorization; + + if (!authorization) { + return next(); + } + const [, token] = authorization.split(' '); - if (authorization || token) { - throw ApiError.badRequest('this function only for non authenticated users'); + if (!token) { + return next(); } const userData = jwtService.verify(token); if (userData) { - throw ApiError.badRequest('this function only for non authenticated users'); + throw ApiError.badRequest( + 'This function is only for non-authenticated users', + ); } next(); diff --git a/src/routers/auth.route.js b/src/routers/auth.route.js index bfcb21fa..44457e31 100644 --- a/src/routers/auth.route.js +++ b/src/routers/auth.route.js @@ -5,7 +5,11 @@ import { guestMiddleware } from '../middlewares/guestMiddleware.js'; export const authRouter = express.Router(); -authRouter.post('/registration', catchError(authController.register)); +authRouter.post( + '/registration', + guestMiddleware, + catchError(authController.register), +); authRouter.get( '/activation/:activationToken', @@ -14,11 +18,7 @@ authRouter.get( ); authRouter.post('/login', guestMiddleware, catchError(authController.login)); -authRouter.post( - '/refresh', - guestMiddleware, - catchError(authController.refresh), -); +authRouter.post('/refresh', catchError(authController.refresh)); authRouter.post( '/password/reset', diff --git a/src/routers/user.route.js b/src/routers/user.route.js index b9eafad3..0a1b748b 100644 --- a/src/routers/user.route.js +++ b/src/routers/user.route.js @@ -31,6 +31,7 @@ userRouter.patch( userRouter.get( '/profile/email/change/activation/:emailChangeToken', + authMiddleware, catchError(userController.activateChangedEmail), ); From 84ca95d1c0d54cf1861500694b0387383f667eb5 Mon Sep 17 00:00:00 2001 From: Tymur Date: Tue, 2 Jun 2026 20:05:00 +0300 Subject: [PATCH 4/4] add task solution --- src/middlewares/guestMiddleware.js | 4 ++-- src/routers/user.route.js | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/middlewares/guestMiddleware.js b/src/middlewares/guestMiddleware.js index fa73d241..95944ccc 100644 --- a/src/middlewares/guestMiddleware.js +++ b/src/middlewares/guestMiddleware.js @@ -1,5 +1,5 @@ -import { ApiError } from '../exceptions/api.error'; -import { jwtService } from '../services/jwt.service'; +import { ApiError } from '../exceptions/api.error.js'; +import { jwtService } from '../services/jwt.service.js'; export const guestMiddleware = (req, res, next) => { const authorization = req.headers.authorization; diff --git a/src/routers/user.route.js b/src/routers/user.route.js index 0a1b748b..e78b71c4 100644 --- a/src/routers/user.route.js +++ b/src/routers/user.route.js @@ -2,6 +2,7 @@ import express from 'express'; import { userController } from '../controllers/user.controller.js'; import { authMiddleware } from '../middlewares/authMiddleware.js'; import { catchError } from '../utils/catchError.js'; +import { guestMiddleware } from '../middlewares/guestMiddleware.js'; export const userRouter = express.Router(); @@ -31,7 +32,7 @@ userRouter.patch( userRouter.get( '/profile/email/change/activation/:emailChangeToken', - authMiddleware, + guestMiddleware, catchError(userController.activateChangedEmail), );