add reset password

This commit is contained in:
Le Viet
2022-03-09 17:46:51 +07:00
parent 5a22668153
commit d04fd654eb
31 changed files with 539 additions and 9563 deletions
Regular → Executable
+2
View File
@@ -1,2 +1,4 @@
NODE_ENV=development
PORT=8000
TOKEN_SECRET=cfe41fd581767805dddabab7e1ca15c8
TOKEN_EXPIRES=1800s
Regular → Executable
View File
Executable
+8
View File
@@ -0,0 +1,8 @@
const path = require('path');
module.exports = {
"config": path.resolve('./src/server/config', 'config.json'),
"models-path": path.resolve('./src/server/models'),
"seeders-path": path.resolve('./src/server/seeders'),
"migrations-path": path.resolve('./src/server/migrations')
};
Regular → Executable
+1 -1
View File
@@ -1,4 +1,4 @@
FROM node:10.15.0-alpine
FROM node:12-alpine
EXPOSE 3000 9229
WORKDIR /home/app
Executable
View File
Regular → Executable
-2
View File
@@ -1,2 +0,0 @@
# FloatApi
BIN
View File
Binary file not shown.
+8
View File
@@ -0,0 +1,8 @@
# docker-compose.override.yml
version: "3"
services:
app:
env_file: .env
volumes:
- .:/home/app/
- /home/app/node_modules
Regular → Executable
+2 -2
View File
@@ -3,8 +3,6 @@ version: "3"
services:
app:
build: .
volumes:
- ./:/home/app
depends_on:
- postgres
ports:
@@ -13,6 +11,8 @@ services:
postgres:
image: postgres:12.0-alpine
volumes:
- ./database:/var/lib/postgresql
environment:
POSTGRES_USER: savvy
POSTGRES_PASSWORD: savvy001!
-9353
View File
File diff suppressed because it is too large Load Diff
Regular → Executable
+14 -8
View File
@@ -1,10 +1,10 @@
{
"name": "floatapi",
"name": "floatpapi",
"version": "1.0.0",
"description": "",
"main": "app.js",
"scripts": {
"start:dev": "./node_modules/.bin/nodemon app.js",
"start:dev": "./node_modules/.bin/nodemon ./src/bin/www -w app.js -w src/server",
"dev-test": "nodemon -x 'npm test'",
"test": "mocha --timeout 1500"
},
@@ -13,24 +13,30 @@
"dependencies": {
"bcrypt": "^5.0.1",
"body-parser": "1.19.0",
"crypto": "^1.0.1",
"dotenv": "^16.0.0",
"express": "4.17.1",
"express-validator": "^6.14.0",
"jsonwebtoken": "^8.5.1",
"morgan": "1.9.1",
"node-gyp": "^9.0.0",
"nodemailer": "^6.7.2",
"nodemon": "^2.0.15",
"pg": "7.12.1",
"pg-hstore": "2.3.3",
"sequelize": "5.19.6"
"pg": "^7.12.1",
"pg-hstore": "^2.3.4",
"pg-promise": "^10.11.1",
"postgres": "^1.0.2",
"sequelize": "^6.17.0",
"sequelize-cli": "^6.4.1"
},
"devDependencies": {
"chai": "4.2.0",
"chai-http": "4.3.0",
"eslint": "6.5.1",
"eslint-config-airbnb": "18.0.1",
"eslint": "^8.10.0",
"eslint-config-airbnb": "^18.2.1",
"eslint-plugin-import": "2.18.2",
"eslint-plugin-jsx-a11y": "6.2.3",
"eslint-plugin-react": "7.16.0",
"mocha": "6.2.1"
"mocha": "^6.2.3"
}
}
+3 -3
View File
@@ -3,8 +3,8 @@
if [ "$NODE_ENV" == "production" ] ; then
npm run start
else
npx sequelize db:migrate
npx sequelize db:seed:all
npm run test
#npx sequelize db:migrate
#npx sequelize db:seed:all
#npm run test
npm run start:dev
fi
+5
View File
@@ -0,0 +1,5 @@
{
"extends": "airbnb",
"installedESLint": true,
"plugins": ["react"]
}
Regular → Executable
+8 -2
View File
@@ -7,10 +7,16 @@ app.use(logger("dev"));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
require("./src/server/routes")(app);
require("./server/routes")(app);
app.get("/api", (req, res) =>
res.status(200).send({
message: "Welcome to Float API"
})
);
app.get("*", (req, res) =>
res.status(200).send({
message: "Welcome to the beginning of nothingness."
message: "Welcome to Float Mobility."
})
);
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
+54 -61
View File
@@ -1,13 +1,11 @@
var Sequelize = require('sequelize');
const nodemailer = require('nodemailer');
const { body, validationResult } = require('express-validator');
const member = require("../models").member;
const memberServices = require("../services").members;
const memberServices = require("../services").member;
module.exports = {
validate(method) {
switch (method) {
case 'createUser':
case 'register':
{
return [
body('username', 'userName doesn\'t exists').exists(),
@@ -23,14 +21,17 @@ module.exports = {
console.log(errors)
return res.status(400).json({ errors: errors.array() });
}
return memberServices.create(req)
.then(member => res.status(200).send("Register successfully"))
.catch(error => res.status(400).send(error.errors));
.then(member => {
res.status(200).send("Register succesfully")
})
.catch(error => {
res.status(400).send(error.errors)
});
},
async login(req, res) {
return memberServices.login(req)
login(req, res) {
memberServices.login(req)
.then(accessToken => res.status(200).send(accessToken))
.catch(error => res.status(400).send(error.errors));
},
@@ -41,44 +42,41 @@ module.exports = {
.catch(error => res.status(400).send(error.errors));
},
async forgotPassword(req, res) {
try {
const email = req.body.email;
const user = await member.findOne({
where: { username: email }
});
if (user) {
return res.status(200).send('Please check email');
} else {
res.send("Email doen't exist");
}
} catch (error) {
console.log(error);
res.status(500).send("Internal Server error Occured");
}
forgotPassword(req, res) {
memberServices.forgotPassword(req)
.then(result => {
console.log(result)
})
.catch(error => res.status(400).send(error.errors));
},
async resetPassword(req, res) {
const email = req.body.email;
const user = await member.findOne({
where: { username: email }
});
const salt = await bcrypt.genSaltSync(10);
const password = bcrypt.hashSync(req.body.password, salt);
user.password = password;
user.save();
res.status(200).send(user.email);
return member
.update({
password: password
}, { returning: true, where: { id: user.id } })
.then(([rowsUpdate, [updatedRow]]) => res.status(200).send(updatedRow))
.catch(error => res.status(400).send(error));
resetPassword(req, res) {
memberServices.resetPassword(req)
.then(result => {
res.status(200).send(result)
})
.catch(error => res.status(400).send(error.errors));
},
update(req, res) {
return member
.update({
confirmResetPassword(req, res) {
memberServices.confirmResetPassword(req)
.then(result => {
res.status(200).send(result)
})
.catch(error => res.status(400).send(error.errors));
},
completeResetPassword(req, res) {
memberServices.completeResetPassword(req)
.then(result => {
res.status(200).send(result)
})
.catch(error => res.status(400).send(error.errors));
},
updateProfile(req, res) {
return memberServices
.updateProfile({
name: req.body.name,
size: req.body.size,
price: req.body.price,
@@ -88,25 +86,20 @@ module.exports = {
.catch(error => res.status(400).send(error));
},
delete(req, res) {
return member
.destroy({
where: {
id: req.params.id
}
})
.then(rowDeleted => {
if (rowDeleted !== 1) {
throw ({
"name": "ValidationError",
"errors": [{
message: 'Item not found'
}]
});
}
return res.status(200).json({ message: "Deleted successfully" });
})
updatePassword(req, res) {
return memberServices
.updatePassword({
name: req.body.name,
size: req.body.size,
price: req.body.price,
status: req.body.status
}, { returning: true, where: { id: req.params.id } })
.then(([rowsUpdate, [updatedRow]]) => res.status(200).send(updatedRow))
.catch(error => res.status(400).send(error));
},
deactivate(req, res) {
}
};
+20
View File
@@ -0,0 +1,20 @@
const jwt = require("jsonwebtoken");
const config = process.env;
const verifyToken = (req, res, next) => {
const bearToken = req.headers["authorization"];
if (!bearToken) {
return res.status(403).send("A token is required for authentication");
}
try {
const token = bearToken.split(' ');
const decoded = jwt.verify(token[1], process.env.TOKEN_SECRET);
req.user = decoded;
} catch (err) {
return res.status(401).send("Invalid Token");
}
return next();
};
module.exports = verifyToken;
Regular → Executable
View File
@@ -1,33 +0,0 @@
'use strict';
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('orderItems', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
orderId: {
type: Sequelize.INTEGER
},
itemId: {
type: Sequelize.INTEGER
},
quantity: {
type: Sequelize.INTEGER
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('orderItems');
}
};
+44
View File
@@ -0,0 +1,44 @@
'use strict';
module.exports = (sequelize, DataTypes) => {
const ResetPassword = sequelize.define('ResetPassword', {
username: {
type: DataTypes.STRING,
allowNull: false,
},
member_id: {
type: DataTypes.STRING,
allowNull: false,
},
reset_pin: {
type: DataTypes.STRING,
allowNull: false,
},
reset_key: {
type: DataTypes.STRING,
allowNull: false,
},
expired: {
type: DataTypes.DATE,
allowNull: false
},
status: {
type: DataTypes.INTEGER,
allowNull: false
}
}, {
tableName: 'resetpassword',
// don't forget to enable timestamps!
timestamps: true,
// I don't want createdAt
createdAt: "created",
// I want updatedAt to actually be called updateTimestamp
updatedAt: false
});
ResetPassword.associate = function (models) {
// associations
};
return ResetPassword;
};
Regular → Executable
+12 -3
View File
@@ -7,21 +7,30 @@ const basename = path.basename(__filename);
const env = process.env.NODE_ENV || 'development';
const config = require(__dirname + '/../config/config.json')[env];
const db = {};
const DataTypes = Sequelize.DataTypes
let sequelize;
if (config.use_env_variable) {
sequelize = new Sequelize(process.env[config.use_env_variable], config);
} else {
sequelize = new Sequelize(config.database, config.username, config.password, config);
}
sequelize
.authenticate()
.then(() => {
console.info('INFO - Database connected.')
})
.catch(err => {
console.error('ERROR - Unable to connect to the database:', err)
})
//sequelize.sync({ force: false })
fs
.readdirSync(__dirname)
.filter(file => {
return (file.indexOf('.') !== 0) && (file !== basename) && (file.slice(-3) === '.js');
})
.forEach(file => {
const model = sequelize['import'](path.join(__dirname, file));
//const model = sequelize['import'](path.join(__dirname, file));
const model = require(path.join(__dirname, file)) (sequelize,DataTypes);
db[model.name] = model;
});
Regular → Executable
+49 -22
View File
@@ -1,11 +1,24 @@
'use strict';
module.exports = (sequelize, DataTypes) => {
const member = sequelize.define('member', {
const Member = sequelize.define('Member', {
username: {
type: DataTypes.STRING,
allowNull: false,
unique:{
args: true, msg: "username already exists"
},
validate: {
len: {
args: [3, 50],
msg: 'Your username may be 5 to 50 characters only.'
}
}
},
email: {
type: DataTypes.STRING,
allowNull: false,
unique: {
msg: 'This email is already taken.'
unique:{
args: true, msg: "email already exists"
},
validate: {
isEmail: {
@@ -13,25 +26,12 @@ module.exports = (sequelize, DataTypes) => {
}
}
},
username: {
type: DataTypes.STRING,
allowNull: false,
unique: {
msg: 'This username is already taken.'
},
validate: {
len: {
args: [5, 50],
msg: 'Your username may be 5 to 50 characters only.'
}
}
},
password: {
type: DataTypes.STRING,
allowNull: false,
validate: {
len: {
args: [5, 72],
args: [6, 72],
msg: 'Your password may be 5 to 72 characters only.'
}
}
@@ -41,7 +41,7 @@ module.exports = (sequelize, DataTypes) => {
allowNull: false,
validate: {
len: {
args: [5, 50],
args: [2, 50],
msg: 'Your first name may be 5 to 50 characters only.'
}
}
@@ -51,14 +51,41 @@ module.exports = (sequelize, DataTypes) => {
allowNull: false,
validate: {
len: {
args: [5, 50],
args: [2, 50],
msg: 'Your last name may be 5 to 50 characters only.'
}
}
},
}, {});
member.associate = function (models) {
phone: {
type: DataTypes.STRING,
allowNull: false,
validate: {
len: {
args: [2, 50],
msg: 'Your last name may be 5 to 50 characters only.'
}
}
}
}, {
tableName: 'members',
// don't forget to enable timestamps!
timestamps: true,
// I don't want createdAt
createdAt: "added",
// I want updatedAt to actually be called updateTimestamp
updatedAt: 'updated',
indexes: [
{
unique: true,
fields: ["username","email"] // Whatever other field you need to make unique
}
]
});
Member.associate = function (models) {
// associations
};
return member;
return Member;
};
Regular → Executable
+8 -12
View File
@@ -1,17 +1,13 @@
const memberController = require("../controllers").member;
const auth = require("../middleware/auth");
module.exports = app => {
app.get("/api", (req, res) =>
res.status(200).send({
message: "Welcome to the Pizza API!"
})
);
app.post("/api/v1/user/register", memberController.register);
app.put("/api/v1/user/login", memberController.login);
app.get("/api/v1/user/forgot-password", memberController.forgotPassword);
app.get("/api/v1/user/reset-password", memberController.resetPassword);
app.delete("/api/v1/user/profile", memberController.profile);
app.post("/api/v1/user/register", memberController.validate('register'), memberController.register);
app.post("/api/v1/user/login", memberController.login);
app.post("/api/v1/user/forgot-password", memberController.forgotPassword);
app.post("/api/v1/user/reset-password", memberController.resetPassword);
app.post("/api/v1/user/confirm-reset-password", memberController.confirmResetPassword);
app.post("/api/v1/user/complete-reset-password", memberController.completeResetPassword);
app.get("/api/v1/user/profile", auth, memberController.profile);
};
Regular → Executable
View File
Regular → Executable
+1 -1
View File
@@ -1,4 +1,4 @@
const member = require("./user/members");
const member = require("./user/member");
module.exports = {
member
+233
View File
@@ -0,0 +1,233 @@
const jwt = require('jsonwebtoken');
const bcrypt = require('bcrypt');
const crypto = require('crypto');
const Member = require("../../models").Member;
const resetPasswordService = require("./resetPassword");
module.exports = {
async create(req) {
const { username, password, firstname, lastname, phone, email } = req.body;
const salt = bcrypt.genSaltSync(10);
const hashpassword = bcrypt.hashSync(password, salt);
const user = {
username: username,
password: hashpassword,
firstname: firstname,
lastname: lastname,
phone: phone,
email: email
}
return await Member.create(user);
},
async login(req) {
try {
const user = await Member.findOne({
where: {
username: req.body.username,
status: 1
}
});
if (user) {
const cmp = await bcrypt.compare(req.body.password, user.password);
if (cmp) {
user.last_login = Date.now()
user.save()
const token = jwt.sign({
username: user.username
},
process.env.TOKEN_SECRET, { expiresIn: process.env.TOKEN_EXPIRES }
);
return {
accessToken: token
};
} else {
return "Wrong username or password.";
}
} else {
return "Wrong username or password.";
}
} catch (error) {
console.debug(error)
return "Internal Server error Occured";
}
},
async forgotPassword(req) {
try {
const user = await Member.findOne({
where: { username: req.body.username }
});
if (user) {
const cmp = await bcrypt.compare(req.body.password, user.password);
if (cmp) {
user.last_login = Date.now()
user.save()
const token = jwt.sign({
username: user.username
},
process.env.TOKEN_SECRET, { expiresIn: process.env.TOKEN_EXPIRES }
);
return {
accessToken: token
};
} else {
return "Wrong username or password.";
}
} else {
return "Wrong username or password.";
}
} catch (error) {
console.debug(error)
return "Internal Server error Occured";
}
},
async resetPassword(req) {
try {
const user = await Member.findOne({
where: {
email: req.body.email,
status: 1
}
});
var date = new Date();
let reset_key = crypto.createHash('md5').update(date.toString()).digest("hex");
let reset_pin = Math.floor(100000 + Math.random() * 900000)
let expired = date.setDate(date.getDate() + 7);
var status = 3;
if (user) {
resetPasswordService.updateStatusExpired(user.id, 7);
status = 0
}
const resetPasswordData = {
username: user.username,
member_id: user.id,
reset_pin: reset_pin,
reset_key: reset_key,
expired: expired,
status: status
}
return await resetPasswordService.create(resetPasswordData);
} catch (error) {
console.debug(error)
return "Invalid email";
}
},
async confirmResetPassword(req) {
try {
const user = await Member.findOne({
where: {
email: req.body.email,
status: 1
}
});
if (user) {
const exist = await resetPasswordService.getByCondition({
member_id: user.id,
status: 0,
reset_pin: req.body.resetPin.toString()
});
if (exist) {
resetPasswordService.updateStatusById(exist.id, 1);
return {
resetKey: exist.reset_key
}
}
return "invalid PIN";
} else {
return "email is not existed";
}
} catch (error) {
console.debug(error)
return "Invalid email";
}
},
async completeResetPassword(req) {
try {
const user = await Member.findOne({
where: {
email: req.body.email,
status: 1
}
});
if (user) {
const exist = await resetPasswordService.getByCondition({
member_id: user.id,
status: 1,
reset_key: req.body.resetKey.toString()
});
if (exist) {
const salt = bcrypt.genSaltSync(10);
const hashpassword = bcrypt.hashSync(req.body.newPassword, salt);
Member
.update(
{
password: hashpassword
},
{
returning: true,
where: {
id: user.id
}
}
)
.then(([rowsUpdate, [updatedRow]]) => {
console.log(rowsUpdate)
})
.catch(error => {
console.log(error)
});
resetPasswordService.updateStatusById(exist.id, 5);
return "Ok"
}
return "invalid PIN";
} else {
return "email is not existed";
}
} catch (error) {
console.debug(error)
return "Invalid email";
}
},
async getProfile(req) {
const username = req.user.username;
return await Member.findOne({
where: { username: username },
attributes: ['username', 'email', 'phone', 'firstname', 'lastname']
});
},
async deactivateAccount(req) {
return Member
.destroy({
where: {
id: req.params.id
}
})
.then(rowDeleted => {
if (rowDeleted !== 1) {
throw ({
"name": "ValidationError",
"errors": [{
message: 'Item not found'
}]
});
}
return res.status(200).json({ message: "Deleted successfully" });
})
.catch(error => res.status(400).send(error));
}
}
-60
View File
@@ -1,60 +0,0 @@
const member = require("../../models").member;
const jwt = require('jsonwebtoken');
var bcrypt = require('bcrypt');
module.exports = {
async create(req) {
console.log(req.body)
const { username, password, firstname, lastname, phone, email } = req.body;
const user = {
username: username,
password: password,
firstname: firstname,
lastname: lastname,
phone: phone,
email: email
}
return await member.create(user);
},
async login(req) {
try {
const user = await member.findOne({
where: { username: req.body.username }
});
if (user) {
const cmp = await bcrypt.compare(req.body.password, user.password);
if (cmp) {
user.last_login = Date.now()
user.save()
console.log(user)
const token = jwt.sign({
username: user.username
},
process.env.TOKEN_SECRET, { expiresIn: process.env.TOKEN_EXPIRES }
);
console.log(token)
return {
accessToken: token
};
} else {
return "Wrong username or password.";
}
} else {
return "Wrong username or password.";
}
} catch (error) {
return "Internal Server error Occured";
}
},
async getProfile(req) {
const username = req.user.username;
return await member.findOne({
where: { username: username },
attributes: ['username', 'email', 'phone', 'firstname', 'lastname']
});
}
}
+67
View File
@@ -0,0 +1,67 @@
const ResetPassword = require("../../models").ResetPassword;
const { Op } = require('sequelize');
module.exports = {
async create(data) {
return await ResetPassword.create(data);
},
updateStatusExpired(member_id, status) {
return ResetPassword
.update(
{
status: status
},
{
returning: true,
where: {
member_id: member_id,
status: {
[Op.notIn]: [3, 5]
}
}
}
)
.then(([rowsUpdate, [updatedRow]]) => {
console.log(rowsUpdate)
})
.catch(error => {
console.log(error)
});
},
async getByStatus(status) {
return await ResetPassword.findOne({
where: { status: status },
attributes: ['member_id']
});
},
updateStatusById(id, status) {
return ResetPassword
.update(
{
status: status
},
{
returning: true,
where: {
id: id
}
}
)
.then(([rowsUpdate, [updatedRow]]) => {
console.log(rowsUpdate)
})
.catch(error => {
console.log(error)
});
},
async getByCondition(condition) {
return await ResetPassword.findOne({
where: condition,
attributes: ['id','member_id','reset_key']
});
},
}
Regular → Executable
View File