-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1 from luiz123o/feat/Users-tasks
User and tariff features.
- Loading branch information
Showing
12 changed files
with
466 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
import * as Yup from 'yup'; | ||
import jwt from 'jsonwebtoken'; | ||
|
||
import authConfig from '../../config/auth'; | ||
import User from '../models/User'; | ||
|
||
class SessionController { | ||
async store(req, res) { | ||
/** | ||
* Validação de informações do Usuario. | ||
*/ | ||
const schema = Yup.object().shape({ | ||
email: Yup.string() | ||
.email() | ||
.required(), | ||
password: Yup.string().required(), | ||
}); | ||
|
||
if (!(await schema.isValid(req.body))) { | ||
return res.status(400).json({ error: 'Validation Fails' }); | ||
} | ||
/** | ||
* Sistema de Validação do usuario. | ||
*/ | ||
const { email, password } = req.body; | ||
|
||
const user = await User.findOne({ where: { email } }); | ||
|
||
// Verifico se o usuario existe utilizando o user que carrega as informações do email. | ||
if (!user) { | ||
return res.status(401).json({ error: 'user not found' }); | ||
} | ||
// Verifico se a senha corresponde | ||
if (!(await user.checkPassword(password))) { | ||
return res.status(401).json({ error: 'Password does not match' }); | ||
} | ||
const { id, name } = user; | ||
return res.json({ | ||
user: { | ||
id, | ||
name, | ||
email, | ||
}, | ||
token: jwt.sign({ id }, authConfig.secret, { | ||
expiresIn: authConfig.expiresIn, | ||
}), | ||
}); | ||
} | ||
} | ||
export default new SessionController(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
import * as Yup from 'yup'; | ||
import { | ||
startOfHour, | ||
parseISO, | ||
isBefore, | ||
startOfDay, | ||
endOfDay, | ||
} from 'date-fns'; | ||
|
||
import { Op } from 'sequelize'; | ||
|
||
import Task from '../models/Task'; | ||
|
||
class TaskController { | ||
async index(req, res) { | ||
const { page = 1 } = req.query; | ||
const { date } = req.query; | ||
const parsedDate = parseISO(date); | ||
|
||
/** | ||
* Sistema de Listagem ordenado por data | ||
*/ | ||
const tasks = await Task.findAll({ | ||
where: { | ||
user_id: req.userId, | ||
canceled_at: null, | ||
date: { [Op.between]: [startOfDay(parsedDate), endOfDay(parsedDate)] }, | ||
}, | ||
order: ['date'], | ||
attributes: [ | ||
'id', | ||
'user_id', | ||
'date', | ||
'title', | ||
'description', | ||
'completed', | ||
'created_at', | ||
], | ||
limit: 20, | ||
offset: (page - 1) * 20, | ||
}); | ||
return res.json(tasks); | ||
} | ||
|
||
async store(req, res) { | ||
/** | ||
* Validação de Dados | ||
*/ | ||
const schema = Yup.object().shape({ | ||
title: Yup.string().required(), | ||
description: Yup.string().required(), | ||
date: Yup.date().required(), | ||
completed: Yup.boolean(), | ||
}); | ||
if (!(await schema.isValid(req.body))) { | ||
return res.status(400).json({ error: 'Validation fails' }); | ||
} | ||
|
||
const { title, description, date, completed } = req.body; | ||
/** | ||
* Check se a data é anterior a atual | ||
*/ | ||
const hourStart = startOfHour(parseISO(date)); | ||
|
||
if (isBefore(hourStart, new Date())) { | ||
return res.status(400).json({ error: 'Past dates are not permitted' }); | ||
} | ||
const checkAvailability = await Task.findOne({ | ||
where: { | ||
canceled_at: null, | ||
date: hourStart, | ||
}, | ||
}); | ||
if (checkAvailability) { | ||
return res.status(400).json({ error: 'Task date is not available' }); | ||
} | ||
/** | ||
* Sistema de criação de tasks | ||
*/ | ||
const tasks = await Task.create({ | ||
user_id: req.userId, | ||
user: req.user, | ||
title, | ||
description, | ||
date, | ||
completed, | ||
}); | ||
|
||
return res.json(tasks); | ||
} | ||
|
||
async update(req, res) { | ||
const tasks = await Task.findByPk(req.params.id); | ||
const { title, description, completed, date } = await tasks.update( | ||
req.body | ||
); | ||
return res.json({ title, description, completed, date }); | ||
} | ||
|
||
async delete(req, res) { | ||
const tasks = await Task.findByPk(req.params.id); | ||
|
||
/** | ||
* Bloqueando a exclusão se uruario for diferente. | ||
*/ | ||
if (tasks.user_id !== req.userId) { | ||
return res | ||
.status(401) | ||
.json({ error: "you don't have permission to cancel this tasks" }); | ||
} | ||
tasks.canceled_at = new Date(); | ||
await tasks.save(); | ||
return res.json(tasks); | ||
} | ||
} | ||
export default new TaskController(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
import * as Yup from 'yup'; | ||
import User from '../models/User'; | ||
|
||
class UserController { | ||
async store(req, res) { | ||
/** | ||
* Validação das informações! | ||
*/ | ||
const schema = Yup.object().shape({ | ||
name: Yup.string().required(), | ||
email: Yup.string() | ||
.email() | ||
.required(), | ||
password: Yup.string() | ||
.required() | ||
.min(6), | ||
}); | ||
if (!(await schema.isValid(req.body))) { | ||
return res.status(400).json({ error: 'Validation fails' }); | ||
} | ||
/** | ||
* Sistema de Cadastro do Usuario | ||
*/ | ||
// 1° Atribuo ao userExists a informação sobre o email. | ||
const userExists = await User.findOne({ where: { email: req.body.email } }); | ||
// 2° Realizo a comparação, e retorno o erro caso exista um usuario com mesmo email. | ||
if (userExists) { | ||
return res.status(400).json({ error: 'User already exists.' }); | ||
} | ||
// 3° Crio um novo objeto e atribuo ao user. | ||
const user = await User.create(req.body); | ||
// 4° Retorna as informações ao frontEnd. | ||
return res.json(user); | ||
} | ||
|
||
async update(req, res) { | ||
/** | ||
* Validação das informações! | ||
*/ | ||
|
||
const schema = Yup.object().shape({ | ||
name: Yup.string(), | ||
email: Yup.string().email(), | ||
oldPassword: Yup.string().min(6), | ||
password: Yup.string() | ||
.min(6) | ||
.when('oldPassword', (oldPassword, field) => | ||
oldPassword ? field.required() : field | ||
), | ||
confirmePassword: Yup.string().when('password', (password, field) => | ||
password ? field.required().oneOf([Yup.ref('password')]) : field | ||
), | ||
}); | ||
if (!(await schema.isValid(req.body))) { | ||
return res.status(400).json({ error: 'Validation fails' }); | ||
} | ||
|
||
/** | ||
* Sistema de Atualização de Cadastro | ||
*/ | ||
|
||
// 1° Atribuo os valor a email e oldPassword. | ||
const { email, oldPassword } = req.body; | ||
// 2° Atribuo ao user o id do usuario. | ||
const user = await User.findByPk(req.userId); | ||
// 3° Comparo o email com o email cadastrado do usuario. | ||
if (email !== user.email) { | ||
// 4° Atribuo ao userExists a informação sobre o email. | ||
const userExists = await User.findOne({ where: { email } }); | ||
// 5° Verifico se o novo email já existe, se Sim retorno Erro 400. | ||
if (userExists) { | ||
return res.status(400).json({ error: 'User already exists' }); | ||
} | ||
} | ||
// 6° Verifico se o oldPassword é igual. | ||
if (oldPassword && !(await user.checkPassword(oldPassword))) { | ||
return res.status(400).json({ error: 'Password does match' }); | ||
} | ||
const { id, name } = await user.update(req.body); | ||
return res.json({ | ||
id, | ||
name, | ||
email, | ||
}); | ||
} | ||
} | ||
export default new UserController(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import jwt from 'jsonwebtoken'; | ||
import { promisify } from 'util'; | ||
import authConfig from '../../config/auth'; | ||
|
||
export default async (req, res, next) => { | ||
const authHeader = req.headers.authorization; | ||
|
||
if (!authHeader) { | ||
return res.status(400).json({ error: 'Token not provided' }); | ||
} | ||
|
||
const [, token] = authHeader.split(' '); | ||
|
||
try { | ||
// promisify vai transformar a callback recebida da função verify do jwt | ||
const decoded = await promisify(jwt.verify)(token, authConfig.secret); | ||
req.userId = decoded.id; | ||
|
||
return next(); | ||
} catch (err) { | ||
return res.status(401).json({ error: 'Token invalid' }); | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import Sequelize, { Model } from 'sequelize'; | ||
|
||
class Task extends Model { | ||
static init(sequelize) { | ||
super.init( | ||
{ | ||
date: Sequelize.DATE, | ||
title: Sequelize.STRING, | ||
description: Sequelize.STRING, | ||
completed: Sequelize.BOOLEAN, | ||
created_at: Sequelize.DATE, | ||
canceled_at: Sequelize.DATE, | ||
}, | ||
{ | ||
sequelize, | ||
} | ||
); | ||
return this; | ||
} | ||
|
||
static associate(models) { | ||
this.belongsTo(models.User, { foreignKey: 'user_id' }); | ||
} | ||
} | ||
export default Task; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import Sequelize, { Model } from 'sequelize'; | ||
import bcrypt from 'bcryptjs'; | ||
|
||
class User extends Model { | ||
static init(sequelize) { | ||
super.init( | ||
{ | ||
name: Sequelize.STRING, | ||
email: Sequelize.STRING, | ||
password: Sequelize.VIRTUAL, | ||
password_hash: Sequelize.STRING, | ||
}, | ||
{ | ||
sequelize, | ||
} | ||
); | ||
this.addHook('beforeSave', async users => { | ||
if (users.password) { | ||
users.password_hash = await bcrypt.hash(users.password, 8); | ||
} | ||
}); | ||
return this; | ||
} | ||
|
||
checkPassword(password) { | ||
return bcrypt.compare(password, this.password_hash); | ||
} | ||
} | ||
export default User; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
export default { | ||
secret: process.env.APP_SECRET, | ||
expiresIn: '7d', | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
require('../bootstrap'); | ||
|
||
module.exports = { | ||
dialect: process.env.DB_DIALECT || 'postgres', | ||
host: process.env.DB_HOST, | ||
username: process.env.DB_USER, | ||
password: process.env.DB_PASS, | ||
database: process.env.DB_NAME, | ||
port: process.env.DB_PORT, | ||
storage: './__tests__/database.sqlite', | ||
define: { | ||
timestamps: true, | ||
underscored: true, | ||
underscoredAll: true, | ||
}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
export default { | ||
dsn: process.env.SENTRY_DSN, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import Sequelize from 'sequelize'; | ||
|
||
import User from '../app/models/User'; | ||
import Task from '../app/models/Task'; | ||
|
||
import databaseConfig from '../config/database'; | ||
|
||
const models = [User, Task]; | ||
|
||
class Database { | ||
constructor() { | ||
this.init(); | ||
} | ||
|
||
init() { | ||
this.connection = new Sequelize(databaseConfig); | ||
|
||
models | ||
.map(model => model.init(this.connection)) | ||
.map(model => model.associate && model.associate(this.connection.models)); | ||
} | ||
} | ||
export default new Database(); |
Oops, something went wrong.