Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dev Raj - Completed all tasks #7

Open
wants to merge 33 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
f70452e
added REST client file
dev-raj-1729 Aug 2, 2021
2ce4b30
added bcrypt
dev-raj-1729 Aug 3, 2021
6e0f373
implemented signup
dev-raj-1729 Aug 3, 2021
fec6054
Implemented login
dev-raj-1729 Aug 3, 2021
fa395ff
implemented profile
dev-raj-1729 Aug 3, 2021
275feba
added ToDoController routes
dev-raj-1729 Aug 3, 2021
24de041
export rotuer
dev-raj-1729 Aug 3, 2021
e637887
added authenticate middleware
dev-raj-1729 Aug 3, 2021
e9f4b7d
bug fix
dev-raj-1729 Aug 3, 2021
3281127
implemented createToDo
dev-raj-1729 Aug 3, 2021
1039f6a
implemented getAllToDo
dev-raj-1729 Aug 3, 2021
156bad8
implemented ToDo functions
dev-raj-1729 Aug 3, 2021
10e22b9
implemented add collaborators
dev-raj-1729 Aug 4, 2021
0fe14e2
implemented remove collaborators
dev-raj-1729 Aug 6, 2021
89cd115
collab in getAllToDo
dev-raj-1729 Aug 6, 2021
cc508ae
added createdBy in collabTodos
dev-raj-1729 Aug 6, 2021
9165abd
login error handling
dev-raj-1729 Aug 7, 2021
6ef0413
signup error handling
dev-raj-1729 Aug 7, 2021
0e111e2
profile error handling
dev-raj-1729 Aug 7, 2021
4b1d8b1
error handling
dev-raj-1729 Aug 7, 2021
71e3702
signup error handling
dev-raj-1729 Aug 7, 2021
6e3af02
error handling
dev-raj-1729 Aug 7, 2021
23ca610
code formatting
dev-raj-1729 Aug 7, 2021
0893c2c
error handling
dev-raj-1729 Aug 8, 2021
920461a
deleteToDo permission bugfix
dev-raj-1729 Aug 8, 2021
d2de4e8
added a body validator middleware
dev-raj-1729 Aug 8, 2021
fa8f647
error handling
dev-raj-1729 Aug 8, 2021
7121aeb
body validation
dev-raj-1729 Aug 8, 2021
f560323
error handling
dev-raj-1729 Aug 8, 2021
f53b8aa
bugfix
dev-raj-1729 Aug 8, 2021
af3fdd8
bugfix
dev-raj-1729 Aug 8, 2021
23f9412
error handling + debug code removal
dev-raj-1729 Aug 8, 2021
a3dff7f
debug code removed
dev-raj-1729 Aug 8, 2021
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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -116,3 +116,6 @@ dist
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*

# REST client file
*.rest
214 changes: 213 additions & 1 deletion controllers/todo.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const { ToDo, Token } = require("../models");
const mongoose = require('mongoose');
const { ToDo, Token, User } = require("../models");

// All the given method require token.
// So be sure to check for it before doing any stuff
Expand All @@ -7,34 +8,245 @@ const { ToDo, Token } = require("../models");
const getAllToDo = async (req, res) => {
// Get the token in header.
// Use the token to get all the ToDo's of a user
ToDo.find(
{
$or: [
{ createdBy: req.user.id },
{ collaborators: req.user.id }
]
},
'_id title createdBy',
(err, todos) => {
if (err) {
console.log(err);
return res.sendStatus(500);
}
const createdTodos = todos.filter(todo => todo.createdBy == req.user.id);
const collabTodos = todos.filter(todo => todo.createdBy != req.user.id);
User.find({
_id: { $in: collabTodos.map(todo => todo.createdBy) }
},
'_id username',
(err, creators) => {
if (err) {
console.log(err);
return res.sendStatus(500);
}
res.status(200).json({
createdTodos: createdTodos.map(todo => ({
id: todo.id,
title: todo.title
})),
collabTodos: collabTodos.map(todo => ({
id: todo.id,
title: todo.title,
createdBy: creators.find(creator => creator.id == todo.createdBy).username
}))
}
);
});

});
};

const createToDo = async (req, res) => {
// Check for the token and create a todo
// or throw error correspondingly
const todo = new ToDo({
title: req.body.title,
createdBy: req.user.id,
collaborators: []
});
todo.save().then(todo => res.status(200).json(
{
id: todo.id,
title: todo.title
}
)).catch(err => {
console.log(err);
res.sendStatus(500);
});

};

const getParticularToDo = async (req, res) => {
// Get the Todo of the logged in user with given id.
ToDo.findById(req.params.id, '_id title collaborators createdBy', (err, todo) => {
if (err) {
console.log(err);
return res.sendStatus(500);
}
if (todo == null) {
return res.sendStatus(404);
}
if (todo.createdBy != req.user.id &&
!(todo.collaborators && todo.collaborators.includes(req.user.id))) {
return res.sendStatus(403);
}

User.find({
$or: [
{_id: {$in: todo.collaborators }},
{_id: todo.createdBy}
]
},(err,users)=>{
if (err || users == null) {
res.sendStatus(500);
}
let creator = users.splice(users.findIndex(user=>user.id == todo.createdBy),1)[0];
res.status(200).json(
{
id: todo.id,
title: todo.title,
createdBy: creator.username,
collaborators: users.map(user=>user.username)
}
);
})
});
};

const editToDo = async (req, res) => {
// Change the title of the Todo with given id, and get the new title as response.
ToDo.findById(req.params.id, (err, todo) => {
if (err) {
console.log(err);
return res.sendStatus(500);
}
if (todo == null) {
return res.sendStatus(404);
}
if (todo.createdBy != req.user.id && !todo.collaborators.includes(req.user.id)) {
return res.sendStatus(403);
}
todo.title = req.body.title;
todo.save().then(todo => res.status(200).json({
id: todo.id,
title: todo.title
}));
});
};

const editToDoPatch = async (req, res) => {
// Change the title of the Todo with given id, and get the new title as response
editToDo(req, res);
};

const deleteToDo = async (req, res) => {
// Delete the todo with given id
ToDo.findById(req.params.id, (err, todo) => {
if (err) {
console.log(err);
res.sendStatus(500);
}
if (todo == null) {
return res.sendStatus(404);
}
if (todo.createdBy != req.user.id && !todo.collaborators.includes(req.user.id)) {
return res.sendStatus(403);
}
todo.remove((err, _) => {
if (err) {
console.log(err);
return res.sendStatus(500);
}
return res.sendStatus(204);
})
})
};

const addCollaborator = async (req, res) => {
// Add Collaborators to todo with given id
const collaborator = req.body.collaborator;
if (!collaborator) {
res.status(400).json({
error: "Missing required fields"
});
}
ToDo.findById( req.params.id, (err, todo) => {
if (err) {
console.log(err);
return res.sendStatus(500);
}
if (todo == null) {
return res.sendStatus(404);
}
if (todo.createdBy != req.user.id) {
return res.sendStatus(403);
}
User.findOne({username : collaborator}, '_id username', (err,collab)=>{
if (err) {
console.log(err);
return res.sendStatus(500);
}
if (collab == null) {
return res.status(404).json({
error: "User not found"
});
}
if (collab.id == req.user.id) {
return res.status(422).json({
error: "Creator cannot be added as collaborator"
});
}
if (todo.collaborators && todo.collaborators.includes(collab.id)) {
return res.status(409).json({
error: "User is already a collaborator"
});
}
if (todo.collaborators) {
todo.collaborators.push(collab.id);
} else {
todo.collaborators = [collab.id];
}
todo.save().then(res.sendStatus(200)).catch(_ => res.sendStatus(500));
});
});
};

const removeCollaborator = async (req, res) => {

ToDo.findById(req.params.id, (err, todo) => {
if (err) {
console.log(err);
return res.sendStatus(500);
}
if (todo == null) {
return res.status(404).json({
error: "Todo not found"
});
}
if (todo.createdBy != req.user.id) {
return res.sendStatus(403);
}
User.findOne({ username: req.body.collaborator }, (err, user) => {
if (err) {
console.log(err);
return res.sendStatus(500);
}
if (user == null) {
return res.status(404).json({
error: "User not found"
});
}
if (!todo.collaborators.includes(user.id)) {
return res.status(404).json({
error: "User not found in collaborators"
});
}
todo.collaborators.splice(todo.collaborators.indexOf(user.id), 1);
todo.save().then(_ => res.sendStatus(200)).catch(_ => res.sendStatus(500));
});
});
}

module.exports = {
createToDo,
deleteToDo,
editToDo,
editToDoPatch,
getAllToDo,
getParticularToDo,
addCollaborator,
removeCollaborator,
};
100 changes: 100 additions & 0 deletions controllers/user.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
const { User, Token } = require("../models");
const { randomBytes } = require("crypto");
const { validate } = require("../utils");
const bcrypt = require("bcrypt");

const createToken = (user) => {
return Token({
Expand All @@ -13,20 +15,118 @@ const login = async (req, res) => {
// Check if data is valid
// Return correct status codes: https://en.wikipedia.org/wiki/List_of_HTTP_status_codes
// If the user is verified, then return a token along with correct status code
const body = req.body;
if (!(body.username && body.password)) {
return res.status(400).send({error: "Missing required fields"});
}
User.findOne({'username':body.username},'_id password',async (err,user)=>{
if (err) {
console.log(err);
return res.sendStatus(500);
}
if (user == null){
return res.status(401).json({error:"Invalid username or password"});
}
const match = await bcrypt.compare(body.password,user.password);
if (match) {
Token.findOne({user:user.id},(err,token)=>{
res.status(200).json({token:token.token});
})
}
else {
res.status(401).json({error:"Invalid username or password"});
}
});

};

const signup = async (req, res) => {
// TODO: Read username, email, name, pwd from the req object
// Hash the password
// Return with appropriate status code in case of an error
// If successful, return with an appropriate token along with correct status code
const body = req.body;
if(!(body.email && body.password && body.name && body.username)) {
return res.status(400).send({error: "Missing required fields"});
}
Comment on lines +49 to +51
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not validating using the middleware here?

if (!validate.isValidEmail(body.email)) {
return res.status(400).send({error:"Invalid Email"});
}
if (!validate.isValidUsername(body.username)) {
return res.status(400).send({error:"Invalid Username"});
}
if (!validate.isValidPassword(body.password)) {
return res.status(400).send({error:"Invalid Password"});
}
if (!validate.isValidName(body.name)) {
return res.status(400).send({error: "Invalid Name"});
}

const user = new User(body);
bcrypt.genSalt(10).then(salt=>{
bcrypt.hash(user.password,salt).then(hashedPassword =>{
user.password = hashedPassword;
user.save().then(user=>{
createToken(user).save().then(tokenObject=>{
res.status(200).json({token:tokenObject.token});
})
}).catch(err=>{
if (err.errors.username && err.errors.username.kind=='unique') {
return res.status(409).json({
error: "username already in use"
});
}
if (err.errors.email && err.errors.email.kind == 'unique') {
return res.status(409).json({
error: "email already in use"
});
}
console.log(err);
res.sendStatus(500);
});
});
});

};

const profile = async (req, res) => {
// TODO:
// Implement the functionality to retrieve the details
// of the logged in user.
// Check for the token and then use it to get user details
const authHeader = req.headers.authorization;
if (authHeader &&
authHeader.split(' ')[0].toLowerCase() == 'token' &&
authHeader.split(' ').length == 2) {
const token = authHeader.split(' ')[1];

Token.findOne({'token':token},'user',(err,token)=>{
if(err) {
console.log(err);
res.sendStatus(500);
}
if (token==null){
return res.status(401).json({
error: "Invalid Token"
});
}
User.findById(token.user,'_id name email username',(err,user)=>{
if (user == null) {
return res.status(404).json({
error: "User Not Found"
});
}
res.status(200).json({
id: user.id,
name: user.name,
email: user.email,
username: user.username
});
});
});
} else {
return res.sendStatus(401);
}
};

module.exports = { login, signup, profile };
Loading