Skip to content

Commit

Permalink
Merge 6a8153c into 3c92451
Browse files Browse the repository at this point in the history
  • Loading branch information
segunolalive committed Nov 14, 2017
2 parents 3c92451 + 6a8153c commit ac720dd
Show file tree
Hide file tree
Showing 7 changed files with 298 additions and 37 deletions.
10 changes: 5 additions & 5 deletions client/components/auth/Login.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class Login extends Component {
};
this.handleLogin = this.handleLogin.bind(this);
this.handleChange = this.handleChange.bind(this);
this.responseGoogle = this.responseGoogle.bind(this);
this.handleGoogleLogin = this.handleGoogleLogin.bind(this);
}

/**
Expand All @@ -39,7 +39,7 @@ class Login extends Component {
* @memberof Login
* @returns {Undefined} redirects to dashboard
*/
responseGoogle(response) {
handleGoogleLogin(response) {
const googleProfile = response.profileObj;
this.props.login(googleProfile);
}
Expand All @@ -66,7 +66,7 @@ class Login extends Component {
handleChange(event) {
event.preventDefault();
const formField = event.target.name;
const user = Object.assign({}, this.state);
const user = { ...this.state };
if (event.target.value.trim()) {
user[formField] = event.target.value.trim();
}
Expand Down Expand Up @@ -136,8 +136,8 @@ class Login extends Component {
<div className="input-field">
<GoogleLogin
clientId={GOOGLE_CLIENT_ID}
onSuccess={this.responseGoogle}
onFailure={this.responseGoogle}
onSuccess={this.handleGoogleLogin}
onFailure={this.handleGoogleLogin}
className="btn red darken-4"
style={{ width: '100%' }}
>
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"job": "babel-node server/cronjob",
"cron": "node dist/server/cronjob",
"serve": "babel-node server/bin/www.js",
"start": "cross-env NODE_ENV=production node dist/server/bin/www.js",
"start": "node dist/server/bin/www.js",
"start:debug": "node --inspect dist/server/bin/www.js",
"start:client": "webpack-dev-server --hot --config ./webpack.dev.config.js",
"start:dev": "nodemon server/bin/www --exec babel-node",
Expand All @@ -17,6 +17,7 @@
"pretest": "sequelize db:migrate:undo:all --env test && sequelize db:migrate --env test && cross-env NODE_ENV=test npm run seed:tables",
"test": "cross-env NODE_ENV=test npm run seed:join && npm run test:travis",
"migrate:dev": "sequelize db:migrate:undo:all --env development && sequelize db:migrate --env development",
"migrate": "sequelize db:migrate:undo:all --env production && sequelize db:migrate --env production",
"coverage": "NODE_ENV=test nyc report --reporter=text-lcov | coveralls",
"seed:tables": "cross-env NODE_ENV=test babel-node server/seeders/user.js && babel-node server/seeders/bookCategory.js && babel-node server/seeders/book.js",
"seed:join": "cross-env NODE_ENV=test babel-node server/seeders/borrowedBook.js",
Expand Down
10 changes: 10 additions & 0 deletions server/config/firebase.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// Initialize Firebase // firebase.initializeApp(config);
// TODO: MOVE THESE TO ENVIRONMENT VARIABLES
export default {
apiKey: 'AIzaSyCUQ8B5UajnVa04E6hFUETBzKWD_4XlnVw',
authDomain: 'hellobooks-180211.firebaseapp.com',
databaseURL: 'https://hellobooks-180211.firebaseio.com',
projectId: 'hellobooks-180211',
storageBucket: 'hellobooks-180211.appspot.com',
messagingSenderId: '701806023399'
};
Original file line number Diff line number Diff line change
@@ -1,30 +1,7 @@
import { Book, BorrowedBook, BookCategory, Notification } from '../models';

/**
* Fetch all books that match a catagory from database
* @private
* @param {object} req - express http request object
* @param {object} res - express http response object
* @return {object} - express http response object
*/
const filterBooksByCategory = (req, res) => {
const categoryId = req.query.category;
Book.findAll({ where: { categoryId } })
.then((books) => {
const message = books.length ? '' :
'No books match the requested category';
return res.status(200).send({
books,
message,
});
})
.catch(error => res.status(500).send({
error
}));
};


export default {
const bookController = {
/**
* Add new book category to library.
* @public
Expand Down Expand Up @@ -130,7 +107,7 @@ export default {
*/
getAllBooks(req, res) {
if (req.query.category) {
return filterBooksByCategory(req, res);
return bookController.filterBooksByCategory(req, res);
}
Book.findAll()
.then((books) => {
Expand All @@ -147,6 +124,29 @@ export default {
.catch(error => res.status(500).send({ error }));
},

/**
* Fetch all books that match a catagory from database
* @private
* @param {object} req - express http request object
* @param {object} res - express http response object
* @return {object} - express http response object
*/
filterBooksByCategory(req, res) {
const categoryId = req.query.category;
Book.findAll({ where: { categoryId } })
.then((books) => {
const message = books.length ? '' :
'No books match the requested category';
return res.status(200).send({
books,
message,
});
})
.catch(error => res.status(500).send({
error
}));
},

/**
* Edit a book's metadata.
* @public
Expand Down Expand Up @@ -326,3 +326,5 @@ export default {
}));
}
};

export default bookController;
249 changes: 249 additions & 0 deletions server/controllers/userController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,249 @@
import bcrypt from 'bcrypt';
import dotenv from 'dotenv';

import { User, Book } from '../models';
import { getJWT } from '../helpers/helpers';
import { transporter, mailOptions } from '../config/mail';

dotenv.config();


const userController = {
/**
* Create new user account.
* It sends a an object containing a success boolean
* and a json web token or error
* @public
* @method
* @param {object} req - express http request object
* @param {object} res - express http response object
* @return {Object} - returns an http response object
*/

createUser(req, res) {
const username = req.body.username;
const email = req.body.email;
return User.find({
where: { $or: [{ username }, { email }] }
}).then((existingUser) => {
if (existingUser && existingUser.username === username) {
return res.status(409).json({
message: 'username is taken',
});
}
if (existingUser && existingUser.email === email) {
return res.status(409).json({
message: 'email is associated with an account',
});
}
User.create(req.body)
.then((user) => {
const {
id,
isAdmin,
membershipType,
} = user;
const jwtOptions = { id, email, username, isAdmin, membershipType };
const token = getJWT(jwtOptions);
const { firstName, lastName } = user;
return res.status(201).json({
token,
id,
firstName,
lastName,
isAdmin,
message: `Welcome ${firstName}. This is your dashboard`,
});
})
.catch(error => res.status(400).send({
error
}));
})
.catch(error => res.status(500).send({
error
}));
},

/**
* Edit user Information
* @public
* @method
* @param {object} req - express http request object
* @param {object} res - express http response object
* @return {Object} - returns an http response object
*/
updateUserInfo(req, res) {
const updateData = req.body;
updateData.passwordResetToken = null;
return User.findById(req.user.id)
.then((user) => {
user.update(updateData, { returning: true, plain: true })
.then(() => {
const {
id,
email,
username,
isAdmin,
membershipType,
} = user;
const jwtOptions = { id, email, username, isAdmin, membershipType };
const token = getJWT(jwtOptions);
const { firstName, lastName } = user;
return res.status(200).json({
token,
id,
firstName,
lastName,
isAdmin,
message: 'Your information was successfully updated',
});
}, (error) => {
res.status(500).send({
error,
});
});
})
.catch(error => res.status(500).send({
error,
}));
},

/**
* Get user data on sign in.
* It sends a an object containing a success boolean
* and a json web token or error
* @public
* @method
* @param {object} req - express http request object
* @param {object} res - express http response object
* @return {Object} - returns an http response object
*/

getUser(req, res) {
const username = req.body.username;
const password = req.body.password;
return User.findOne({ where: { username } }).then((user) => {
if (!user) {
if (req.body.authId) {
return userController.createUser(req, res);
}
return res.status(403).send({
message: 'user does not exist',
});
}
bcrypt.compare(password, user.password).then((result) => {
if (!result) {
return res.status(403).send({
message: 'wrong username and password combination',
});
}
const {
id,
email,
isAdmin,
membershipType,
} = user;
const jwtOptions = { id, email, username, isAdmin, membershipType };
const token = getJWT(jwtOptions);
const { firstName, lastName } = user;
return res.status(200).json({
token,
id,
firstName,
lastName,
isAdmin,
message: `Welcome back ${firstName}`,
});
}).catch(error => res.status(500).send({
error,
}));
}).catch(error => res.status(400).send({
error
}));
},

/**
* Get list of books borrowed by specific user
* It sends a an object containing a success boolean
* and a data key, an array of borrowed books or an error
* Response can be filtered by returned status
* @public
* @method
* @param {object} req - express http request object
* @param {object} res - express http response object
* @return {Object} - returns an http rresponse object
*/
getBorrowedBooks(req, res) {
const id = req.params.id;
User.findOne({
where: { id },
include: [{ model: Book }]
}).then((user) => {
let books;
if (req.query && req.query.returned === 'false') {
books = user.Books.filter(
book => book.BorrowedBook.returned === false
);
} else if (req.query && req.query.returned === 'true') {
books = user.Books.filter(
book => book.BorrowedBook.returned === true
);
} else {
books = user.Books;
}
return res.status(200).send({
books
});
})
.catch(error => res.status(500).send({
message: 'An error occured while fetching borrowing history',
error,
}));
},

passwordResetMail(req, res) {
return User.findOne({
where: { email: req.body.email },
attributes: ['id', 'email'],
plain: true,
})
.then((user) => {
if (!user) {
return res.status(404).send({
message: 'Email does not match any account in our records',
});
}
const BASE_URL = process.env.NODE_ENV === 'development' ?
'http://localhost:8080' :
'https://segunolalive-hellobooks.com';
const token = getJWT({ id: user.id }, '1h');
user.passwordResetToken = token;
user.save();
const to = user.email;
const bcc = null;
const subject = 'no-reply: Password reset link';
const html = `<h3>Use this link to reset your password.</h3>
${BASE_URL}/reset-password?token=${token}}
<p>This link is valid only for an hour</p>`;
transporter.sendMail(mailOptions(to, bcc, subject, html),
(err) => {
if (err) {
return res.status(500).send({
message: 'An error occured while sending you a link. Try again',
});
}
return res.status(200).send({
message: 'An password reset link has been sent to your email',
});
});
})
.catch(() => (
res.status(500).send({
message: 'An error occured while sending you a link. Try again',
})
));
}
};


export default userController;

0 comments on commit ac720dd

Please sign in to comment.