diff --git a/app/components/auth/signUp.scheme.ts b/app/components/auth/signUp.scheme.ts index 6227695..ffc2ee7 100644 --- a/app/components/auth/signUp.scheme.ts +++ b/app/components/auth/signUp.scheme.ts @@ -34,12 +34,8 @@ export const signUpScheme: FastifySchema = { export const signUpConfirmScheme: FastifySchema = { - params: { - type: 'object', - required: ['code'], - properties: { - code: {type: 'string'} - }, + querystring: { + code: {type: 'string'} }, response: { 200: { diff --git a/app/components/auth/signUp.ts b/app/components/auth/signUp.ts index 6028302..b1ebcab 100644 --- a/app/components/auth/signUp.ts +++ b/app/components/auth/signUp.ts @@ -5,6 +5,7 @@ import {createTransport} from 'nodemailer'; import {SMTPOpt} from '@/assist/mail'; import {randomBytes} from 'crypto'; import {env} from '@/envConfig'; +import * as crypto from 'crypto'; interface IBody { email: string @@ -24,13 +25,15 @@ export const signUp = async (server: FastifyInstance) => { if (rowCount) { const transporter = createTransport(SMTPOpt); - const link = `http://${env.nodeEnv === 'dev' ? 'localhost:3000' : '51.15.71.195'}/confirm-email/` + linkCode; + const link = `http://${env.nodeEnv === 'development' ? 'localhost:3000' : '51.15.71.195'}/signup/confirm/?code=${linkCode}`; const mailOptions = { to: email, subject: 'Confirmation of registration', html: `

Hello.

Please click on the link to confirm your registration.

` }; + const sent = await transporter.sendMail(mailOptions); + if (sent) { return reply.status(201).send('An email has been sent to your email address'); } else { @@ -43,17 +46,17 @@ export const signUp = async (server: FastifyInstance) => { ); }; -interface IParams { +interface IQuerystring { code: string } export const signUpConfirm = async (server: FastifyInstance) => { - server.get<{Params: IParams}>( - '/confirm-email/:code', + server.get<{Querystring: IQuerystring}>( + '/signup/confirm/', {schema: signUpConfirmScheme}, async (req, reply) => { - const {code} = req.params; - const {rowCount} = await server.pg.query('UPDATE root.users SET confirmed = true, code = NULL WHERE code = $1 AND confirmed = false', [code]); + const {code} = req.query; + const {rowCount} = await server.pg.query('UPDATE root.users SET confirmed = true, code = NULL WHERE code = $1 AND confirmed = false', [code]); if (rowCount) { reply.type('text/html').send('

Registration has been successfully confirmed!

'); } else { diff --git a/app/components/users/passRecovery.scheme.ts b/app/components/users/passRecovery.scheme.ts index 4d749a2..4d3dc8e 100644 --- a/app/components/users/passRecovery.scheme.ts +++ b/app/components/users/passRecovery.scheme.ts @@ -1,39 +1,51 @@ import {FastifySchema} from 'fastify'; import {emailRegex, passRegex} from '@/validation/regex'; -export const passRecoveryScheme: FastifySchema = { +export const passResetScheme: FastifySchema = { body: { type: 'object', - oneOf: [ - { - required: ['email'], - properties: { - email: { - type: 'string', - pattern: emailRegex, - }, - }, + required: ['email'], + properties: { + email: { + type: 'string', + pattern: emailRegex, }, - { - required: ['code', 'password', 'confirmPassword'], - properties: { - email: { - type: 'string', - pattern: emailRegex, - }, - password: { - type: 'string', - pattern: passRegex, - }, - confirmPassword: { - type: 'string', - const: { - $data: '1/password' - } - }, - }, + }, + }, + response: { + 200: { + type: 'string', + }, + 500: { + type: 'string', + }, + }, +}; + + +export const usersPasswordScheme: FastifySchema = { + body: { + type: 'object', + required: ['email', 'code', 'password', 'confirmPassword'], + properties: { + email: { + type: 'string', + pattern: emailRegex, + }, + code: { + type: 'string', }, - ] + password: { + type: 'string', + pattern: passRegex, + }, + confirmPassword: { + type: 'string', + const: { + $data: '1/password' + } + }, + }, }, response: { 200: { @@ -45,3 +57,5 @@ export const passRecoveryScheme: FastifySchema = { }, }; + + diff --git a/app/components/users/passRecovery.ts b/app/components/users/passRecovery.ts index f0a370f..e7801aa 100644 --- a/app/components/users/passRecovery.ts +++ b/app/components/users/passRecovery.ts @@ -1,55 +1,61 @@ import {FastifyInstance} from 'fastify'; import {randomBytes} from 'crypto'; -import {passRecoveryScheme} from '@/components/users/passRecovery.scheme'; +import {passResetScheme, usersPasswordScheme} from '@/components/users/passRecovery.scheme' import {createTransport} from 'nodemailer'; import {SMTPOpt} from '@/assist/mail'; import {sha256} from '@/components/auth/assistant'; -interface IBody { - email?: string, - code?: string, - password?: string, - confirmPassword?: string +interface IBodyPassReset { + email: string, +} +interface IBodyUsersPassword { + email: string, + code: string, + password: string, + confirmPassword: string } export const passRecovery = async (server: FastifyInstance) => { - server.patch<{Body: IBody}>( - '/users/password-recovery', - {schema: passRecoveryScheme}, + server.patch<{Body: IBodyPassReset}>( + '/password-reset', + {schema: passResetScheme}, async (req, reply) => { - const {email, code, password, confirmPassword} = req.body; + const {email} = req.body; - if (email) { - const recoveryCode = randomBytes(48).toString('hex').substring(0, 63); - const {rowCount} = await server.pg.query('UPDATE root.users SET code = $1 WHERE email = $2', [recoveryCode, email]); + const recoveryCode = Math.floor(Math.random() * 10000000).toString(); + const {rowCount} = await server.pg.query('UPDATE root.users SET code = $1 WHERE email = $2', [recoveryCode, email]); - if (rowCount) { - const transporter = createTransport(SMTPOpt); - const mailOptions = { - to: email, - subject: 'Password recovery', - html: `

Your recovery code:
${recoveryCode}
Please copy it to the app

` - }; - const sent = await transporter.sendMail(mailOptions); + if (rowCount) { + const transporter = createTransport(SMTPOpt); + const mailOptions = { + to: email, + subject: 'Password recovery', + html: `

Your recovery code: ${recoveryCode}
Please copy it to the app

` + }; + const sent = await transporter.sendMail(mailOptions); - if (sent) { - return reply.status(200).send('An email has been sent to your email address'); - } else { - return reply.status(500).send('Couldn\'t send email.'); - } + if (sent) { + return reply.status(200).send('An email has been sent to your email address'); } else { - return reply.status(500).send('Failed to write to the database'); + return reply.status(500).send('Couldn\'t send email.'); } + } else { + return reply.status(500).send('Failed to write to the database'); } + }); + + server.patch<{Body: IBodyUsersPassword}>( + '/users/password', + {schema: usersPasswordScheme}, + async (req, reply) => { + const {email, code, password} = req.body; - if (code && password && confirmPassword) { - const hash = sha256(password); - const {rowCount} = await server.pg.query('UPDATE root.users SET code = NULL, password_hash = $1 WHERE code = $2', [hash, code]); + const hash = sha256(password); + const {rowCount} = await server.pg.query('UPDATE root.users SET code = NULL, password_hash = $1 WHERE code = $2 AND email=$3', [hash, code, email]); - if (rowCount) { - return reply.send('You have successfully changed your password!'); - } else { - return reply.status(500).send('Something went wrong. Check the code'); - } + if (rowCount) { + return reply.send('You have successfully changed your password!'); + } else { + return reply.status(500).send('Something went wrong. Check the code'); } }); }; diff --git a/app/envConfig.ts b/app/envConfig.ts index f402aee..ff1965e 100644 --- a/app/envConfig.ts +++ b/app/envConfig.ts @@ -20,5 +20,5 @@ export const env:TEnv = { mailClientId: process.env.MAIL_OAUTH_CLIENTID as string, mailClientSecret: process.env.MAIL_OAUTH_CLIENT_SECRET as string, mailRefreshToken: process.env.MAIL_OAUTH_REFRESH_TOKEN as string, - mailAccessToken: process.env.MAIL_OAUTH_ACCESS_TOKEN as string + mailAccessToken: process.env.MAIL_OAUTH_ACCESS_TOKEN as string, }; diff --git a/package.json b/package.json index 904e1bb..dd050ef 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,8 @@ "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "start": "node -r dotenv/config dist/app.js", - "build": "webpack && ts-node dev-helpers/create-package.ts", + "build:dev": "webpack --mode=development && ts-node dev-helpers/create-package.ts", + "build:prod": "webpack --mode=production && ts-node dev-helpers/create-package.ts", "lint": "eslint .", "lint:fix": "eslint --fix --ext .ts, app/app.ts", "prepare": "husky install" diff --git a/webpack.config.ts b/webpack.config.ts index b2acec2..75e98ba 100644 --- a/webpack.config.ts +++ b/webpack.config.ts @@ -5,7 +5,6 @@ import {externals} from './dev-helpers/dependencies'; export default { target: 'node', - mode: 'production', entry: resolve('app/app.ts'), output: { path: resolve('dist'),