Commit c1f6e9ad authored by Pavel Mishakov's avatar Pavel Mishakov

webinar is done with email reset password BACK

parent 2e137c79
......@@ -18,6 +18,7 @@
"mongoose": "^7.0.3",
"multer": "^1.4.5-lts.1",
"nanoid": "^4.0.1",
"nodemailer": "^6.9.1",
"pg": "^8.9.0",
"pg-hstore": "^2.3.4",
"sequelize": "^6.29.0",
......@@ -35,6 +36,7 @@
"@types/mongoose": "^5.11.97",
"@types/multer": "^1.4.7",
"@types/nanoid": "^3.0.0",
"@types/nodemailer": "^6.4.7",
"@types/shortid": "^0.0.29",
"@types/uuidv4": "^5.0.0",
"@types/validator": "^13.7.12",
......@@ -277,6 +279,15 @@
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.14.1.tgz",
"integrity": "sha512-QH+37Qds3E0eDlReeboBxfHbX9omAcBCXEzswCu6jySP642jiM3cYSIkU/REqwhCUqXdonHFuBfJDiAJxMNhaQ=="
},
"node_modules/@types/nodemailer": {
"version": "6.4.7",
"resolved": "https://registry.npmjs.org/@types/nodemailer/-/nodemailer-6.4.7.tgz",
"integrity": "sha512-f5qCBGAn/f0qtRcd4SEn88c8Fp3Swct1731X4ryPKqS61/A3LmmzN8zaEz7hneJvpjFbUUgY7lru/B/7ODTazg==",
"dev": true,
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/qs": {
"version": "6.9.7",
"resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz",
......@@ -1654,6 +1665,14 @@
"webidl-conversions": "^3.0.0"
}
},
"node_modules/nodemailer": {
"version": "6.9.1",
"resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.1.tgz",
"integrity": "sha512-qHw7dOiU5UKNnQpXktdgQ1d3OFgRAekuvbJLcdG5dnEo/GtcTHRYM7+UfJARdOFU9WUQO8OiIamgWPmiSFHYAA==",
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/nopt": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz",
......@@ -2959,6 +2978,15 @@
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.14.1.tgz",
"integrity": "sha512-QH+37Qds3E0eDlReeboBxfHbX9omAcBCXEzswCu6jySP642jiM3cYSIkU/REqwhCUqXdonHFuBfJDiAJxMNhaQ=="
},
"@types/nodemailer": {
"version": "6.4.7",
"resolved": "https://registry.npmjs.org/@types/nodemailer/-/nodemailer-6.4.7.tgz",
"integrity": "sha512-f5qCBGAn/f0qtRcd4SEn88c8Fp3Swct1731X4ryPKqS61/A3LmmzN8zaEz7hneJvpjFbUUgY7lru/B/7ODTazg==",
"dev": true,
"requires": {
"@types/node": "*"
}
},
"@types/qs": {
"version": "6.9.7",
"resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz",
......@@ -4014,6 +4042,11 @@
}
}
},
"nodemailer": {
"version": "6.9.1",
"resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.1.tgz",
"integrity": "sha512-qHw7dOiU5UKNnQpXktdgQ1d3OFgRAekuvbJLcdG5dnEo/GtcTHRYM7+UfJARdOFU9WUQO8OiIamgWPmiSFHYAA=="
},
"nopt": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz",
......
......@@ -19,6 +19,7 @@
"mongoose": "^7.0.3",
"multer": "^1.4.5-lts.1",
"nanoid": "^4.0.1",
"nodemailer": "^6.9.1",
"pg": "^8.9.0",
"pg-hstore": "^2.3.4",
"sequelize": "^6.29.0",
......@@ -36,6 +37,7 @@
"@types/mongoose": "^5.11.97",
"@types/multer": "^1.4.7",
"@types/nanoid": "^3.0.0",
"@types/nodemailer": "^6.4.7",
"@types/shortid": "^0.0.29",
"@types/uuidv4": "^5.0.0",
"@types/validator": "^13.7.12",
......
......@@ -6,6 +6,7 @@ import { EStatuses } from "../enums/EStatuses"
import IUserGetDto from "../interfaces/IUserGetDto"
import { auth } from "../middlewares/auth"
import IRequestWithTokenData from "../interfaces/IRequestWithTokenData"
import jwt from 'jsonwebtoken'
......@@ -20,6 +21,8 @@ export class UserController {
this.router.post('/login', this.login)
this.router.get('/token', auth, this.checkToken)
this.router.put('/', auth, this.editUser)
this.router.post('/forgot-password', this.sendEmailPassword)
this.router.put('/reset-password', this.resetPassword)
}
public getRouter = (): Router => {
return this.router
......@@ -55,6 +58,18 @@ export class UserController {
}
res.status(200).send(response)
}
private sendEmailPassword = async (req: Request, res: Response): Promise<void> => {
const response: IResponse<undefined> = await this.service.sendEmailPassword(req.body)
res.status(200).send(response)
}
private resetPassword = async (req: Request, res: Response): Promise<void> => {
const user = jwt.verify(req.body.token || '', process.env.SECRET_KEY || '') as {isResetPassword: boolean, _id: string}
if (!user || !user.isResetPassword) return
const response: IResponse<undefined> = await this.service.resetPassword(req.body.password, user._id)
res.status(200).send(response)
}
}
export const userController = new UserController()
\ No newline at end of file
import nodemailer, { Transporter } from "nodemailer"
class EmailService {
private transporter: Transporter | null = null
public init = (): void => {
try {
this.transporter = nodemailer.createTransport({
host: 'smtp.gmail.com',
auth: {
user: process.env.EMAIL_USER,
pass: process.env.EMAIL_PASS,
},
})
console.log('Email service transporter is ready')
} catch (err: unknown) {
console.log(err)
}
}
public sendEmail = async (to: string, subject: string, html: string): Promise<void> => {
try {
if (!this.transporter) return
await this.transporter.sendMail({
to,
subject,
html
})
} catch (err: unknown) {
console.log(err)
}
}
}
export const emailService = new EmailService()
\ No newline at end of file
import jwt from 'jsonwebtoken'
export const generateJWT = (payload: {[key: string]: string | number | boolean}) => {
return jwt.sign(payload, process.env.SECRET_KEY || '', {expiresIn: '2h'})
export const generateJWT = (payload: {[key: string]: string | number | boolean}, lifeTime: string | number) => {
return jwt.sign(payload, process.env.SECRET_KEY || '', {expiresIn: lifeTime})
}
\ No newline at end of file
......@@ -10,6 +10,7 @@ import IDataBase from './interfaces/IDataBase'
import { SuppliersController } from './controllers/suppliers'
import { UserController } from './controllers/user'
import { mongo } from './repository/mongo'
import { emailService } from './email/emailService'
dotenv.config()
class App {
......@@ -42,6 +43,7 @@ class App {
// process.on('exit', () => {
// currentDb.close()
// })
emailService.init()
mongo.init()
process.on('exit', () => {
mongo.close()
......
import IUser from "./IUser";
export default interface IUserResetPasswordDto {
password: IUser['password']
token: string
}
\ No newline at end of file
......@@ -15,6 +15,8 @@ import IUserCreateDto from '../interfaces/IUserCreateDto'
import IUser from '../interfaces/IUser'
import IUserGetDto from '../interfaces/IUserGetDto'
import { generateJWT } from '../helpers/generateJWT'
import { emailService } from '../email/emailService'
import bcrypt from 'bcrypt'
dotenv.config()
export class Mongo implements IDataBase {
......@@ -259,7 +261,7 @@ export class Mongo implements IDataBase {
const data = {
_id: user._id,
username: user.username,
token: generateJWT({_id: user._id, username: user.username})
token: generateJWT({_id: user._id, username: user.username}, '2h')
}
return {
status: EStatuses.OK,
......@@ -281,15 +283,16 @@ export class Mongo implements IDataBase {
if (!user) {
throw new Error('Not found')
}
const isMatch: boolean = user.checkPassword(userDto.password)
const isMatch: boolean = await user.checkPassword(userDto.password)
if (!isMatch) {
throw new Error('Wrong password')
}
console.log('IS MATHC!!!!!!!! ', isMatch)
const data = {
_id: user._id,
username: user.username,
token: generateJWT({_id: user._id, username: user.username})
token: generateJWT({_id: user._id, username: user.username}, '2h')
}
await user.save()
return {
......@@ -329,7 +332,9 @@ export class Mongo implements IDataBase {
username: userDto.username
}
if (userDto.password) {
update.password = userDto.password
const salt = await bcrypt.genSalt(parseInt(process.env.BCRYPT_SALT || '') || 10)
const hash = await bcrypt.hash(userDto.password, salt)
update.password = hash
}
const user: IUserGetDto | null = await User.findOneAndUpdate({_id: id}, update, {
new: true
......@@ -348,6 +353,56 @@ export class Mongo implements IDataBase {
}
}
public sendEmailPassword = async (email: string): Promise<IResponse<undefined>> => {
try {
const user = await User.findOne({username: email})
if (user) {
const token = generateJWT({_id: user._id, isResetPassword: true}, '2m')
emailService.sendEmail(
'pashamishakov@gmail.com',
'Webinar 29 March',
`<h1>Hello ${user.username}</h1>
<p>Please click the link:</p>
<div>
<a href='http://localhost:3001/reset-password?token=${token}'>CLICK ME TO RESET YOUR PASSWORD</a>
</div>
`
)
}
return {
status: EStatuses.OK,
result: undefined,
message: `Email was sent to ${email}`
}
} catch(err: unknown) {
return {
status: EStatuses.NOT_OK,
result: undefined,
message: `Email was sent to ${email}`
}
}
}
public resetPassword = async (password: string, _id: string): Promise<IResponse<undefined>> => {
try {
const salt = await bcrypt.genSalt(parseInt(process.env.BCRYPT_SALT || '') || 10)
const hash = await bcrypt.hash(password, salt)
await User.findOneAndUpdate({_id: _id}, {password: hash})
return {
status: EStatuses.OK,
result: undefined,
message: 'Password successfully was changed'
}
} catch(err: unknown) {
return {
status: EStatuses.NOT_OK,
result: undefined,
message: '[ERROR] Cannot change password'
}
}
}
}
export const mongo = new Mongo()
\ No newline at end of file
......@@ -26,6 +26,14 @@ export class UserService {
public getUsers = async (): Promise<IResponse<IUser[] | undefined>> => {
return await this.repository.getUsers()
}
public sendEmailPassword = async (req: {email: string}): Promise<IResponse<undefined>> => {
return await this.repository.sendEmailPassword(req.email)
}
public resetPassword = async (password: string, _id: string): Promise<IResponse<undefined>> => {
return await this.repository.resetPassword(password, _id)
}
}
export const userService = new UserService()
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment