Skip to content

Commit

Permalink
Merge pull request #15 from chingu-voyages/backend
Browse files Browse the repository at this point in the history
Backend
  • Loading branch information
sherlineau authored Jan 10, 2023
2 parents 1619152 + e29b313 commit 68fbb74
Show file tree
Hide file tree
Showing 13 changed files with 5,131 additions and 1 deletion.
8 changes: 7 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
/node_modules
node_modules

.env
/.env

# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
Expand All @@ -20,4 +26,4 @@

npm-debug.log*
yarn-debug.log*
yarn-error.log*
yarn-error.log*
2 changes: 2 additions & 0 deletions server/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
PORT=8000
SECRET_KEY={your-secret-key}
11 changes: 11 additions & 0 deletions server/config/jwt.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
const jwt = require("jsonwebtoken");

module.exports.auth = (req, res, next) => {
jwt.verify(req.cookies.usertoken, process.env.SECRET_KEY, (err, payload) => {
if (err) {
res.status(401).json({ verified: false });
} else {
next();
}
});
};
12 changes: 12 additions & 0 deletions server/config/mongoose.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
const mongoose = require("mongoose");
DATABASE = require("../server");

mongoose
.connect(`mongodb://127.0.0.1/${DATABASE}`, {
useNewUrlParser: true,
useUnifiedTopology: true,
})
.then(() => console.log("Established a connection to the database"))
.catch((err) =>
console.log("Something went wrong when connecting to the database ", err)
);
53 changes: 53 additions & 0 deletions server/controllers/applications.controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
const { ApplicationModel } = require("../models/applications.model");
const User = require("../models/user.model");

class ApplicationController {
// Create new Application
createApplication = async (req, res) => {
try {
const newApplication = new ApplicationModel(req.body);
await newApplication.save();
await User.findOneAndUpdate(
{ _id: newApplication.userId },
{ $push: { applications: newApplication } }
);
res.json(newApplication);
} catch (err) {
res.status(400).json(err);
}
};

// Read applications
getAllApplications = (req, res) => {
// sorts returned query in descending order
ApplicationModel.find({})
.sort({ createdAt: -1 })
.then((applications) => res.json(applications))
.catch((err) => res.status(400).json(err));
};

getOneApplication = (req, res) => {
ApplicationModel.findOne(req.params)
.then((application) => res.json(application))
.catch((err) => res.status(400).json(err));
};

// Update Applications
updateApplication = (req, res) => {
ApplicationModel.findOneAndUpdate(req.params, req.body, {
new: true,
runValidators: true,
})
.then((update) => res.json(update))
.catch((err) => res.status(400).json(err));
};

// Delete Applications
deleteApplication = (req, res) => {
ApplicationModel.deleteOne(req.params)
.then((deleteConfirm) => res.json(deleteConfirm))
.catch((err) => res.json(err));
};
}

module.exports = new ApplicationController();
125 changes: 125 additions & 0 deletions server/controllers/user.controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
const User = require("../models/user.model");
const jwt = require("jsonwebtoken");
const bcrypt = require("bcrypt");

class UserController {
// functions this class has available to use
// Create
register = (req, res) => {
User.find({ email: req.body.email })
.then((userEmail) => {
if (userEmail.length === 0) {
User.create(req.body)
.then((user) => {
const userToken = jwt.sign(
{ id: user._id },
process.env.SECRET_KEY
);
res
.cookie("usertoken", userToken, process.env.SECRET_KEY, {
httpOnly: true,
})
.json(user);
})
.catch((err) => res.status(400).json(err));
} else {
res.status(400).json({
errors: {
email: {
message: "Email is already taken, please provide another",
},
},
});
}
})
.catch((err) => res.status(400).json(err));
};

// READ
getAllUsers = (req, res) => {
User.find().populate('applications')
.then((allUsers) => res.json(allUsers))
.catch((err) => res.status(400).json(err));
};

getOneUser = (req, res) => {
User.findOne(req.params)
.then((user) => res.json(user))
.catch((err) => res.status(400).json(err));
};

// UPDATE
updateUser = (req, res) => {
User.findOneAndUpdate(req.params, req.body, {
new: true,
runValidators: true,
})
.then((update) => res.json(update))
.catch((err) => res.status(400).json(err));
};

// DELETE
deleteUser = (req, res) => {
User.deleteOne(req.params)
.then((deleteConfirm) => res.json(deleteConfirm))
.catch((err) => res.json(err));
};

// AUTH functions
// Login
login = async (req, res) => {
const user = await User.findOne({ email: req.body.email });

// if user is not found in database, return error status 400
if (user === null) {
return res.sendStatus(400);
}

// user exists in database, check submitted password is the same as password in database
const correctPassword = await bcrypt.compare(
req.body.password,
user.password
);

if (!correctPassword) {
// password does not match
return res.sendStatus(400);
}

// password is correct, create jwt token
const userToken = jwt.sign(
{
id: user._id,
},
process.env.SECRET_KEY
);

res
.cookie("usertoken", userToken, process.env.SECRET_KEY, {
httpOnly: true,
})
.json({ msg: "success!" });
};

logout = (req, res) => {
res.clearCookie("usertoken");
res.sendStatus(200);
};

/* use this method to retrieve logged in USER data from the backend via jwt cookies
example:
axios
.get(`http://localhost:8000/api/users/getloggedinuser`, {withCredentials: true})
.then(res => console.log(res.data[0]))
*/
getLoggedInUser = (req, res) => {
// use info stored in cookie to get id of logged in user and query db to find user with that id, return that users info
const decodedJwt = jwt.decode(req.cookies.usertoken, { complete: true });
User.find({ _id: decodedJwt.payload.id })
.then((foundUser) => res.json(foundUser))
.catch((err) => res.json(err));
};
}

// export a new instance of the UserController class
module.exports = new UserController();
24 changes: 24 additions & 0 deletions server/models/applications.model.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
const mongoose = require("mongoose");

const ApplicationSchema = new mongoose.Schema(
{
userId: String,
title: {
type: String,
required: [true, "Title is required"],
minLength: [5, "Title must be at least 5 characters"],
},
company: {
type: String,
required: [true, "Company is required"],
minLength: [5, "Company must be at least 5 characters"],
},
status: String,
},
{ timestamps: true }
);

module.exports = {
ApplicationModel: mongoose.model("Application", ApplicationSchema),
ApplicationSchema,
};
58 changes: 58 additions & 0 deletions server/models/user.model.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
const mongoose = require("mongoose");
const bcrypt = require("bcrypt");
const { ApplicationSchema } = require("./applications.model");

const UserSchema = new mongoose.Schema(
{
firstName: {
type: String,
required: [true, "is required"],
},
lastName: {
type: String,
required: [true, "is required"],
},
email: {
type: String,
required: [true, "Email is required"],
validate: {
validator: (val) => /^([\w-\.]+@([\w-]+\.)+[\w-]+)?$/.test(val),
message: "Please enter a valid email",
},
},
password: {
type: String,
required: [true, "is required"],
},
applications: [ApplicationSchema],
},
// created at, updated at
{ timestamps: true }
);

// creates a virtual field used JUST for validations, this is not saved in the database ['confirmPassword'] needs to match field in postman/frontend form
UserSchema.virtual("confirmPassword")
.get(() => this._confirmPassword)
.set((value) => (this._confirmPassword = value));

// before(pre) running other model validations [required: true]
// validate if confirm password matches password
UserSchema.pre("validate", function (next) {
if (this.password !== this.get("confirmPassword")) {
// creates a validation error message (similar to above required: [true, "error message"])
this.invalidate("confirmPassword", "Passwords must match");
}
next();
});

// before(pre) saving to database, encrypt the users password
UserSchema.pre("save", function (next) {
bcrypt.hash(this.password, 10).then((hash) => {
this.password = hash;
next();
});
});



module.exports = mongoose.model("User", UserSchema);
Loading

0 comments on commit 68fbb74

Please sign in to comment.