diff --git a/src/config/database.js b/src/config/database.js
new file mode 100644
index 00000000..45ae4e94
--- /dev/null
+++ b/src/config/database.js
@@ -0,0 +1,16 @@
+/* eslint-disable no-console */
+require('dotenv').config();
+
+const { Sequelize } = require('sequelize');
+
+const sequelize = new Sequelize(
+ process.env.DATABASE,
+ process.env.USERNAME_DATABASE,
+ process.env.PASSWORD_DATABASE,
+ {
+ host: process.env.HOST_DATABASE,
+ dialect: 'postgres',
+ },
+);
+
+module.exports = { sequelize };
diff --git a/src/controllers/auth.controller.js b/src/controllers/auth.controller.js
new file mode 100644
index 00000000..0ebfdf37
--- /dev/null
+++ b/src/controllers/auth.controller.js
@@ -0,0 +1,106 @@
+/* eslint-disable no-console */
+const User = require('../models/user.model');
+const bcrypt = require('bcrypt');
+const {
+ validateEmail,
+ validatePassword,
+ send,
+} = require('../services/auth.services');
+const { v4: uuidv4 } = require('uuid');
+const jwt = require('jsonwebtoken');
+
+const registration = async (req, res) => {
+ const { name, email, password } = req.body;
+ const errorEmail = validateEmail(email);
+ const errorPassword = validatePassword(password);
+
+ if (errorEmail) {
+ return res.status(400).send(errorEmail);
+ }
+
+ if (errorPassword) {
+ return res.status(400).send(errorPassword);
+ }
+
+ const exist = await User.findOne({ where: { email } });
+
+ if (exist !== null) {
+ return res.status(401).send('User already exist');
+ }
+
+ const cashedPassword = await bcrypt.hash(password, 10);
+
+ const uuid = uuidv4();
+
+ await User.create({
+ name: name,
+ email: email,
+ password: cashedPassword,
+ activationToken: uuid,
+ });
+
+ await send(
+ email,
+ 'Activate email',
+ `go to link http://localhost:3000/activation/${uuid}`,
+ );
+ res.status(201).send('User created');
+};
+
+const activation = async (req, res) => {
+ const { activationToken } = req.params;
+
+ const user = await User.findOne({ where: { activationToken } });
+
+ if (!user) {
+ return res.status(404).send('Invalid activation token');
+ }
+
+ user.isActivated = true;
+ user.activationToken = null;
+ await user.save();
+ res.redirect('http://localhost:3000/profile');
+};
+
+const login = async (req, res) => {
+ const { email, password } = req.body;
+
+ const existUser = await User.findOne({ where: { email } });
+
+ if (!existUser) {
+ return res.status(404).send('User not found');
+ }
+
+ const isValid = await bcrypt.compare(password, existUser.password);
+
+ if (!isValid) {
+ return res.status(401).send('Invalid credentials');
+ }
+
+ if (!existUser.isActivated) {
+ await send(
+ email,
+ 'Activate email',
+ `go to link http://localhost:3000/activation/${existUser.activationToken}`,
+ );
+
+ return res.status(403).send('Activate email first');
+ }
+
+ const token = jwt.sign(
+ { id: existUser.id, email: existUser.email },
+ process.env.JWT_SECRET,
+ { expiresIn: '30 days' },
+ );
+
+ res.cookie('token', token, { httpOnly: true });
+ res.redirect('http://localhost:3000/profile');
+};
+
+module.exports = {
+ authController: {
+ registration,
+ activation,
+ login,
+ },
+};
diff --git a/src/controllers/user.controller.js b/src/controllers/user.controller.js
new file mode 100644
index 00000000..f5f4592d
--- /dev/null
+++ b/src/controllers/user.controller.js
@@ -0,0 +1,167 @@
+/* eslint-disable no-console */
+const User = require('../models/user.model');
+const bcrypt = require('bcrypt');
+const { v4: uuidv4 } = require('uuid');
+
+const {
+ validateEmail,
+ validatePassword,
+ send,
+} = require('../services/auth.services');
+
+const profile = async (req, res) => {
+ const userId = req.user.id;
+
+ const user = await User.findByPk(userId, {
+ attributes: ['id', 'name', 'email'],
+ });
+
+ res.send(user);
+};
+
+const changeName = async (req, res) => {
+ const userId = req.user.id;
+ const { name } = req.body;
+
+ const user = await User.findByPk(userId);
+
+ user.name = name;
+ await user.save();
+ res.send('Username changed');
+};
+
+const changePassword = async (req, res) => {
+ const userId = req.user.id;
+ const { password, newPassword, confirm } = req.body;
+
+ if (newPassword !== confirm) {
+ return res.send('Passwords do not match');
+ }
+
+ const user = await User.findByPk(userId);
+
+ const isValid = await bcrypt.compare(password, user.password);
+
+ if (!isValid) {
+ return res.status(401).send('Invalid credentials');
+ }
+
+ user.password = await bcrypt.hash(newPassword, 10);
+ await user.save();
+ res.send('password changed');
+};
+
+const resetPassword = async (req, res) => {
+ const { email } = req.body;
+
+ const user = await User.findOne({ where: { email } });
+
+ if (!user) {
+ return res.status(401).send('Invalid credentials');
+ }
+
+ const token = uuidv4();
+
+ user.resetToken = token;
+
+ await send(
+ user.email,
+ 'password reset',
+ `go to link http://localhost:3000/reset/${token}`,
+ );
+
+ await user.save();
+ res.send('email sent');
+};
+
+const resetPasswordConfirm = async (req, res) => {
+ const { resetToken } = req.params;
+ const { password, repeatPassword } = req.body;
+
+ const errorPassword = validatePassword(password);
+
+ if (errorPassword) {
+ return res.status(400).send(errorPassword);
+ }
+
+ if (password !== repeatPassword) {
+ return res.send('passwords not equil');
+ }
+
+ const user = await User.findOne({ where: { resetToken } });
+
+ if (!user) {
+ return res.status(401).send('Invalid credentials');
+ }
+
+ user.password = await bcrypt.hash(password, 10);
+ user.resetToken = null;
+ await user.save();
+
+ return res.send(`
+
Password changed successfully
+ Go to login
+`);
+};
+
+const changeEmail = async (req, res) => {
+ const userId = req.user.id;
+ const { password, email, confirmEmail } = req.body;
+
+ const user = await User.findByPk(userId);
+
+ const isValid = await bcrypt.compare(password, user.password);
+
+ if (!isValid) {
+ return res.status(401).send('Invalid credentials');
+ }
+
+ if (email !== confirmEmail) {
+ return res.status(400).send('Emails do not match');
+ }
+
+ const errorEmail = validateEmail(email);
+
+ if (errorEmail) {
+ return res.status(400).send(errorEmail);
+ }
+
+ const uuid = uuidv4();
+
+ await send(
+ user.email,
+ 'Email change notification',
+ 'Your email is being changed.',
+ );
+
+ await send(
+ email,
+ 'Activate new email',
+ `Go to link http://localhost:3000/activation/${uuid}`,
+ );
+
+ user.email = email;
+ user.isActivated = false;
+ user.activationToken = uuid;
+
+ await user.save();
+
+ return res.redirect('/login');
+};
+
+const logout = async (req, res) => {
+ res.clearCookie('token');
+ res.redirect('/login');
+};
+
+module.exports = {
+ userController: {
+ profile,
+ changeName,
+ changePassword,
+ changeEmail,
+ logout,
+ resetPassword,
+ resetPasswordConfirm,
+ },
+};
diff --git a/src/index.js b/src/index.js
index ad9a93a7..046d24b8 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1 +1,38 @@
+/* eslint-disable max-len */
+/* eslint-disable no-console */
'use strict';
+
+const express = require('express');
+const cookieParser = require('cookie-parser');
+const { sequelize } = require('../src/config/database'); // import connect to database
+const { router: authRouter } = require('../src/routes/auth.routes');
+const { router: userRouter } = require('../src/routes/user.routes'); // import routes
+
+async function start() {
+ try {
+ await sequelize.authenticate(); // connect to database
+ console.log('database connected');
+
+ await sequelize.sync({ alter: true }); // create tables
+ console.log('tables created');
+
+ const app = express(); // create express app
+
+ app.use(cookieParser());
+ app.use(express.json());
+
+ app.use(authRouter);
+ app.use(userRouter);
+
+ // 404 handler
+ app.use((req, res) => {
+ res.status(404).send('Page not found');
+ });
+
+ app.listen(3000, () => console.log('server running on 3000 port')); // start server
+ } catch (err) {
+ console.error('unable connect to database', err);
+ }
+}
+
+start();
diff --git a/src/middlewares/auth.middleware.js b/src/middlewares/auth.middleware.js
new file mode 100644
index 00000000..62ea70f9
--- /dev/null
+++ b/src/middlewares/auth.middleware.js
@@ -0,0 +1,32 @@
+const jwt = require('jsonwebtoken');
+
+require('dotenv').config();
+
+function authMiddleware(req, res, next) {
+ const token = req.cookies.token;
+
+ if (!token) {
+ return res.status(401).send('unauthorized');
+ }
+
+ try {
+ const userData = jwt.verify(token, process.env.JWT_SECRET);
+
+ req.user = userData;
+ next();
+ } catch {
+ return res.status(401).send('Invalid token');
+ }
+}
+
+function guestMiddleware(req, res, next) {
+ const token = req.cookies.token;
+
+ if (token) {
+ return res.redirect('/profile');
+ }
+
+ next();
+}
+
+module.exports = { authMiddleware, guestMiddleware };
diff --git a/src/models/user.model.js b/src/models/user.model.js
new file mode 100644
index 00000000..1dc1510f
--- /dev/null
+++ b/src/models/user.model.js
@@ -0,0 +1,33 @@
+const { DataTypes } = require('sequelize');
+const { sequelize } = require('../config/database');
+
+const Users = sequelize.define('Users', {
+ name: {
+ type: DataTypes.STRING,
+ allowNull: false,
+ },
+ email: {
+ type: DataTypes.STRING,
+ allowNull: false,
+ unique: true,
+ },
+ password: {
+ type: DataTypes.STRING,
+ allowNull: false,
+ },
+ isActivated: {
+ type: DataTypes.BOOLEAN,
+ allowNull: true,
+ defaultValue: false,
+ },
+ activationToken: {
+ type: DataTypes.STRING,
+ allowNull: true,
+ },
+ resetToken: {
+ type: DataTypes.STRING,
+ allowNull: true,
+ },
+});
+
+module.exports = Users;
diff --git a/src/routes/auth.routes.js b/src/routes/auth.routes.js
new file mode 100644
index 00000000..9fd3f93a
--- /dev/null
+++ b/src/routes/auth.routes.js
@@ -0,0 +1,16 @@
+const express = require('express');
+const router = express.Router();
+
+const { authController } = require('../controllers/auth.controller');
+const { guestMiddleware } = require('../middlewares/auth.middleware');
+
+router.post('/registration', guestMiddleware, authController.registration);
+
+router.get(
+ '/activation/:activationToken',
+ guestMiddleware,
+ authController.activation,
+);
+router.post('/login', guestMiddleware, authController.login);
+
+module.exports = { router };
diff --git a/src/routes/user.routes.js b/src/routes/user.routes.js
new file mode 100644
index 00000000..6f44719e
--- /dev/null
+++ b/src/routes/user.routes.js
@@ -0,0 +1,29 @@
+/* eslint-disable no-unused-vars */
+const express = require('express');
+const router = express.Router();
+
+const { userController } = require('../controllers/user.controller');
+const {
+ authMiddleware,
+ guestMiddleware,
+} = require('../middlewares/auth.middleware');
+
+router.get('/profile', authMiddleware, userController.profile);
+router.patch('/profile/name', authMiddleware, userController.changeName);
+
+router.patch(
+ '/profile/password',
+ authMiddleware,
+ userController.changePassword,
+);
+router.patch('/profile/email', authMiddleware, userController.changeEmail);
+router.post('/logout', authMiddleware, userController.logout);
+router.post('/reset', guestMiddleware, userController.resetPassword);
+
+router.post(
+ '/reset/:resetToken',
+ guestMiddleware,
+ userController.resetPasswordConfirm,
+);
+
+module.exports = { router };
diff --git a/src/services/auth.services.js b/src/services/auth.services.js
new file mode 100644
index 00000000..c44b404f
--- /dev/null
+++ b/src/services/auth.services.js
@@ -0,0 +1,48 @@
+const nodemailer = require('nodemailer');
+
+require('dotenv').config();
+
+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';
+ }
+}
+
+function validatePassword(password) {
+ if (!password) {
+ return 'Password is required';
+ }
+
+ if (password.length < 6) {
+ return 'At least 6 characters';
+ }
+}
+
+const transporter = nodemailer.createTransport({
+ service: 'gmail',
+ auth: {
+ user: process.env.NODEMAILER_LOGIN,
+ pass: process.env.NODEMAILER_PASSWORD,
+ },
+});
+
+function send(email, subject, html) {
+ return transporter.sendMail({
+ from: 'Auth API',
+ to: email,
+ subject,
+ html,
+ });
+}
+
+module.exports = {
+ validateEmail,
+ validatePassword,
+ send,
+};