-
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.
- Loading branch information
1 parent
4963316
commit 826e1d5
Showing
21 changed files
with
2,556 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 @@ | ||
web: node app.js |
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,69 @@ | ||
#### Setup | ||
|
||
```bash | ||
npm install && npm start | ||
``` | ||
|
||
#### Database Connection | ||
|
||
1. Import connect.js | ||
2. Invoke in start() | ||
3. Setup .env in the root | ||
4. Add MONGO_URI with correct value | ||
|
||
#### Routers | ||
|
||
- auth.js | ||
- jobs.js | ||
|
||
#### User Model | ||
|
||
Email Validation Regex | ||
|
||
```regex | ||
/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/ | ||
``` | ||
|
||
#### Register User | ||
|
||
- Validate - name, email, password - with Mongoose | ||
- Hash Password (with bcryptjs) | ||
- Save User | ||
- Generate Token | ||
- Send Response with Token | ||
|
||
#### Login User | ||
|
||
- Validate - email, password - in controller | ||
- If email or password is missing, throw BadRequestError | ||
- Find User | ||
- Compare Passwords | ||
- If no user or password does not match, throw UnauthenticatedError | ||
- If correct, generate Token | ||
- Send Response with Token | ||
|
||
#### Mongoose Errors | ||
|
||
- Validation Errors | ||
- Duplicate (Email) | ||
- Cast Error | ||
|
||
#### Security | ||
|
||
- helmet | ||
- cors | ||
- xss-clean | ||
- express-rate-limit | ||
|
||
Swagger UI | ||
|
||
```yaml | ||
/jobs/{id}: | ||
parameters: | ||
- in: path | ||
name: id | ||
schema: | ||
type: string | ||
required: true | ||
description: the job id | ||
``` |
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,74 @@ | ||
require('dotenv').config(); | ||
require('express-async-errors'); | ||
|
||
// extra security packages | ||
|
||
const helmet = require('helmet') | ||
const cors = require('cors') | ||
const xss = require('xss-clean') | ||
const rateLimiter = require('express-rate-limit') | ||
|
||
// Swagger | ||
|
||
const swaggerUI = require('swagger-ui-express') | ||
const YAML = require('yamljs') | ||
const swaggerDocument = YAML.load('./swagger.yaml') | ||
|
||
|
||
const express = require('express'); | ||
const app = express(); | ||
|
||
// connect db | ||
|
||
const connectDB = require('./db/connect') | ||
const authenticateUser = require('./middleware/authentication') | ||
|
||
// routers | ||
|
||
const authRouter = require('./routes/auth') | ||
const jobsRouter = require('./routes/jobs') | ||
|
||
// error handler | ||
const notFoundMiddleware = require('./middleware/not-found'); | ||
const errorHandlerMiddleware = require('./middleware/error-handler'); | ||
|
||
app.set('trust proxy',1) | ||
|
||
app.use(rateLimiter({ | ||
windowMs: 15*60*1000, | ||
max: 100, | ||
})) | ||
|
||
app.use(express.json()); | ||
app.use(helmet()) | ||
app.use(cors()) | ||
app.use(xss()) | ||
|
||
app.get('/',(req,res)=>{ | ||
res.send('<h1>jobs API</h1> <a href="/api-docs">Documentation</a>') | ||
}) | ||
|
||
app.use('/api-docs',swaggerUI.serve, swaggerUI.setup(swaggerDocument)) | ||
|
||
// routes | ||
app.use('/api/v1/auth',authRouter) | ||
app.use('/api/v1/jobs', authenticateUser ,jobsRouter) | ||
|
||
|
||
app.use(notFoundMiddleware); | ||
app.use(errorHandlerMiddleware); | ||
|
||
const port = process.env.PORT || 3000; | ||
|
||
const start = async () => { | ||
try { | ||
await connectDB(process.env.MONGO_URI) | ||
app.listen(port, () => | ||
console.log(`Server is listening on port ${port}...`) | ||
); | ||
} catch (error) { | ||
console.log(error); | ||
} | ||
}; | ||
|
||
start(); |
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,41 @@ | ||
const User = require('../models/User') | ||
const {StatusCodes} = require('http-status-codes') | ||
const {BadRequestError, UnauthenticatedError} = require('../errors/index') | ||
|
||
|
||
const register = async (req,res) => { | ||
const user = await User.create({...req.body}) | ||
const token = user.createJWT() | ||
res.status(StatusCodes.CREATED).json({user:{name:user.name} ,token}) | ||
} | ||
|
||
const login = async (req,res) => { | ||
const {email,password} = req.body | ||
|
||
if(!email || !password){ | ||
throw new BadRequestError('Please provide email and password') | ||
} | ||
|
||
const user = await User.findOne({email}) | ||
|
||
if(!user){ | ||
throw new UnauthenticatedError('Invalid Credentials') | ||
} | ||
|
||
const isPasswordCorrect = await user.comparePassword(password) | ||
if(!isPasswordCorrect){ | ||
throw new UnauthenticatedError('Invalid Credentials') | ||
} | ||
|
||
// compare password | ||
const token = user.createJWT() | ||
|
||
res.status(StatusCodes.OK).json({user:{name:user.name},token}) | ||
|
||
} | ||
|
||
|
||
module.exports = { | ||
register, | ||
login, | ||
} |
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,80 @@ | ||
const Job = require('../models/Job') | ||
const {StatusCodes} = require('http-status-codes') | ||
const { | ||
NotFoundError, | ||
BadRequestError | ||
} = require('../errors/index') | ||
|
||
const getAllJobs = async (req,res) => { | ||
const jobs = await Job.find({createdBy:req.user.userId}).sort('createdAt') | ||
res.status(StatusCodes.OK).json({jobs,count: jobs.length}) | ||
} | ||
|
||
const getJob = async (req,res) => { | ||
const {user:{userId},params:{id:jobId}} = req | ||
|
||
const job = await Job.findOne({ | ||
_id:jobId, | ||
createdBy:userId | ||
}) | ||
if(!job){ | ||
throw new NotFoundError(`No job with id ${jobId}`) | ||
} | ||
res.status(StatusCodes.OK).json({job}) | ||
} | ||
|
||
const createJob = async (req,res) => { | ||
req.body.createdBy = req.user.userId | ||
const job = await Job.create(req.body) | ||
res.status(StatusCodes.CREATED).json({job}) | ||
} | ||
|
||
const updateJob = async (req,res) => { | ||
const { | ||
body:{company,position}, | ||
user:{userId}, | ||
params:{id:jobId} | ||
} = req | ||
|
||
if(company==='' || position===''){ | ||
throw new BadRequestError('Company or Position fields cannot be empty') | ||
} | ||
|
||
const job = await Job.findByIdAndUpdate({ | ||
_id:jobId, | ||
createdBy:userId | ||
}, | ||
req.body, | ||
{new:true,runValidators:true} | ||
) | ||
|
||
if(!job){ | ||
throw new NotFoundError(`No job with id ${jobId}`) | ||
} | ||
res.status(StatusCodes.OK).json({job}) | ||
|
||
} | ||
|
||
const deleteJob = async (req,res) => { | ||
const {user:{userId},params:{id:jobId}} = req | ||
|
||
const job = await Job.findOneAndRemove({ | ||
_id:jobId, | ||
createdBy:userId | ||
}) | ||
|
||
if(!job){ | ||
throw new NotFoundError(`No job with id ${jobId}`) | ||
} | ||
res.status(StatusCodes.OK).send() | ||
|
||
} | ||
|
||
|
||
module.exports = { | ||
getAllJobs, | ||
getJob, | ||
createJob, | ||
updateJob, | ||
deleteJob, | ||
} |
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,12 @@ | ||
const mongoose = require('mongoose') | ||
|
||
const connectDB = (url) => { | ||
return mongoose.connect(url, { | ||
useNewUrlParser: true, | ||
useCreateIndex: true, | ||
useFindAndModify: false, | ||
useUnifiedTopology: true, | ||
}) | ||
} | ||
|
||
module.exports = connectDB |
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,11 @@ | ||
const { StatusCodes } = require('http-status-codes'); | ||
const CustomAPIError = require('./custom-api'); | ||
|
||
class BadRequestError extends CustomAPIError { | ||
constructor(message) { | ||
super(message); | ||
this.statusCode = StatusCodes.BAD_REQUEST; | ||
} | ||
} | ||
|
||
module.exports = BadRequestError; |
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,7 @@ | ||
class CustomAPIError extends Error { | ||
constructor(message) { | ||
super(message) | ||
} | ||
} | ||
|
||
module.exports = CustomAPIError |
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,11 @@ | ||
const CustomAPIError = require('./custom-api') | ||
const UnauthenticatedError = require('./unauthenticated') | ||
const NotFoundError = require('./not-found') | ||
const BadRequestError = require('./bad-request') | ||
|
||
module.exports = { | ||
CustomAPIError, | ||
UnauthenticatedError, | ||
NotFoundError, | ||
BadRequestError, | ||
} |
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,11 @@ | ||
const { StatusCodes } = require('http-status-codes'); | ||
const CustomAPIError = require('./custom-api'); | ||
|
||
class NotFoundError extends CustomAPIError { | ||
constructor(message) { | ||
super(message); | ||
this.statusCode = StatusCodes.NOT_FOUND; | ||
} | ||
} | ||
|
||
module.exports = NotFoundError; |
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,11 @@ | ||
const { StatusCodes } = require('http-status-codes'); | ||
const CustomAPIError = require('./custom-api'); | ||
|
||
class UnauthenticatedError extends CustomAPIError { | ||
constructor(message) { | ||
super(message); | ||
this.statusCode = StatusCodes.UNAUTHORIZED; | ||
} | ||
} | ||
|
||
module.exports = UnauthenticatedError; |
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,26 @@ | ||
const User = require('../models/User') | ||
const jwt = require('jsonwebtoken') | ||
const {UnauthenticatedError} = require('../errors/index') | ||
|
||
const auth = async (req,res,next) =>{ | ||
|
||
// check header | ||
|
||
const authHeader = req.headers.authorization | ||
if(!authHeader || !authHeader.startsWith('Bearer ')){ | ||
throw new UnauthenticatedError('Authentication Invalid') | ||
} | ||
const token = authHeader.split(' ')[1] | ||
|
||
try { | ||
const payload = jwt.verify(token, process.env.JWT_SECRET) | ||
// attch the user to the job routes | ||
req.user = {userId:payload.userId, name:payload.name} | ||
next() | ||
} catch (error){ | ||
throw new UnauthenticatedError('Authentication Invalid') | ||
} | ||
} | ||
|
||
|
||
module.exports = auth |
Oops, something went wrong.