From 54bb8807cc53be4f00e96366616797b3ddf00801 Mon Sep 17 00:00:00 2001 From: Oleh Mysko Date: Fri, 21 Nov 2025 14:38:11 +0200 Subject: [PATCH 1/7] 10%complete --- src/controllers/auth.controller.js | 9 +++++++++ src/createServer.js | 24 ++++++++++++++++++++++++ src/index.js | 18 ++++++++++++++++++ src/models/user.js | 15 +++++++++++++++ src/routes/auth.route.js | 6 ++++++ src/utils/db.js | 12 ++++++++++++ 6 files changed, 84 insertions(+) create mode 100644 src/controllers/auth.controller.js create mode 100644 src/createServer.js create mode 100644 src/models/user.js create mode 100644 src/routes/auth.route.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..4e83b6bd --- /dev/null +++ b/src/controllers/auth.controller.js @@ -0,0 +1,9 @@ +import {user} from '../models/user.js' +const register = async (req, res) => { + const { email, password } = req.body; + const newUser = await user.create({email, password}) + res.send(newUser) +} +export const authController = { + register + } diff --git a/src/createServer.js b/src/createServer.js new file mode 100644 index 00000000..c839523c --- /dev/null +++ b/src/createServer.js @@ -0,0 +1,24 @@ +import express from 'express'; +import 'dotenv/config'; +import { authRouter } from './routes/auth.route.js'; +import cors from 'cors'; + + + +export function createServer() { + const app = express(); + app.use(express.json()); + + app.use(cors({ + origin: process.env.CLIENT_HOST, + credentials:true + })); + + app.use(authRouter); + app.get('/', (req, res) => { + res.send("hello") + }) + return app; +} + + diff --git a/src/index.js b/src/index.js index ad9a93a7..67766cf0 100644 --- a/src/index.js +++ b/src/index.js @@ -1 +1,19 @@ 'use strict'; +import 'dotenv/config'; +import { createServer } from './createServer.js'; +const PORT = process.env.PORT || 3004; + + +async function start () { + try { + +createServer().listen(PORT, () => { + + console.log(`server runing on ${PORT}`) +}) + }catch { + console.log('error start server') +} +} +start() + diff --git a/src/models/user.js b/src/models/user.js new file mode 100644 index 00000000..e6c6a029 --- /dev/null +++ b/src/models/user.js @@ -0,0 +1,15 @@ +import { DataTypes } from "sequelize"; +import { client } from "../utils/db.js"; + +export const user = client.define('user', { + email: { + type: DataTypes.STRING, + allowNull: false, + unique: true, + }, + password: { + type: DataTypes.STRING, + allowNull: false, + } +}) + diff --git a/src/routes/auth.route.js b/src/routes/auth.route.js new file mode 100644 index 00000000..59005056 --- /dev/null +++ b/src/routes/auth.route.js @@ -0,0 +1,6 @@ +import express from 'express'; +import { authController } from '../controllers/auth.controller.js'; + +export const authRouter = new express.Router(); + +authRouter.post('/registration',authController.register) diff --git a/src/utils/db.js b/src/utils/db.js new file mode 100644 index 00000000..846bda99 --- /dev/null +++ b/src/utils/db.js @@ -0,0 +1,12 @@ +import { Sequelize } from "sequelize"; + +export const client = new Sequelize({ + host: process.env.DB_HOST, + username: process.env.DB_USER, + password: process.env.DB_PASSWORD, + database: process.env.DB_DATABASE, + dialect: 'postgres', + port: process.env.DB_PORT || 5432, +}) + + From 14db4a4201c1a6158f5c5fba3203e65c1a3cbc25 Mon Sep 17 00:00:00 2001 From: Oleh Mysko Date: Thu, 27 Nov 2025 18:08:34 +0200 Subject: [PATCH 2/7] 30%complete --- src/controllers/auth.controller.js | 52 ++++++++++++++++++++++++++--- src/controllers/user.controller.js | 11 ++++++ src/createServer.js | 26 +++++++++------ src/exeptions/api.error.js | 33 ++++++++++++++++++ src/index.js | 22 ++++++------ src/middlewares/authMiddlewares.js | 18 ++++++++++ src/middlewares/errorMiddlewares.js | 9 +++++ src/models/user.js | 13 +++++--- src/routes/auth.route.js | 5 ++- src/routes/user.route.js | 10 ++++++ src/services/jwt.service.js | 19 +++++++++++ src/services/mail.services.js | 35 +++++++++++++++++++ src/services/user.services.js | 19 +++++++++++ src/utils/catchError.js | 9 +++++ src/utils/db.js | 6 ++-- 15 files changed, 249 insertions(+), 38 deletions(-) create mode 100644 src/controllers/user.controller.js create mode 100644 src/exeptions/api.error.js create mode 100644 src/middlewares/authMiddlewares.js create mode 100644 src/middlewares/errorMiddlewares.js create mode 100644 src/routes/user.route.js create mode 100644 src/services/jwt.service.js create mode 100644 src/services/mail.services.js create mode 100644 src/services/user.services.js create mode 100644 src/utils/catchError.js diff --git a/src/controllers/auth.controller.js b/src/controllers/auth.controller.js index 4e83b6bd..8da0f787 100644 --- a/src/controllers/auth.controller.js +++ b/src/controllers/auth.controller.js @@ -1,9 +1,51 @@ -import {user} from '../models/user.js' +import { User } from '../models/user.js'; +import { emailServices } from '../services/mail.services.js'; +import { v4 as uuidv4 } from 'uuid'; +import { userService } from '../services/user.services.js'; + +import { jwtService } from '../services/jwt.service.js'; const register = async (req, res) => { const { email, password } = req.body; - const newUser = await user.create({email, password}) - res.send(newUser) + const activationToken = uuidv4() + const newUser = await User.create({ email, password ,activationToken}); + +await emailServices.sendActivationEmail(email,activationToken) + res.send(newUser); + + + +}; + +const activate = async (req, res) => { + const{ activationToken} = req.params + const user = await User.findOne({ where: { activationToken } }) + if (!user) { + res.sendStatus(404) + return + } + user.activationToken = null + user.save() + res.send(user) } + +const login = async (req, res) => { + const { email, password } = req.body + const user = await userService.findByEmail(email); + if (!user || user.password !== password) { + res.send(401); + return + + } + const normalizeUser = userService.normalize(user) + const accessToken = jwtService.sign(normalizeUser) + res.send({ + user: normalizeUser, + accessToken + }) +} + export const authController = { - register - } + register, + activate, + login +}; diff --git a/src/controllers/user.controller.js b/src/controllers/user.controller.js new file mode 100644 index 00000000..91a421eb --- /dev/null +++ b/src/controllers/user.controller.js @@ -0,0 +1,11 @@ + +import { userService } from '../services/user.services.js'; + +const getAllActivated = async (req, res) => { + const user = await userService.getAllActivated() + res.send(user.map(userService.normalize)) +} + +export const userController = { + getAllActivated +}; diff --git a/src/createServer.js b/src/createServer.js index c839523c..6c0325ab 100644 --- a/src/createServer.js +++ b/src/createServer.js @@ -1,24 +1,28 @@ import express from 'express'; import 'dotenv/config'; import { authRouter } from './routes/auth.route.js'; -import cors from 'cors'; - - +import cors from 'cors'; +import { userRouter } from './routes/user.route.js'; +import { errorMiddlewares } from './middlewares/errorMiddlewares.js'; export function createServer() { const app = express(); + app.use(express.json()); - app.use(cors({ - origin: process.env.CLIENT_HOST, - credentials:true - })); + app.use( + cors({ + origin: process.env.CLIENT_HOST, + credentials: true, + }), + ); app.use(authRouter); + app.use('/users', userRouter); + app.get('/', (req, res) => { - res.send("hello") - }) + res.send('hello'); + }); +app.use(errorMiddlewares) return app; } - - diff --git a/src/exeptions/api.error.js b/src/exeptions/api.error.js new file mode 100644 index 00000000..80345f2e --- /dev/null +++ b/src/exeptions/api.error.js @@ -0,0 +1,33 @@ +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 user', + errors, + status: 404 + }) +} + +} diff --git a/src/index.js b/src/index.js index 67766cf0..a2dee405 100644 --- a/src/index.js +++ b/src/index.js @@ -1,19 +1,17 @@ +/* eslint-disable no-console */ 'use strict'; import 'dotenv/config'; import { createServer } from './createServer.js'; -const PORT = process.env.PORT || 3004; +const PORT = process.env.PORT || 3004; -async function start () { +async function start() { try { - -createServer().listen(PORT, () => { - - console.log(`server runing on ${PORT}`) -}) - }catch { - console.log('error start server') -} + createServer().listen(PORT, () => { + console.log(`server runing on ${PORT}`); + }); + } catch { + console.log('error start server'); + } } -start() - +start(); diff --git a/src/middlewares/authMiddlewares.js b/src/middlewares/authMiddlewares.js new file mode 100644 index 00000000..225aa854 --- /dev/null +++ b/src/middlewares/authMiddlewares.js @@ -0,0 +1,18 @@ +import { jwtService } from "../services/jwt.service.js"; + +export const authMiddlewares = (req, res , next) => { + const authorization = req.headers['authorization'] || ''; + const [, token] = authorization.split(' ') + + if (!authorization || !token) { + res.sendStatus(401) + return + } + + const userData = jwtService.verify(token) + if (!userData) { + res.sendStatus(401) + return + } + next() +} diff --git a/src/middlewares/errorMiddlewares.js b/src/middlewares/errorMiddlewares.js new file mode 100644 index 00000000..6d393ed0 --- /dev/null +++ b/src/middlewares/errorMiddlewares.js @@ -0,0 +1,9 @@ +export const errorMiddlewares = (error, req, res, next) => { + if (error) { + res.statusCode = 500; + res.send({ + message:'server error' + }) + } + next() +} diff --git a/src/models/user.js b/src/models/user.js index e6c6a029..4376a29e 100644 --- a/src/models/user.js +++ b/src/models/user.js @@ -1,7 +1,7 @@ -import { DataTypes } from "sequelize"; -import { client } from "../utils/db.js"; +import { DataTypes } from 'sequelize'; +import { client } from '../utils/db.js'; -export const user = client.define('user', { +export const User = client.define('user', { email: { type: DataTypes.STRING, allowNull: false, @@ -10,6 +10,9 @@ export const user = client.define('user', { password: { type: DataTypes.STRING, allowNull: false, + }, + activationToken: { + type: DataTypes.STRING, + } -}) - +}); diff --git a/src/routes/auth.route.js b/src/routes/auth.route.js index 59005056..fba224af 100644 --- a/src/routes/auth.route.js +++ b/src/routes/auth.route.js @@ -1,6 +1,9 @@ import express from 'express'; import { authController } from '../controllers/auth.controller.js'; +import { catchError } from '../utils/catchError.js'; export const authRouter = new express.Router(); -authRouter.post('/registration',authController.register) +authRouter.post('/registration', catchError( authController.register)); +authRouter.get('/activation/:email/:activationToken',catchError( authController.activate)); +authRouter.post('/login',catchError(authController.login)) diff --git a/src/routes/user.route.js b/src/routes/user.route.js new file mode 100644 index 00000000..228674a7 --- /dev/null +++ b/src/routes/user.route.js @@ -0,0 +1,10 @@ +import express from 'express'; +import { userController } from '../controllers/user.controller.js'; +import { authMiddlewares } from '../middlewares/authMiddlewares.js'; +import { catchError } from '../utils/catchError.js'; + + +export const userRouter = new express.Router(); + + +userRouter.get('/',authMiddlewares, catchError(userController.getAllActivated)); diff --git a/src/services/jwt.service.js b/src/services/jwt.service.js new file mode 100644 index 00000000..55375daf --- /dev/null +++ b/src/services/jwt.service.js @@ -0,0 +1,19 @@ +import jwt from 'jsonwebtoken'; +import 'dotenv/config'; +function sign(user) { + const token = jwt.sign({ user }, process.env.JWT_KEY) + return token +} + +function verify(token) { + try { + return jwt.verify(token,process.env.JWT_KEY) + } catch { + return null + } + +} +export const jwtService = { + sign, + verify +} diff --git a/src/services/mail.services.js b/src/services/mail.services.js new file mode 100644 index 00000000..279ffbf1 --- /dev/null +++ b/src/services/mail.services.js @@ -0,0 +1,35 @@ + +import nodemailer from 'nodemailer' +import 'dotenv/config' + +const transporter = nodemailer.createTransport({ + host: process.env.SMTP_HOST, + port: process.env.SMTP_PORT, + secure: true, // true for 465, false for other ports + auth: { + user: process.env.SMTP_USER, + pass: process.env.SMTP_PASSWORD, + }, +}); + +export function send({ email, subject, html }) +{ + return transporter.sendMail({ + to: email, + subject: subject, + html: html, + }) +} + +function sendActivationEmail(email, token) { + const href = `${process.env.CLIENT_HOST}/activate/${encodeURIComponent(email)}/${token}` + const html = `< h1 > ActivateAcount + ${href} ` + return send({ + email, + html, subject: 'activate' + }) + } +console.log('email send') + +export const emailServices = {send, sendActivationEmail} diff --git a/src/services/user.services.js b/src/services/user.services.js new file mode 100644 index 00000000..d040cf5f --- /dev/null +++ b/src/services/user.services.js @@ -0,0 +1,19 @@ +import {User} from '../models/user.js' + function getAllActivated() { + return User.findAll({ + where: { activationToken: null } + }) +} + + function normalize( {id, email} ) { + return{id,email} + } + +function findByEmail(email) { + return User.findOne({where:{email}}) +} + +export const userService = { + getAllActivated, normalize + , findByEmail +} diff --git a/src/utils/catchError.js b/src/utils/catchError.js new file mode 100644 index 00000000..0187c78f --- /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 index 846bda99..668b0887 100644 --- a/src/utils/db.js +++ b/src/utils/db.js @@ -1,4 +1,4 @@ -import { Sequelize } from "sequelize"; +import { Sequelize } from 'sequelize'; export const client = new Sequelize({ host: process.env.DB_HOST, @@ -7,6 +7,4 @@ export const client = new Sequelize({ database: process.env.DB_DATABASE, dialect: 'postgres', port: process.env.DB_PORT || 5432, -}) - - +}); From 536070d3a53ab3088ac5fa5207cc5703704e87ab Mon Sep 17 00:00:00 2001 From: Oleh Mysko Date: Tue, 2 Dec 2025 21:02:56 +0200 Subject: [PATCH 3/7] 50% --- src/controllers/auth.controller.js | 71 +++++++++++++++++++---------- src/controllers/user.controller.js | 10 ++-- src/createServer.js | 3 +- src/exeptions/api.error.js | 26 +++++------ src/middlewares/authMiddlewares.js | 23 ++++++---- src/middlewares/errorMiddlewares.js | 19 ++++++-- src/models/user.js | 3 +- src/routes/auth.route.js | 10 ++-- src/routes/user.route.js | 8 ++-- src/services/jwt.service.js | 15 +++--- src/services/mail.services.js | 25 +++++----- src/services/user.services.js | 41 ++++++++++++----- src/utils/catchError.js | 10 ++-- 13 files changed, 158 insertions(+), 106 deletions(-) diff --git a/src/controllers/auth.controller.js b/src/controllers/auth.controller.js index 8da0f787..a69767b6 100644 --- a/src/controllers/auth.controller.js +++ b/src/controllers/auth.controller.js @@ -1,51 +1,72 @@ import { User } from '../models/user.js'; -import { emailServices } from '../services/mail.services.js'; -import { v4 as uuidv4 } from 'uuid'; + import { userService } from '../services/user.services.js'; import { jwtService } from '../services/jwt.service.js'; -const register = async (req, res) => { - const { email, password } = req.body; - const activationToken = uuidv4() - const newUser = await User.create({ email, password ,activationToken}); +import { ApiError } from '../exeptions/api.error.js'; + -await emailServices.sendActivationEmail(email,activationToken) - res.send(newUser); +function validateEmail(value) { + const EMAIL_PATTERN = /^[\w.+-]+@([\w-]+\.){1,3}[\w-]{2,}$/; + + if (!value){ return 'Email is required'}; + if (!EMAIL_PATTERN.test(value)) {return 'Email is not valid'}; +} +const validatePassword = (value) => { + if (!value) {return 'Password is required';} + if (value.length < 6) {return 'At least 6 characters'}; +}; +const register = async (req, res,next) => { + const { email, password } = req.body; + const errors = { + email: validateEmail(email), + password:validatePassword(password) +} +if (errors.email || errors.password) { + throw ApiError.badRequest('Bad request', errors) +} + await userService.register(email,password) + res.send({message:'OK'}); }; const activate = async (req, res) => { - const{ activationToken} = req.params - const user = await User.findOne({ where: { activationToken } }) + const { activationToken } = req.params; + const user = await User.findOne({ where: { activationToken } }); + if (!user) { - res.sendStatus(404) - return + res.sendStatus(404); + + return; } - user.activationToken = null - user.save() - res.send(user) -} + user.activationToken = null; + user.save(); + res.send(user); +}; const login = async (req, res) => { - const { email, password } = req.body - const user = await userService.findByEmail(email); + const { email, password } = req.body; + const user = await userService.findByEmail(email); + if (!user || user.password !== password) { res.send(401); - return + return; } - const normalizeUser = userService.normalize(user) - const accessToken = jwtService.sign(normalizeUser) + + const normalizeUser = userService.normalize(user); + const accessToken = jwtService.sign(normalizeUser); + res.send({ user: normalizeUser, - accessToken - }) -} + accessToken, + }); +}; export const authController = { register, activate, - login + login, }; diff --git a/src/controllers/user.controller.js b/src/controllers/user.controller.js index 91a421eb..56d3ec68 100644 --- a/src/controllers/user.controller.js +++ b/src/controllers/user.controller.js @@ -1,11 +1,11 @@ - import { userService } from '../services/user.services.js'; const getAllActivated = async (req, res) => { - const user = await userService.getAllActivated() - res.send(user.map(userService.normalize)) -} + const user = await userService.getAllActivated(); + + res.send(user.map(userService.normalize)); +}; export const userController = { - getAllActivated + getAllActivated, }; diff --git a/src/createServer.js b/src/createServer.js index 6c0325ab..cbb69c4b 100644 --- a/src/createServer.js +++ b/src/createServer.js @@ -23,6 +23,7 @@ export function createServer() { app.get('/', (req, res) => { res.send('hello'); }); -app.use(errorMiddlewares) + app.use(errorMiddlewares); + return app; } diff --git a/src/exeptions/api.error.js b/src/exeptions/api.error.js index 80345f2e..bd9e30d4 100644 --- a/src/exeptions/api.error.js +++ b/src/exeptions/api.error.js @@ -1,33 +1,29 @@ export class ApiError extends Error { - constructor({ message, status, errors={} }) { - + constructor({ message, status, errors = {} }) { super(message); this.status = status; - this.errors = errors - - + this.errors = errors; } static badRequest(message, errors) { return new ApiError({ message, errors, - status: 400 - }) + status: 400, + }); } - static unauthorized( errors) { + static unauthorized(errors) { return new ApiError({ message: 'unauthorized user', errors, - status: 401 - }) + status: 401, + }); } - static notFound( errors) { + static notFound(errors) { return new ApiError({ message: 'not Found user', errors, - status: 404 - }) -} - + status: 404, + }); + } } diff --git a/src/middlewares/authMiddlewares.js b/src/middlewares/authMiddlewares.js index 225aa854..703f2da3 100644 --- a/src/middlewares/authMiddlewares.js +++ b/src/middlewares/authMiddlewares.js @@ -1,18 +1,21 @@ -import { jwtService } from "../services/jwt.service.js"; +import { jwtService } from '../services/jwt.service.js'; -export const authMiddlewares = (req, res , next) => { +export const authMiddlewares = (req, res, next) => { const authorization = req.headers['authorization'] || ''; - const [, token] = authorization.split(' ') + const [, token] = authorization.split(' '); if (!authorization || !token) { - res.sendStatus(401) - return + res.sendStatus(401); + + return; } - const userData = jwtService.verify(token) + const userData = jwtService.verify(token); + if (!userData) { - res.sendStatus(401) - return + res.sendStatus(401); + + return; } - next() -} + next(); +}; diff --git a/src/middlewares/errorMiddlewares.js b/src/middlewares/errorMiddlewares.js index 6d393ed0..6fafc804 100644 --- a/src/middlewares/errorMiddlewares.js +++ b/src/middlewares/errorMiddlewares.js @@ -1,9 +1,18 @@ +import { ApiError } from "../exeptions/api.error.js"; + export const errorMiddlewares = (error, req, res, next) => { - if (error) { + if (error instanceof ApiError) { + res.status(error.status).send({ + message: error.message, + error: error.errors + }) +return +} + res.statusCode = 500; + res.send({ - message:'server error' + message: 'server errorooooooooooooooo', }) - } - next() -} + +}; diff --git a/src/models/user.js b/src/models/user.js index 4376a29e..baae90a2 100644 --- a/src/models/user.js +++ b/src/models/user.js @@ -13,6 +13,5 @@ export const User = client.define('user', { }, activationToken: { type: DataTypes.STRING, - - } + }, }); diff --git a/src/routes/auth.route.js b/src/routes/auth.route.js index fba224af..dc74d8f2 100644 --- a/src/routes/auth.route.js +++ b/src/routes/auth.route.js @@ -4,6 +4,10 @@ import { catchError } from '../utils/catchError.js'; export const authRouter = new express.Router(); -authRouter.post('/registration', catchError( authController.register)); -authRouter.get('/activation/:email/:activationToken',catchError( authController.activate)); -authRouter.post('/login',catchError(authController.login)) +authRouter.post('/registration', catchError(authController.register)); + +authRouter.get( + '/activation/:email/:activationToken', + catchError(authController.activate), +); +authRouter.post('/login', catchError(authController.login)); diff --git a/src/routes/user.route.js b/src/routes/user.route.js index 228674a7..f6d0ce9a 100644 --- a/src/routes/user.route.js +++ b/src/routes/user.route.js @@ -3,8 +3,10 @@ import { userController } from '../controllers/user.controller.js'; import { authMiddlewares } from '../middlewares/authMiddlewares.js'; import { catchError } from '../utils/catchError.js'; - export const userRouter = new express.Router(); - -userRouter.get('/',authMiddlewares, catchError(userController.getAllActivated)); +userRouter.get( + '/', + authMiddlewares, + catchError(userController.getAllActivated), +); diff --git a/src/services/jwt.service.js b/src/services/jwt.service.js index 55375daf..c9eeac00 100644 --- a/src/services/jwt.service.js +++ b/src/services/jwt.service.js @@ -1,19 +1,20 @@ import jwt from 'jsonwebtoken'; import 'dotenv/config'; + function sign(user) { - const token = jwt.sign({ user }, process.env.JWT_KEY) - return token + const token = jwt.sign({ user }, process.env.JWT_KEY); + + return token; } function verify(token) { try { - return jwt.verify(token,process.env.JWT_KEY) + return jwt.verify(token, process.env.JWT_KEY); } catch { - return null + return null; } - } export const jwtService = { sign, - verify -} + verify, +}; diff --git a/src/services/mail.services.js b/src/services/mail.services.js index 279ffbf1..7eba62d6 100644 --- a/src/services/mail.services.js +++ b/src/services/mail.services.js @@ -1,6 +1,5 @@ - -import nodemailer from 'nodemailer' -import 'dotenv/config' +import nodemailer from 'nodemailer'; +import 'dotenv/config'; const transporter = nodemailer.createTransport({ host: process.env.SMTP_HOST, @@ -12,24 +11,24 @@ const transporter = nodemailer.createTransport({ }, }); -export function send({ email, subject, html }) -{ +export function send({ email, subject, html }) { return transporter.sendMail({ to: email, subject: subject, html: html, - }) + }); } function sendActivationEmail(email, token) { - const href = `${process.env.CLIENT_HOST}/activate/${encodeURIComponent(email)}/${token}` + const href = `${process.env.CLIENT_HOST}/activate/${encodeURIComponent(email)}/${token}`; const html = `< h1 > ActivateAcount - ${href} ` + ${href} `; + return send({ email, - html, subject: 'activate' - }) - } -console.log('email send') + html, + subject: 'activate', + }); +} -export const emailServices = {send, sendActivationEmail} +export const emailServices = { send, sendActivationEmail }; diff --git a/src/services/user.services.js b/src/services/user.services.js index d040cf5f..aecfe1ae 100644 --- a/src/services/user.services.js +++ b/src/services/user.services.js @@ -1,19 +1,36 @@ -import {User} from '../models/user.js' - function getAllActivated() { +import { ApiError } from '../exeptions/api.error.js'; +import { User } from '../models/user.js'; +import { emailServices } from '../services/mail.services.js'; +import { v4 as uuidv4 } from 'uuid'; + +function getAllActivated() { return User.findAll({ - where: { activationToken: null } - }) + where: { activationToken: null }, + }); } - function normalize( {id, email} ) { - return{id,email} - } +function normalize({ id, email }) { + return { id, email }; +} function findByEmail(email) { - return User.findOne({where:{email}}) + return User.findOne({ where: { email } }); } - -export const userService = { - getAllActivated, normalize - , findByEmail + +async function register(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({ email, password, activationToken }); + + await emailServices.sendActivationEmail(email, activationToken); } +export const userService = { + getAllActivated, + normalize, + findByEmail,register +}; diff --git a/src/utils/catchError.js b/src/utils/catchError.js index 0187c78f..0e1e7d8f 100644 --- a/src/utils/catchError.js +++ b/src/utils/catchError.js @@ -1,9 +1,9 @@ export const catchError = (action) => { return async function (req, res, next) { try { - await action(req,res,next) + await action(req, res, next); } catch (error) { - next(error) - } - } -} + next(error); + } + }; +}; From f63ad7ad4b66657d7693e441928d91bb0214b34d Mon Sep 17 00:00:00 2001 From: Oleh Mysko Date: Wed, 3 Dec 2025 14:50:47 +0200 Subject: [PATCH 4/7] 55% --- src/controllers/auth.controller.js | 74 +++++++++++++++++++++-------- src/createServer.js | 3 +- src/middlewares/errorMiddlewares.js | 18 +++---- src/models/token.js | 13 +++++ src/routes/auth.route.js | 1 + src/services/jwt.service.js | 20 +++++++- src/services/mail.services.js | 2 +- src/services/token.service.js | 10 ++++ src/services/user.services.js | 13 +++-- 9 files changed, 116 insertions(+), 38 deletions(-) create mode 100644 src/models/token.js create mode 100644 src/services/token.service.js diff --git a/src/controllers/auth.controller.js b/src/controllers/auth.controller.js index a69767b6..1e705f17 100644 --- a/src/controllers/auth.controller.js +++ b/src/controllers/auth.controller.js @@ -4,32 +4,44 @@ import { userService } from '../services/user.services.js'; import { jwtService } from '../services/jwt.service.js'; import { ApiError } from '../exeptions/api.error.js'; - +import bcrypt from 'bcrypt'; function validateEmail(value) { const EMAIL_PATTERN = /^[\w.+-]+@([\w-]+\.){1,3}[\w-]{2,}$/; - if (!value){ return 'Email is required'}; - if (!EMAIL_PATTERN.test(value)) {return 'Email is not valid'}; + if (!value) { + return 'Email is required'; + } + + if (!EMAIL_PATTERN.test(value)) { + return 'Email is not valid'; + } } const validatePassword = (value) => { - if (!value) {return 'Password is required';} - if (value.length < 6) {return 'At least 6 characters'}; + if (!value) { + return 'Password is required'; + } + + if (value.length < 6) { + return 'At least 6 characters'; + } }; -const register = async (req, res,next) => { +const register = async (req, res, next) => { const { email, password } = req.body; - const errors = { - email: validateEmail(email), - password:validatePassword(password) -} -if (errors.email || errors.password) { - throw ApiError.badRequest('Bad request', errors) -} + const errors = { + email: validateEmail(email), + password: validatePassword(password), + }; + + if (errors.email || errors.password) { + throw ApiError.badRequest('Bad request', errors); + } - await userService.register(email,password) - res.send({message:'OK'}); + const hashedPass = await bcrypt.hash(password,10) + await userService.register(email, hashedPass); + res.send({ message: 'OK' }); }; const activate = async (req, res) => { @@ -50,23 +62,43 @@ const login = async (req, res) => { const { email, password } = req.body; const user = await userService.findByEmail(email); - if (!user || user.password !== password) { - res.send(401); + 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) - return; +}; + +const refresh = (req, res) => { + const { refreshToken } = req.cookies; + const user = jwtService.verifyRefresh(refreshToken) + if (!user) { + throw ApiError.unauthorized() } + generateTokens(res, user) +} + +const generateTokens = (res, user) => { const normalizeUser = userService.normalize(user); const accessToken = jwtService.sign(normalizeUser); - + const refreshToken = jwtService.signRefresh(normalizeUser) + res.cookie('refreshToken', refreshToken, { + maxAge:30*24*60*60*1000, + httpOnly: true +}) res.send({ user: normalizeUser, accessToken, }); -}; +} export const authController = { register, activate, - login, + login,refresh }; diff --git a/src/createServer.js b/src/createServer.js index cbb69c4b..67493449 100644 --- a/src/createServer.js +++ b/src/createServer.js @@ -4,12 +4,13 @@ import { authRouter } from './routes/auth.route.js'; import cors from 'cors'; import { userRouter } from './routes/user.route.js'; import { errorMiddlewares } from './middlewares/errorMiddlewares.js'; +import cookieParser from 'cookie-parser'; export function createServer() { const app = express(); app.use(express.json()); - + app.use(cookieParser()); app.use( cors({ origin: process.env.CLIENT_HOST, diff --git a/src/middlewares/errorMiddlewares.js b/src/middlewares/errorMiddlewares.js index 6fafc804..e5b99db6 100644 --- a/src/middlewares/errorMiddlewares.js +++ b/src/middlewares/errorMiddlewares.js @@ -1,18 +1,18 @@ -import { ApiError } from "../exeptions/api.error.js"; +import { ApiError } from '../exeptions/api.error.js'; export const errorMiddlewares = (error, req, res, next) => { if (error instanceof ApiError) { res.status(error.status).send({ message: error.message, - error: error.errors - }) -return -} + error: error.errors, + }); - res.statusCode = 500; + return; + } - res.send({ - message: 'server errorooooooooooooooo', - }) + res.statusCode = 500; + res.send({ + message: 'server errorooooooooooooooo', + }); }; diff --git a/src/models/token.js b/src/models/token.js new file mode 100644 index 00000000..b4d29572 --- /dev/null +++ b/src/models/token.js @@ -0,0 +1,13 @@ +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 + + + } +}) +Token.belongsTo(User) +User.hasOne(Token) diff --git a/src/routes/auth.route.js b/src/routes/auth.route.js index dc74d8f2..1e1a7a31 100644 --- a/src/routes/auth.route.js +++ b/src/routes/auth.route.js @@ -11,3 +11,4 @@ authRouter.get( catchError(authController.activate), ); authRouter.post('/login', catchError(authController.login)); +authRouter.get('/refresh', catchError(authController.refresh)); diff --git a/src/services/jwt.service.js b/src/services/jwt.service.js index c9eeac00..8c603a0b 100644 --- a/src/services/jwt.service.js +++ b/src/services/jwt.service.js @@ -2,7 +2,9 @@ import jwt from 'jsonwebtoken'; import 'dotenv/config'; function sign(user) { - const token = jwt.sign({ user }, process.env.JWT_KEY); + const token = jwt.sign({ user }, process.env.JWT_KEY, { + expiresIn:'5s' + }); return token; } @@ -14,7 +16,23 @@ function verify(token) { 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 { + return null; + } +} export const jwtService = { sign, verify, + verifyRefresh, + signRefresh }; diff --git a/src/services/mail.services.js b/src/services/mail.services.js index 7eba62d6..8cfcdff5 100644 --- a/src/services/mail.services.js +++ b/src/services/mail.services.js @@ -21,7 +21,7 @@ export function send({ email, subject, html }) { function sendActivationEmail(email, token) { const href = `${process.env.CLIENT_HOST}/activate/${encodeURIComponent(email)}/${token}`; - const html = `< h1 > ActivateAcount + const html = `< h1 > ActivateAcount ${href} `; return send({ diff --git a/src/services/token.service.js b/src/services/token.service.js new file mode 100644 index 00000000..179b940c --- /dev/null +++ b/src/services/token.service.js @@ -0,0 +1,10 @@ +async function save(userId,newToken) { + +} +function getByToken(refreshToken) { + +} + +export const tokenService = { + save,getByToken +} diff --git a/src/services/user.services.js b/src/services/user.services.js index aecfe1ae..7e02404a 100644 --- a/src/services/user.services.js +++ b/src/services/user.services.js @@ -18,19 +18,22 @@ function findByEmail(email) { } async function register(email, password) { - const activationToken = uuidv4(); - const existUser = await findByEmail(email) + const existUser = await findByEmail(email); + if (existUser) { - throw ApiError.badRequest('User already exist',{email:'User already exist'}) + throw ApiError.badRequest('User already exist', { + email: 'User already exist', + }); } - await User.create({ email, password, activationToken }); + await User.create({ email, password, activationToken }); await emailServices.sendActivationEmail(email, activationToken); } export const userService = { getAllActivated, normalize, - findByEmail,register + findByEmail, + register, }; From cbb0814fed4461260d56b86a0eb13949c9cf13a3 Mon Sep 17 00:00:00 2001 From: Oleh Mysko Date: Thu, 4 Dec 2025 21:09:39 +0200 Subject: [PATCH 5/7] 60% --- src/controllers/auth.controller.js | 84 +++++++++++++++++++++-------- src/createServer.js | 1 + src/middlewares/errorMiddlewares.js | 2 + src/models/token.js | 18 +++---- src/routes/auth.route.js | 1 + src/services/jwt.service.js | 4 +- src/services/token.service.js | 24 +++++++-- 7 files changed, 96 insertions(+), 38 deletions(-) diff --git a/src/controllers/auth.controller.js b/src/controllers/auth.controller.js index 1e705f17..5de116b2 100644 --- a/src/controllers/auth.controller.js +++ b/src/controllers/auth.controller.js @@ -1,7 +1,7 @@ import { User } from '../models/user.js'; import { userService } from '../services/user.services.js'; - +import { tokenService } from '../services/token.service.js'; import { jwtService } from '../services/jwt.service.js'; import { ApiError } from '../exeptions/api.error.js'; import bcrypt from 'bcrypt'; @@ -39,7 +39,8 @@ const register = async (req, res, next) => { throw ApiError.badRequest('Bad request', errors); } - const hashedPass = await bcrypt.hash(password,10) + const hashedPass = await bcrypt.hash(password, 10); + await userService.register(email, hashedPass); res.send({ message: 'OK' }); }; @@ -62,43 +63,82 @@ const login = async (req, res) => { const { email, password } = req.body; const user = await userService.findByEmail(email); - if (!user ) { - throw ApiError.badRequest('No such user') + if (!user) { + throw ApiError.badRequest('No such user'); } - const isPasswordValid = await bcrypt.compare(password,user.password) + + const isPasswordValid = await bcrypt.compare(password, user.password); + if (!isPasswordValid) { - throw ApiError.badRequest('Wrong password') + throw ApiError.badRequest('Wrong password'); } - generateTokens(res, user) - + generateTokens(res, user); }; - -const refresh = (req, res) => { +const refresh = async (req, res) => { const { refreshToken } = req.cookies; - const user = jwtService.verifyRefresh(refreshToken) + + if (!refreshToken) { + throw ApiError.unauthorized(); + } + + const payload = await jwtService.verifyRefresh(refreshToken); + + if (!payload) { + throw ApiError.unauthorized(); + } + + const tokenRecord = await tokenService.getByToken(refreshToken); + + if (!tokenRecord) { + throw ApiError.unauthorized(); + } + + const { user: payloadUser } = payload; + + const user = await userService.findByEmail(payloadUser.email); + if (!user) { - throw ApiError.unauthorized() + throw ApiError.unauthorized(); } - generateTokens(res, user) -} -const generateTokens = (res, user) => { + generateTokens(res, user); +}; +const generateTokens = async (res, user) => { const normalizeUser = userService.normalize(user); const accessToken = jwtService.sign(normalizeUser); - const refreshToken = jwtService.signRefresh(normalizeUser) - res.cookie('refreshToken', refreshToken, { - maxAge:30*24*60*60*1000, - httpOnly: true -}) + const refreshAccessToken = jwtService.signRefresh(normalizeUser); + + await tokenService.save(normalizeUser.id, refreshAccessToken); + + res.cookie('refreshToken', refreshAccessToken, { + maxAge: 30 * 24 * 60 * 60 * 1000, + httpOnly: true, + }); + res.send({ user: normalizeUser, accessToken, }); +}; + +const logout = async (req, res) => { + const { refreshToken } = req.cookies; + const payload = await jwtService.verifyRefresh(refreshToken); + + if (!refreshToken || !payload) { + throw ApiError.unauthorized(); + } + + await tokenService.remove(payload.user.id); + res.clearCookie('refreshToken', { httpOnly: true }); + res.sendStatus(204); +}; -} export const authController = { register, activate, - login,refresh + login, + refresh, + logout, }; diff --git a/src/createServer.js b/src/createServer.js index 67493449..6cd94dba 100644 --- a/src/createServer.js +++ b/src/createServer.js @@ -11,6 +11,7 @@ export function createServer() { app.use(express.json()); app.use(cookieParser()); + app.use( cors({ origin: process.env.CLIENT_HOST, diff --git a/src/middlewares/errorMiddlewares.js b/src/middlewares/errorMiddlewares.js index e5b99db6..e92d2d09 100644 --- a/src/middlewares/errorMiddlewares.js +++ b/src/middlewares/errorMiddlewares.js @@ -1,6 +1,8 @@ import { ApiError } from '../exeptions/api.error.js'; export const errorMiddlewares = (error, req, res, next) => { + + if (error instanceof ApiError) { res.status(error.status).send({ message: error.message, diff --git a/src/models/token.js b/src/models/token.js index b4d29572..3ae1fdca 100644 --- a/src/models/token.js +++ b/src/models/token.js @@ -1,13 +1,11 @@ -import { DataTypes } from "sequelize"; -import { client } from "../utils/db.js"; -import { User } from "./user.js"; +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 - - - } -}) -Token.belongsTo(User) -User.hasOne(Token) + allowNull: false, + }, +}); +Token.belongsTo(User); +User.hasOne(Token); diff --git a/src/routes/auth.route.js b/src/routes/auth.route.js index 1e1a7a31..37a84e2b 100644 --- a/src/routes/auth.route.js +++ b/src/routes/auth.route.js @@ -12,3 +12,4 @@ authRouter.get( ); authRouter.post('/login', catchError(authController.login)); authRouter.get('/refresh', catchError(authController.refresh)); +authRouter.post('/logout', catchError(authController.logout)); diff --git a/src/services/jwt.service.js b/src/services/jwt.service.js index 8c603a0b..aa061bed 100644 --- a/src/services/jwt.service.js +++ b/src/services/jwt.service.js @@ -3,7 +3,7 @@ import 'dotenv/config'; function sign(user) { const token = jwt.sign({ user }, process.env.JWT_KEY, { - expiresIn:'5s' + expiresIn: '15m', }); return token; @@ -34,5 +34,5 @@ export const jwtService = { sign, verify, verifyRefresh, - signRefresh + signRefresh, }; diff --git a/src/services/token.service.js b/src/services/token.service.js index 179b940c..046474a3 100644 --- a/src/services/token.service.js +++ b/src/services/token.service.js @@ -1,10 +1,26 @@ -async function save(userId,newToken) { +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(); } -function getByToken(refreshToken) { +function getByToken(refreshToken) { + return Token.findOne({ where: { refreshToken } }); } -export const tokenService = { - save,getByToken +function remove(userId) { + return Token.destroy({ where: { userId } }); } +export const tokenService = { + save, + getByToken, + remove, +}; From 34aef24d1b314632a16d3cb2eed4be3e37a36797 Mon Sep 17 00:00:00 2001 From: Oleh Mysko Date: Tue, 9 Dec 2025 18:46:01 +0200 Subject: [PATCH 6/7] solutin --- src/controllers/auth.controller.js | 45 ++++++++- src/controllers/user.controller.js | 142 ++++++++++++++++++++++++++++ src/createServer.js | 4 + src/middlewares/authMiddlewares.js | 1 + src/middlewares/errorMiddlewares.js | 2 - src/models/user.js | 17 ++++ src/routes/auth.route.js | 2 + src/routes/user.route.js | 21 ++++ src/services/mail.services.js | 53 ++++++++++- src/services/user.services.js | 45 ++++++++- 10 files changed, 320 insertions(+), 12 deletions(-) diff --git a/src/controllers/auth.controller.js b/src/controllers/auth.controller.js index 5de116b2..a23b0507 100644 --- a/src/controllers/auth.controller.js +++ b/src/controllers/auth.controller.js @@ -27,21 +27,31 @@ const validatePassword = (value) => { return 'At least 6 characters'; } }; +const validateName = (value) => { + if (!value) { + return 'Name is last'; + } + + if (value.length < 2) { + return 'At least 2 characters'; + } +}; const register = async (req, res, next) => { - const { email, password } = req.body; + const { name, email, password } = req.body; const errors = { + name: validateName(name), email: validateEmail(email), password: validatePassword(password), }; - if (errors.email || errors.password) { + if (errors.email || errors.password || errors.name) { throw ApiError.badRequest('Bad request', errors); } const hashedPass = await bcrypt.hash(password, 10); - await userService.register(email, hashedPass); + await userService.register(name, email, hashedPass); res.send({ message: 'OK' }); }; @@ -55,6 +65,7 @@ const activate = async (req, res) => { return; } user.activationToken = null; + user.save(); res.send(user); }; @@ -67,6 +78,10 @@ const login = async (req, res) => { throw ApiError.badRequest('No such user'); } + if (user.activationToken !== null) { + throw ApiError.badRequest('you must activated your email check your box'); + } + const isPasswordValid = await bcrypt.compare(password, user.password); if (!isPasswordValid) { @@ -135,10 +150,34 @@ const logout = async (req, res) => { res.sendStatus(204); }; +const resetPassword = async (req, res) => { + const { email } = req.body; + const errors = { + email: validateEmail(email), + }; + + if (errors.email) { + throw ApiError.badRequest('Bad request', errors); + } + + await userService.resetPassword(email); + res.send({ message: 'email send' }); +}; + +const confirm = async (req, res) => { + const { password, reppassword, token } = req.body; + + userService.confirmReset(password, reppassword, token); + res.sendStatus(204); +}; + export const authController = { register, activate, login, refresh, logout, + resetPassword, + confirm, + validateEmail, }; diff --git a/src/controllers/user.controller.js b/src/controllers/user.controller.js index 56d3ec68..9eb81ae6 100644 --- a/src/controllers/user.controller.js +++ b/src/controllers/user.controller.js @@ -1,4 +1,10 @@ +import { ApiError } from '../exeptions/api.error.js'; +import { User } from '../models/user.js'; import { userService } from '../services/user.services.js'; +import bcrypt from 'bcrypt'; +import { authController } from './auth.controller.js'; +import { emailServices } from '../services/mail.services.js'; +import { v4 as uuidv4 } from 'uuid'; const getAllActivated = async (req, res) => { const user = await userService.getAllActivated(); @@ -6,6 +12,142 @@ const getAllActivated = async (req, res) => { res.send(user.map(userService.normalize)); }; +const changeName = async (req, res) => { + const { name } = req.body; + const userId = req.user.id; + + if (name.length === 0) { + throw ApiError.badRequest('name is empty'); + } + + if (!userId) { + throw ApiError.badRequest('unautorized'); + } + await User.update({ name }, { where: { id: userId } }); + + res.json({ message: 'Name updated successfully' }); +}; + +const changePassword = async (req, res) => { + const { password, newPassword, confirmPassword } = req.body; + + const userId = req.user.id; + + if (!password || !newPassword || !confirmPassword) { + throw ApiError.badRequest('some passwords are empty'); + } + + if (newPassword !== confirmPassword) { + throw ApiError.badRequest('New password and confirmation do not match'); + } + + if (!userId) { + throw ApiError.badRequest('user unatorization'); + } + + const user = await User.findOne({ where: { id: userId } }); + + if (!user) { + throw ApiError.unauthorized('user unauthorized'); + } + + const isPasswordValid = await bcrypt.compare(password, user.password); + + if (!isPasswordValid) { + throw ApiError.badRequest('Wrong password'); + } + + const hashedPass = await bcrypt.hash(newPassword, 10); + + user.password = hashedPass; + user.save(); + res.json({ message: 'Password updated successfully' }); +}; + +const changeEmail = async (req, res) => { + const { email, password } = req.body; + const userId = req.user.id; + + if (!email || !password) { + throw ApiError.badRequest('empty email or password'); + } + + const errors = { + email: authController.validateEmail(email), + }; + + if (errors.email) { + throw ApiError.badRequest('error email'); + } + + const user = await User.findOne({ where: { id: userId } }); + + if (!user) { + throw ApiError.unauthorized('unauthorized'); + } + + const isPasswordValid = await bcrypt.compare(password, user.password); + + if (!isPasswordValid) { + throw ApiError.badRequest('wrong password'); + } + + const token = uuidv4(); + + user.resetEmailToken = token; + user.newEmail = email; + await user.save(); + + await emailServices.sendChangeNewEmail(user.email, email, token); + + res.send({ message: 'email sent' }); +}; + +const confirmNewEmail = async (req, res) => { + const { token } = req.query; + const activeToken = uuidv4(); + + if (!token) { + throw ApiError.badRequest('invalid token'); + } + + const findUser = await User.findOne({ where: { resetEmailToken: token } }); + + if (!findUser) { + throw ApiError.badRequest(' user not found'); + } + + findUser.resetEmailToken = null; + findUser.activationToken = activeToken; + await findUser.save(); + await emailServices.sendNewEmail(findUser.newEmail, activeToken); + + res.send(200); +}; + +const finallConfirm = async (req, res) => { + const { token } = req.query; + + if (!token) { + throw ApiError.badRequest('invalid token'); + } + + const findUser = await User.findOne({ where: { activationToken: token } }); + + if (!findUser) { + throw ApiError.badRequest('no user'); + } + findUser.email = findUser.newEmail; + findUser.newEmail = null; + await findUser.save(); + res.json('email is active now '); +}; + export const userController = { getAllActivated, + changeName, + changePassword, + changeEmail, + confirmNewEmail, + finallConfirm, }; diff --git a/src/createServer.js b/src/createServer.js index 6cd94dba..dc7572ca 100644 --- a/src/createServer.js +++ b/src/createServer.js @@ -27,5 +27,9 @@ export function createServer() { }); app.use(errorMiddlewares); + app.use('*', (req, res) => { + res.status(404).json({ message: 'Not Found' }); + }); + return app; } diff --git a/src/middlewares/authMiddlewares.js b/src/middlewares/authMiddlewares.js index 703f2da3..9f189303 100644 --- a/src/middlewares/authMiddlewares.js +++ b/src/middlewares/authMiddlewares.js @@ -17,5 +17,6 @@ export const authMiddlewares = (req, res, next) => { return; } + req.user = userData.user; next(); }; diff --git a/src/middlewares/errorMiddlewares.js b/src/middlewares/errorMiddlewares.js index e92d2d09..e5b99db6 100644 --- a/src/middlewares/errorMiddlewares.js +++ b/src/middlewares/errorMiddlewares.js @@ -1,8 +1,6 @@ import { ApiError } from '../exeptions/api.error.js'; export const errorMiddlewares = (error, req, res, next) => { - - if (error instanceof ApiError) { res.status(error.status).send({ message: error.message, diff --git a/src/models/user.js b/src/models/user.js index baae90a2..68a7b2f8 100644 --- a/src/models/user.js +++ b/src/models/user.js @@ -2,6 +2,10 @@ 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, @@ -14,4 +18,17 @@ export const User = client.define('user', { activationToken: { type: DataTypes.STRING, }, + resetToken: { + type: DataTypes.STRING, + allowNull: true, + }, + resetEmailToken: { + type: DataTypes.STRING, + allowNull: true, + }, + newEmail: { + type: DataTypes.STRING, + allowNull: true, + unique: true, + }, }); diff --git a/src/routes/auth.route.js b/src/routes/auth.route.js index 37a84e2b..ce12e07e 100644 --- a/src/routes/auth.route.js +++ b/src/routes/auth.route.js @@ -13,3 +13,5 @@ authRouter.get( authRouter.post('/login', catchError(authController.login)); authRouter.get('/refresh', catchError(authController.refresh)); authRouter.post('/logout', catchError(authController.logout)); +authRouter.post('/reset', catchError(authController.resetPassword)); +authRouter.post('/confirm-reset', catchError(authController.confirm)); diff --git a/src/routes/user.route.js b/src/routes/user.route.js index f6d0ce9a..0f069fcf 100644 --- a/src/routes/user.route.js +++ b/src/routes/user.route.js @@ -10,3 +10,24 @@ userRouter.get( authMiddlewares, catchError(userController.getAllActivated), ); + +userRouter.patch( + '/change-name', + authMiddlewares, + catchError(userController.changeName), +); + +userRouter.post( + '/change-password', + authMiddlewares, + catchError(userController.changePassword), +); + +userRouter.post( + '/change-email', + authMiddlewares, + catchError(userController.changeEmail), +); + +userRouter.get('/confirm-email', catchError(userController.confirmNewEmail)); +userRouter.get('/confirm-finall', catchError(userController.finallConfirm)); diff --git a/src/services/mail.services.js b/src/services/mail.services.js index 8cfcdff5..f861c752 100644 --- a/src/services/mail.services.js +++ b/src/services/mail.services.js @@ -19,10 +19,10 @@ export function send({ email, subject, html }) { }); } -function sendActivationEmail(email, token) { +function sendActivationEmail(name, email, token) { const href = `${process.env.CLIENT_HOST}/activate/${encodeURIComponent(email)}/${token}`; - const html = `< h1 > ActivateAcount - ${href} `; + const html = `

ActivateAcount from ${name}

+ ${href} `; return send({ email, @@ -31,4 +31,49 @@ function sendActivationEmail(email, token) { }); } -export const emailServices = { send, sendActivationEmail }; +function sendResetEmail(email, token) { + const href = `${process.env.CLIENT_HOST}/reset/${encodeURIComponent(email)}/${token}`; + const html = `

Reset password for ${email}

+ ${href} `; + + return send({ + email, + html, + subject: 'resetPassword', + }); +} + +function sendChangeNewEmail(email, newEmail, token) { + const href = `${process.env.CLIENT_HOST}/change/${encodeURIComponent(email)}/${token}`; + + const html = `

Reset email for ${email}

+

ON ${newEmail}

+ ${href} `; + + return send({ + email, + html, + subject: 'changeEmail', + }); +} + +function sendNewEmail(email, token) { + const href = `${process.env.CLIENT_HOST}/confirm/${encodeURIComponent(email)}/${token}`; + + const html = `

your new email for login ${email}

+

ON ${email}

+ ${href} `; + + return send({ + email, + html, + subject: 'New Email', + }); +} +export const emailServices = { + send, + sendActivationEmail, + sendResetEmail, + sendChangeNewEmail, + sendNewEmail, +}; diff --git a/src/services/user.services.js b/src/services/user.services.js index 7e02404a..f09ec8d5 100644 --- a/src/services/user.services.js +++ b/src/services/user.services.js @@ -2,6 +2,7 @@ import { ApiError } from '../exeptions/api.error.js'; import { User } from '../models/user.js'; import { emailServices } from '../services/mail.services.js'; import { v4 as uuidv4 } from 'uuid'; +import bycrypt from 'bcrypt'; function getAllActivated() { return User.findAll({ @@ -17,7 +18,7 @@ function findByEmail(email) { return User.findOne({ where: { email } }); } -async function register(email, password) { +async function register(name, email, password) { const activationToken = uuidv4(); const existUser = await findByEmail(email); @@ -27,13 +28,51 @@ async function register(email, password) { }); } - await User.create({ email, password, activationToken }); + await User.create({ + name, + email, + password, + activationToken, + }); + + await emailServices.sendActivationEmail(name, email, activationToken); +} + +async function resetPassword(email) { + const resToken = uuidv4(); + const findUser = await findByEmail(email); + + if (!findUser) { + throw ApiError.badRequest('sorry'); + } + findUser.resetToken = resToken; + findUser.save(); + await emailServices.sendResetEmail(email, resToken); +} - await emailServices.sendActivationEmail(email, activationToken); +async function confirmReset(password1, password2, resetToken) { + if (password1 !== password2 || !resetToken) { + throw ApiError.badRequest('Password do not mach'); + } + + const findUser = await User.findOne({ where: { resetToken } }); + + if (!findUser) { + throw ApiError.badRequest('try again'); + } + + const hashedPass = await bycrypt.hash(password1, 10); + + findUser.resetToken = null; + findUser.password = hashedPass; + findUser.save(); } + export const userService = { getAllActivated, normalize, findByEmail, register, + resetPassword, + confirmReset, }; From 21df4a4a757264b6726032fd79fad16fdc506c29 Mon Sep 17 00:00:00 2001 From: Oleh Mysko Date: Tue, 9 Dec 2025 18:59:48 +0200 Subject: [PATCH 7/7] solutin2 --- src/controllers/auth.controller.js | 6 +++--- src/controllers/user.controller.js | 3 ++- src/services/mail.services.js | 6 +++--- src/services/user.services.js | 6 +++--- 4 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/controllers/auth.controller.js b/src/controllers/auth.controller.js index a23b0507..f02e21a7 100644 --- a/src/controllers/auth.controller.js +++ b/src/controllers/auth.controller.js @@ -66,7 +66,7 @@ const activate = async (req, res) => { } user.activationToken = null; - user.save(); + await user.save(); res.send(user); }; @@ -87,7 +87,7 @@ const login = async (req, res) => { if (!isPasswordValid) { throw ApiError.badRequest('Wrong password'); } - generateTokens(res, user); + await generateTokens(res, user); }; const refresh = async (req, res) => { const { refreshToken } = req.cookies; @@ -167,7 +167,7 @@ const resetPassword = async (req, res) => { const confirm = async (req, res) => { const { password, reppassword, token } = req.body; - userService.confirmReset(password, reppassword, token); + await userService.confirmReset(password, reppassword, token); res.sendStatus(204); }; diff --git a/src/controllers/user.controller.js b/src/controllers/user.controller.js index 9eb81ae6..8b57b8e5 100644 --- a/src/controllers/user.controller.js +++ b/src/controllers/user.controller.js @@ -60,7 +60,7 @@ const changePassword = async (req, res) => { const hashedPass = await bcrypt.hash(newPassword, 10); user.password = hashedPass; - user.save(); + await user.save(); res.json({ message: 'Password updated successfully' }); }; @@ -139,6 +139,7 @@ const finallConfirm = async (req, res) => { } findUser.email = findUser.newEmail; findUser.newEmail = null; + findUser.activationToken = null; await findUser.save(); res.json('email is active now '); }; diff --git a/src/services/mail.services.js b/src/services/mail.services.js index f861c752..d1cf0aa0 100644 --- a/src/services/mail.services.js +++ b/src/services/mail.services.js @@ -21,7 +21,7 @@ export function send({ email, subject, html }) { function sendActivationEmail(name, email, token) { const href = `${process.env.CLIENT_HOST}/activate/${encodeURIComponent(email)}/${token}`; - const html = `

ActivateAcount from ${name}

+ const html = `

Activate Account from ${name}

${href} `; return send({ @@ -46,7 +46,7 @@ function sendResetEmail(email, token) { function sendChangeNewEmail(email, newEmail, token) { const href = `${process.env.CLIENT_HOST}/change/${encodeURIComponent(email)}/${token}`; - const html = `

Reset email for ${email}

+ const html = `

change of email ${email}

ON ${newEmail}

${href} `; @@ -61,7 +61,7 @@ function sendNewEmail(email, token) { const href = `${process.env.CLIENT_HOST}/confirm/${encodeURIComponent(email)}/${token}`; const html = `

your new email for login ${email}

-

ON ${email}

+

ON ${email}

${href} `; return send({ diff --git a/src/services/user.services.js b/src/services/user.services.js index f09ec8d5..eca9505d 100644 --- a/src/services/user.services.js +++ b/src/services/user.services.js @@ -46,13 +46,13 @@ async function resetPassword(email) { throw ApiError.badRequest('sorry'); } findUser.resetToken = resToken; - findUser.save(); + await findUser.save(); await emailServices.sendResetEmail(email, resToken); } async function confirmReset(password1, password2, resetToken) { if (password1 !== password2 || !resetToken) { - throw ApiError.badRequest('Password do not mach'); + throw ApiError.badRequest('Password do not match'); } const findUser = await User.findOne({ where: { resetToken } }); @@ -65,7 +65,7 @@ async function confirmReset(password1, password2, resetToken) { findUser.resetToken = null; findUser.password = hashedPass; - findUser.save(); + await findUser.save(); } export const userService = {