Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/google signin #50

Merged
merged 2 commits into from
Nov 14, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use object destructuring prefer-destructuring

const email = req.body.email;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use object destructuring prefer-destructuring

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 };

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Expected a line break after this opening brace object-curly-newline
Expected a line break before this closing brace object-curly-newline

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 };

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Expected a line break after this opening brace object-curly-newline
Expected a line break before this closing brace object-curly-newline

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;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use object destructuring prefer-destructuring

const password = req.body.password;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use object destructuring prefer-destructuring

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 };

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Expected a line break after this opening brace object-curly-newline
Expected a line break before this closing brace object-curly-newline

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;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use object destructuring prefer-destructuring

User.findOne({
where: { id },
include: [{ model: Book }]
}).then((user) => {
let books;
if (req.query && req.query.returned === 'false') {
books = user.Books.filter(

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unexpected newline after '(' function-paren-newline

book => book.BorrowedBook.returned === false
);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unexpected newline before ')' function-paren-newline

} else if (req.query && req.query.returned === 'true') {
books = user.Books.filter(

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unexpected newline after '(' function-paren-newline

book => book.BorrowedBook.returned === true
);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unexpected newline before ')' function-paren-newline

} 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),

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Expected a newline after '(' function-paren-newline

(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',
});
});

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Expected a newline before ')' function-paren-newline

})
.catch(() => (
res.status(500).send({
message: 'An error occured while sending you a link. Try again',
})
));
}
};


export default userController;
Loading