Skip to content
Open
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
100 changes: 100 additions & 0 deletions api/helper/helper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
const REQUIRED_KEYS = [ 'username', 'password' ]
const { SECRET } = process.env
const jwt = require('jsonwebtoken')

//Middleware for validating account signup
const signupValidation = (req, res, next) => {
const {username, password, admin} = req.body

//Checks to see all required fields are provided
if (!req.body) next({ status: 401, message: 'Missing request POST body!' })

//Checks to see if username is empty
if(!username) next({ status: 400, message: 'Please enter a username!' })

//Checks to see if password is provided
if(!password){
next({ status: 400, message: 'Please enter a password!' })
}else{
//If password is provided validate for its length, error out if less then minimum of 8 characters
if(password.length < 8) next({ status: 400, message: 'Invalid password length. Please enter at least 8 characters.' })
Copy link
Collaborator

Choose a reason for hiding this comment

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

For all of these, I would add a return statement here.

}

next()
}

//This is the middleware to only allow admin users to change user permissions
const userAdmins = (req, res, next) => {
const {admin} = req.body
//Check to see if req body has the admin key and boolean value
if(!req.body) next({ status: 400, message: 'Request body does not include an admin key!' })
if(admin !== false && admin !== true) next({ status: 400, message: 'The request body does not include an admin key with a boolean value!' })

//Check to see if there is a auth bearer attached to the request
if(!req.headers.authorization) next({ status: 401, message: 'A valid JWT token is not provided' })
const token = req.headers.authorization.split('Bearer ')[1]

//Verify that the token is a valid token
const payload = jwt.verify(token, SECRET)
console.log(payload.admin)
//Checks if the logged in user token is an admin
if(payload.admin === false) next({status:401, message: 'The JWT token is for a user who is not an admin'})

next()

}


//This is the middleware for the book creation route to prevent non-admins from posting
const bookAdmins = (req, res, next) => {
//Check to see if there is a auth bearer attached to the request
if(!req.headers.authorization) next({ status: 401, message: 'A valid JWT token is not provided' })
const token = req.headers.authorization.split('Bearer ')[1]

//Verify that the token is a valid token
const payload = jwt.verify(token, SECRET)
console.log(payload.admin)
//Checks if the logged in user token is an admin
if(payload.admin === false) next({status:401, message: 'You are not an ADMIN, you CANNOT access this route.'})

next()
}



//This is the middleware for the book reserve route
const reserver = (req, res, next) => {
//Check to see if there is a auth bearer attached to the request
if(!req.headers.authorization) next({ status: 401, message: 'A valid JWT token is not provided.' })
const token = req.headers.authorization.split('Bearer ')[1]

//Verify that the token is a valid token
const payload = jwt.verify(token, SECRET)

//Set userId to logged in users id so that I can use it in the route to set the reserved memberId to this.
req.userId = payload.id
if(!payload) next({status: 401, message: 'You are not allowed to RESERVE a book.'})

next()
}


//This is the middleware for the book return route
const returner = (req, res, next) => {
//Check to see if there is a auth bearer attached to the request
if(!req.headers.authorization) next({ status: 401, message: 'A valid JWT token is not provided.' })
const token = req.headers.authorization.split('Bearer ')[1]

//Verify that the token is a valid token
const payload = jwt.verify(token, SECRET)

//Set userId to logged in users id so that I can use it in the route to set the reserved memberId to this.
req.userId = payload.id
if(!payload) next({status: 401, message: 'You are not allowed to RETURN this book.'})

next()
}



module.exports = { signupValidation, userAdmins, bookAdmins, reserver, returner }
Copy link
Collaborator

Choose a reason for hiding this comment

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

These are all excellent. Well done using middleware. I would say some of the names could use some work, but that's one of the hardest things to do. :)

2 changes: 1 addition & 1 deletion api/models/book.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,4 @@ const schema = new mongoose.Schema({
}
}, { timestamps: { createdAt: 'created_at', updatedAt: 'updated_at' } })

module.exports = mongoose.model('Book', schema)
module.exports = mongoose.model('Book', schema)
18 changes: 18 additions & 0 deletions api/models/user.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
const mongoose = require('mongoose')

const userSchema = new mongoose.Schema({
username: {
type:String,
required: true
},
password: {
type: String,
required: true
},
admin: {
type: Boolean,
default: false
}
})

module.exports = mongoose.model('User', userSchema)
112 changes: 85 additions & 27 deletions api/routes/books.js
Original file line number Diff line number Diff line change
@@ -1,36 +1,41 @@
const router = require('express').Router()
const Book = require('../models/book')
const helper = require('../helper/helper')

//Get all books route
router.get('/', async (req, res, next) => {
const status = 200
const response = await Book.find().select('-__v')

res.json({ status, response })
})
})//End of get all books route


//Get certain book route
router.get('/:id', async (req, res, next) => {
const { id } = req.params
const status = 200
try {
const response = await Book.findById(id).select('-__v')
if (!response) throw new Error(`Invalid Book _id: ${id}`)
res.json({ status, response })

res.json({ status, response })
} catch (e) {
console.error(e)
const error = new Error(`Cannot find book with id ${id}.`)
error.status = 404
next(error)
}
})
})//End of get certain book route


// You should only be able to create a book if the user is an admin
router.post('/', async (req, res, next) => {
// You should only be able to create a book if the user is an admin - DONE
router.post('/', helper.bookAdmins, async (req, res, next) => {
const status = 200
try {
const book = await Book.create(req.body)
if (!book) throw new Error(`Request body failed: ${JSON.stringify(req.body)}`)

const response = await Book.findById(book._id).select('-__v')
res.json({ status, response })
} catch (e) {
Expand All @@ -40,39 +45,92 @@ router.post('/', async (req, res, next) => {
error.status = 400
next(error)
}
})
})//End of book creation route


// You should only be able to reserve a book if a user is logged in
router.patch('/:id/reserve', async (req, res, next) => {
// You should only be able to reserve a book if a user is logged in - DONE
router.patch('/:id/reserve', helper.reserver, async (req, res, next) => {
const { id } = req.params
try {

const book = await Book.findById(id)

if (!book) {
const error = new Error(`Invalid Book _id: ${id}`)
const error = new Error(`Book _id: ${id} cannot be found.`)
error.message = 404
return next(error)
}

book.reserved.status = true
// Set the reserved memberId to the current user
await book.save()

//Checks to see if the book is available to reserve.
//If it is already reserved return a status of 400 and message that its already reservered
if(book.reserved.status == false){
book.reserved.status = true
book.reserved.memberId = req.userId

// Set the reserved memberId to the current user
await book.save()
}else{
const status = 400
const message = `The book ${id} is already reserved.`
return res.status(status).json({status, message})
}

const response = await Book.findById(book._id).select('-__v')
const status = 200
res.json({ status, response })
} catch (e) {

}catch (e) {
console.error(e)
const message = `Book ${id} cannot be found.`
const error = new Error(message)
error.status = 404
next(error)
}
})
})//End of reserve patch route


// You should only be able to return a book if the user is logged in

// You should only be able to return a book if the user is logged in - DONE
// and that user is the one who reserved the book
router.patch('/:id/return', async (req, res, next) => {
const status = 200
const message = 'You must implement this route!'

console.log(message)
res.status(status).json({ status, message })
})
router.patch('/:id/return', helper.returner, async (req, res, next) => {
const { id } = req.params
try{
const status = 200
const book = await Book.findById(id)

//Check to see if the book is not yet reserved
if(book.reserved.status == false){
const status = 400
const message = `The book ${id} is NOT yet reserved.`
return res.status(status).json({status, message})
}else if(book.reserved.status == true && book.reserved.memberId == req.userId){ //Check to see if the book is reserved by the logged in user.
//Set status and memberID to false and null
book.reserved.status = false
book.reserved.memberId = null
//Save book object in database
await book.save()

const status = 201
const message = `You have SUCCESSFULLY Returned book ${id}.`

return res.status(status).json({status, message, book})
}else{ //Check to see if the book is reserved by a different user than that who is logged in.
const status = 401
const message = `The book is reserved by a different user`
return res.status(status).json({status, message})
}


}catch(e){
//Error handler
console.error(e)
const message = `Book ${id} cannot be found.`
const error = new Error(message)
error.status = 404
next(error)
}


})//End of return patch route

module.exports = router
module.exports = router
Loading