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
38 changes: 33 additions & 5 deletions 06-jobs-api/starter/app.js
Original file line number Diff line number Diff line change
@@ -1,27 +1,55 @@
require('dotenv').config();
require('express-async-errors');
const helmet = require('helmet');
const cors = require('cors');
const xss = require('xss-clean');
const rateLimiter = require('express-rate-limit');

const express = require('express');
const app = express();
const connectDB = require('./db/connect');
const authenticateUser = require('./middleware/authentication');

// routers
const authRouter = require('./routes/auth');
const jobsRouter = require('./routes/jobs');

// error handler
// error handlings
const notFoundMiddleware = require('./middleware/not-found');
const errorHandlerMiddleware = require('./middleware/error-handler');


app.set("trust proxy", 1);
app.use(
rateLimiter({
// 15 minutes
windowMs: 15 * 60 * 1000,
// limit each IP to 100 requests per windowMs
max: 100,
})
);
app.use(express.json());
// extra packages
app.use(helmet());
app.use(cors());
app.use(xss());

// routes
app.get('/', (req, res) => {
res.send('jobs api');
app.get("/", (req, res) => {
res.send('<h1>Jobs API</h1><a href="/api-docs">Documentation</a>');
});

// routes
app.use('/api/v1/auth', authRouter);
app.use('/api/v1/jobs', authenticateUser, jobsRouter);

app.use(notFoundMiddleware);
app.use(errorHandlerMiddleware);

// port
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}...`)
);
Expand Down
38 changes: 38 additions & 0 deletions 06-jobs-api/starter/controllers/auth.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
const { BadRequestError, UnauthenticatedError } = require('../errors')
const User = require('../models/User')
const { StatusCodes } = require('http-status-codes')


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')
}
// Comparing passwords
const token = user.createJWT()
res.status(StatusCodes.OK).json({ user: { name: user.name }, token })
}

module.exports = {
register,
login,
}
32 changes: 32 additions & 0 deletions 06-jobs-api/starter/controllers/jobs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
const Job = require('../models/Job')
const { StatusCodes } = require('http-status-codes')
const { BadRequestError, NotFoundError } = require('../errors')

const getAllJobs = async (req, res) => {
res.send('Get All Jobs')
}
const getJob = async (req, res) => {
res.send('Get Single 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) => {
res.send('Update Job')
}

const deleteJob = async (req, res) => {
res.send('Delete Job')
}

module.exports = {
createJob,
deleteJob,
getAllJobs,
updateJob,
getJob,
}
23 changes: 23 additions & 0 deletions 06-jobs-api/starter/middleware/authentication.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
const User = require('../models/User')
const jwt = require('jsonwebtoken')
const { UnauthenticatedError } = require('../errors')

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)
// attach 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
27 changes: 27 additions & 0 deletions 06-jobs-api/starter/models/Job.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
const mongoose = require('mongoose')
const JobSchema = new mongoose.Schema(
{
company: {
type: String,
required: [true, 'Please provide company name'],
maxlength: 50,
},
position: {
type: String,
required: [true, 'Please provide position'],
maxlength: 100,
},
status: {
type: String,
enum: ['interview', 'declined', 'pending'],
default: 'pending',
},
createdBy: {
type: mongoose.Types.ObjectId,
ref: 'User',
required: [true, 'Please provide user'],
},
},
{ timestamps: true }
)
module.exports = mongoose.model('Job', JobSchema)
46 changes: 46 additions & 0 deletions 06-jobs-api/starter/models/User.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
const mongoose = require('mongoose')
const bcrypt = require('bcryptjs')
const jwt = require('jsonwebtoken')

const UserSchema = new mongoose.Schema({
name: {
type: String,
required: [true, 'Please provide name'],
maxlength: 50,
minlength: 3,
},
email: {
type: String,
required: [true, 'Please provide email'],
match: [
/^(([^<>()[\]\\.,;:\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,}))$/,
'Please provide a valid email',
],
unique: true,
},
password: {
type: String,
required: [true, 'Please provide password'],
minlength: 6,
},
})

UserSchema.pre('save', async function () {
const salt = await bcrypt.genSalt(10)
this.password = await bcrypt.hash(this.password, salt)
})

UserSchema.methods.createJWT = function () {
return jwt.sign(
{ userId: this._id, name: this.name },
process.env.JWT_SECRET,
{
expiresIn: process.env.JWT_LIFETIME,
}
)
}
UserSchema.methods.comparePassword = async function (canditatePassword) {
const isMatch = await bcrypt.compare(canditatePassword, this.password)
return isMatch
}
module.exports = mongoose.model('User', UserSchema)
8 changes: 8 additions & 0 deletions 06-jobs-api/starter/routes/auth.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
const express = require('express')
const router = express.Router()
const { register, login } = require('../controllers/auth')
router.post('/register', register)
router.post('/login', login)


module.exports = router
14 changes: 14 additions & 0 deletions 06-jobs-api/starter/routes/jobs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
const express = require('express')
const router = express.Router()
const {
createJob,
deleteJob,
getAllJobs,
updateJob,
getJob,
} = require('../controllers/jobs')

router.route('/').post(createJob).get(getAllJobs)
router.route('/:id').get(getJob).delete(deleteJob).patch(updateJob)

module.exports = router