/**
* @namespace Controller
* @property {module:AuthController} AuthController
*/
/**
* AuthController handle with login and signup process.
* @module AuthController
*/
'use strict'
/**
* Student.Model Module
* @const
*/
import Student from '../models/Student.Model'
/**
* Teacher.Model Module
* @const
*/
import Teacher from '../models/Teacher.Model'
/**
* User.Model Module
* @const
*/
import User from './../models/User.Model'
/**
* Book.Controller Module
* @const
*/
import BookController from '../controllers/Book.Controller'
/**
* Chapter.Controller Module
* @const
*/
import ChapterController from '../controllers/Chapter.Controller'
/**
* JWT Config Object
* @const
*/
import config from '../config/jwt'
/**
* JWT module
* @const
*/
import jwt from 'jsonwebtoken'
/**
* HashPassword Service Module
* @const
*/
import HashPassword from '../services/HashPassword'
export default class AuthController {
/**
* Signup method.
* Responds to POST /auth/signup.
* If Success returns 201 status code, user-object and token-header => { acess_token: "", token_type: "" }.
* If error return 400 status code and a object => { errors }.
* 500 status code only will be returned if the method generates some unexpected error.
*
* @name Signup
* @param {object} req - Express requisition object.
* @param {object} res - Express response object.
* @return {json} status and return object.
* @method signup
* @todo Refactor this method breaking him in 2. One for student signup and another for teacher signup.
*/
signup (req, res) {
let data = req.body
// 1. Encrypt password to be saved
data.password = HashPassword.encrypt(data.password)
// 2. Save user and get its promise
let model = {}
if (req.params.type === 'student') model = new Student(data).persist()
else if (req.params.type === 'teacher') model = new Teacher(data).persist()
model
.then(user => {
if (user) {
delete user['password']
let token = this._generateToken(user)
res.set('authorization', `${token.type_token} ${token.acess_token}`)
// 3. Send user without password and with token in response
res.status(201).json(user).end()
} else throw new Error('user_not_saved')
})
.catch(err => {
console.error(err)
let errorMsg
if (err.code === 11000) {
if (err.errmsg.match(/email_1/)) errorMsg = 'duplicate_email'
else if (err.errmsg.match(/login_1/)) errorMsg = 'duplicate_login'
} else errorMsg = err.message
res.status(400).json(errorMsg).end()
})
}
/**
* Login method.
* Responds to POST /auth/login.
* If Success returns 200 status code, user-object and token-header => { acess_token: "", token_type: "" }.
* If error return 403 status code and message of invalid credentials => { errors }.
* 500 status code only will be returned if the method generates some unexpected error.
*
* @name Login
* @param {object} req - Express requisition object.
* @param {object} res - Express response object.
* @return {json} status and return object.
* @method login
*/
login (req, res) {
const data = {
login: req.body.login
}
let userWithBooks = null
// 1. Get user from login
new User(data).getByField()
.then(user => {
if (user.length !== 0) {
user = user[0]
if (HashPassword.encrypt(req.body.password) === user.password) {
delete user['password']
let token = this._generateToken(user)
res.set('Authorization', `${token.type_token} ${token.acess_token}`)
userWithBooks = user
return new BookController().getStudentBooks(user.books)
} else throw new Error('invalid_login_password')
// User not found
} else throw new Error('invalid_login_password')
})
// 2. Get student books
.then(books => {
let chapters = []
books.forEach(book => {
book.chapters.forEach(chapter => {
chapters.push(chapter)
})
})
userWithBooks.books = books
if (chapters.length === 0) return chapters
else return new ChapterController().getBooksChapters(chapters)
})
// 3. Get student chapters
.then(chapters => {
if (chapters.length !== 0) {
userWithBooks.books.map(book => {
book.chapters = []
chapters = chapters.filter(chapter => {
if (chapter._book.toString() === book._id.toString()) {
let chapterWithoutBookId = Object.assign({}, chapter)
delete chapterWithoutBookId['_book']
book.chapters.push(chapterWithoutBookId)
}
return chapter._book.toString() !== book._id.toString()
})
return book
})
}
// 4. Return user with books and inside chapters
res.status(200).json(userWithBooks)
})
.catch(err => {
console.error(err)
if (err.message === 'invalid_login_password') res.status(403).json(err.message).end()
else res.status(500).json(err.message).end()
})
}
/**
* Receive a Mongoose Object Scheme of User type and generates a new JWT Token.
*
* @name _generateToken
* @param {object} data - models\schemes\UserModel Object.
* @return {json} json containing => { acess_token: "", token_type: "" }.
* @method _generateToken
* @private
* @todo Write comments
*/
_generateToken (data) {
let tokenInfo = {
'email': data.email,
'login': data.login,
'_id': data._id
}
return {
'acess_token': jwt.sign(tokenInfo, config.secret, {
expiresIn: 10080 // in seconds
}),
'type_token': 'Bearer'
}
}
}