diff --git a/06-jobs-api/starter/app.js b/06-jobs-api/starter/app.js
index 4d6385152d..57823292b3 100644
--- a/06-jobs-api/starter/app.js
+++ b/06-jobs-api/starter/app.js
@@ -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('
Jobs API
Documentation');
});
+// 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}...`)
);
diff --git a/06-jobs-api/starter/controllers/auth.js b/06-jobs-api/starter/controllers/auth.js
index e69de29bb2..aa91fb4c52 100644
--- a/06-jobs-api/starter/controllers/auth.js
+++ b/06-jobs-api/starter/controllers/auth.js
@@ -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,
+}
diff --git a/06-jobs-api/starter/controllers/jobs.js b/06-jobs-api/starter/controllers/jobs.js
index e69de29bb2..cb84d37ff9 100644
--- a/06-jobs-api/starter/controllers/jobs.js
+++ b/06-jobs-api/starter/controllers/jobs.js
@@ -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,
+}
diff --git a/06-jobs-api/starter/middleware/authentication.js b/06-jobs-api/starter/middleware/authentication.js
index e69de29bb2..7175e7710c 100644
--- a/06-jobs-api/starter/middleware/authentication.js
+++ b/06-jobs-api/starter/middleware/authentication.js
@@ -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
diff --git a/06-jobs-api/starter/models/Job.js b/06-jobs-api/starter/models/Job.js
index e69de29bb2..e110582ce5 100644
--- a/06-jobs-api/starter/models/Job.js
+++ b/06-jobs-api/starter/models/Job.js
@@ -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)
diff --git a/06-jobs-api/starter/models/User.js b/06-jobs-api/starter/models/User.js
index e69de29bb2..cda283a95d 100644
--- a/06-jobs-api/starter/models/User.js
+++ b/06-jobs-api/starter/models/User.js
@@ -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)
diff --git a/06-jobs-api/starter/routes/auth.js b/06-jobs-api/starter/routes/auth.js
index e69de29bb2..0df3daff74 100644
--- a/06-jobs-api/starter/routes/auth.js
+++ b/06-jobs-api/starter/routes/auth.js
@@ -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
diff --git a/06-jobs-api/starter/routes/jobs.js b/06-jobs-api/starter/routes/jobs.js
index e69de29bb2..7ee1b0a5fb 100644
--- a/06-jobs-api/starter/routes/jobs.js
+++ b/06-jobs-api/starter/routes/jobs.js
@@ -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