Skip to content

Commit

Permalink
work on signup and password recovery, change build script
Browse files Browse the repository at this point in the history
  • Loading branch information
mk1020 committed Jun 14, 2021
1 parent b1da9f5 commit c346b9b
Show file tree
Hide file tree
Showing 7 changed files with 98 additions and 79 deletions.
8 changes: 2 additions & 6 deletions app/components/auth/signUp.scheme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: {
Expand Down
15 changes: 9 additions & 6 deletions app/components/auth/signUp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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: `<h3>Hello.</h3> <p>Please click on the <a href=${link}><b>link</b></a> to confirm your registration.</p>`
};

const sent = await transporter.sendMail(mailOptions);

if (sent) {
return reply.status(201).send('An email has been sent to your email address');
} else {
Expand All @@ -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('<h2>Registration has been successfully confirmed!</h2>');
} else {
Expand Down
72 changes: 43 additions & 29 deletions app/components/users/passRecovery.scheme.ts
Original file line number Diff line number Diff line change
@@ -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: {
Expand All @@ -45,3 +57,5 @@ export const passRecoveryScheme: FastifySchema = {
},
};



76 changes: 41 additions & 35 deletions app/components/users/passRecovery.ts
Original file line number Diff line number Diff line change
@@ -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: `<p>Your recovery code: <br> <b>${recoveryCode}</b> <br>Please copy it to the app</p>`
};
const sent = await transporter.sendMail(mailOptions);
if (rowCount) {
const transporter = createTransport(SMTPOpt);
const mailOptions = {
to: email,
subject: 'Password recovery',
html: `<p>Your recovery code: <b>${recoveryCode}</b> <br>Please copy it to the app</p>`
};
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');
}
});
};
2 changes: 1 addition & 1 deletion app/envConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
1 change: 0 additions & 1 deletion webpack.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'),
Expand Down

0 comments on commit c346b9b

Please sign in to comment.