Skip to content

Commit

Permalink
Add files via upload
Browse files Browse the repository at this point in the history
  • Loading branch information
aayushsrivastava9760 authored Dec 19, 2021
1 parent 4963316 commit 826e1d5
Show file tree
Hide file tree
Showing 21 changed files with 2,556 additions and 0 deletions.
1 change: 1 addition & 0 deletions Procfile
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
web: node app.js
69 changes: 69 additions & 0 deletions README.MD
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
```
74 changes: 74 additions & 0 deletions app.js
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();
41 changes: 41 additions & 0 deletions controllers/auth.js
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,
}
80 changes: 80 additions & 0 deletions controllers/jobs.js
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,
}
12 changes: 12 additions & 0 deletions db/connect.js
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
11 changes: 11 additions & 0 deletions errors/bad-request.js
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;
7 changes: 7 additions & 0 deletions errors/custom-api.js
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
11 changes: 11 additions & 0 deletions errors/index.js
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,
}
11 changes: 11 additions & 0 deletions errors/not-found.js
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;
11 changes: 11 additions & 0 deletions errors/unauthenticated.js
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;
26 changes: 26 additions & 0 deletions middleware/authentication.js
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
Loading

0 comments on commit 826e1d5

Please sign in to comment.