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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
node_modules
6 changes: 6 additions & 0 deletions config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// NOTE: select node 2.2.12 when getting uri from mongodb atlas
// otherwise it may throw an error
// enter the database uri here
const dbURI = "";

module.exports.dbURI = dbURI;
143 changes: 142 additions & 1 deletion controllers/todo.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
const { ToDo, Token } = require("../models");
const User = require("../models/user.js");
const Todo = require("../models/todo.js");
const checker = require('mongoose').Types.ObjectId.isValid;

// All the given method require token.
// So be sure to check for it before doing any stuff
Expand All @@ -7,27 +10,163 @@ 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
let result = await Todo.find({
$or: [{createdBy: res.locals.token.user._id}, {collaborators: res.locals.token.user._id}
]}, "_id title").exec();
res.status(200).json(result);
};

const createToDo = async (req, res) => {
// Check for the token and create a todo
// or throw error correspondingly
let todo = new Todo({
title: req.body.title,
createdBy: res.locals.token.user._id,
collaborators: []
});

await todo.save();
res.status(200).json({id: todo._id, title: todo.title});
};

const getParticularToDo = async (req, res) => {
// Get the Todo of the logged in user with given id.
if(checker(req.params.id)){
let result = await Todo.findOne({
$and: [{_id: req.params.id}, {$or: [{collaborators: res.locals.token.user._id}, {createdBy: res.locals.token.user._id}]}]
}, "_id title").exec();

return res.status(200).json(result);
}
else{
return res.status(400).send("Invalid todo id!");
}
};

const editToDo = async (req, res) => {
// Change the title of the Todo with given id, and get the new title as response.
if(checker(req.params.id)){
let result = await Todo.findOne({
$and: [
{_id: req.params.id},
{$or: [{createdBy: res.locals.token.user._id}, {collaborators: res.locals.token.user._id}]}
]}
, "_id title").exec();

if(result){
result.title = req.body.title;
await result.save();
res.status(200).json({id: result._id, title: result.title});
}
else{
return res.status(400).send("Either todo id incorrect or you dont have edit rights!");
}
}
else{
return res.status(400).send("Invalid todo id!");
}
};

const editToDoPatch = async (req, res) => {
// Change the title of the Todo with given id, and get the new title as response
if(checker(req.params.id)){
let result = await Todo.findOne({
$and: [
{_id: req.params.id},
{$or: [{createdBy: res.locals.token.user._id}, {collaborators: res.locals.token.user._id}]}
]}
, "_id title").exec();

if(result){
result.title = req.body.title;
await result.save();
res.status(200).json({id: result._id, title: result.title});
}
else{
return res.status(400).send("Either todo id incorrect or you dont have edit rights!");
}
}
else{
return res.status(400).send("Invalid todo id!");
}
};

const deleteToDo = async (req, res) => {
// Delete the todo with given id
if(checker(req.params.id)){
let result = await Todo.findOne({
$and: [
{_id: req.params.id},
{$or: [{createdBy: res.locals.token.user._id}, {collaborators: res.locals.token.user._id}]}
]}).exec();
if(result){
await result.remove();
res.status(200).send("todo deleted!");
}
else{
return res.status(400).send("Either todo id incorrect or you dont have delete rights!");
}
}
else{
return res.status(400).send("Invalid todo id!");
}
};

const addCollaborators = async (req, res)=>{
if(checker(req.params.id)){
let username = req.body.username;
let result = await Todo.findOne({$and: [{_id: req.params.id}, {createdBy: res.locals.token.user._id}]}).exec();
let user = await User.findOne({username}).exec();

if(!user){
return res.staus(400).send(`User ${username} doesnt exist`);
}
if(result){
for(let i=0; i<result.collaborators.length; i++){
if(String(user._id) == String(result.collaborators[i])){
return res.send("collaborator Already exists!");
}
}
result.collaborators.push(user._id);
await result.save();
return res.status(200).send("collaborator added!");
}
else{
return res.status(401).send("Either todo id incorrect or you dont have adding collaborators rights!");
}
}
else{
return res.status(400).send("Invalid todo id!");
}
};


const removeCollaborators = async (req, res)=>{
if(checker(req.params.id)){
let username = req.body.username;
let result = await Todo.findOne({$and: [{_id: req.params.id}, {createdBy: res.locals.token.user._id}]}).exec();
let user = await User.findOne({username}).exec();

if(!user){
return res.status(400).send("Collaborator not found!");
}
if(result){
for(let i=0; i<result.collaborators.length; i++){
if(String(user._id) == String(result.collaborators[i])){
result.collaborators.pop(i);
await result.save();
return res.status(200).send("collaborator removed!");
}
}
return res.status(400).send("Collaborator not found!");
}
else{
return res.status(401).send("Either todo id incorrect or you dont have removing collaborators rights!");
}
}
else{
return res.status(400).send("Invalid todo id!");
}
};

module.exports = {
Expand All @@ -37,4 +176,6 @@ module.exports = {
editToDoPatch,
getAllToDo,
getParticularToDo,
};
addCollaborators,
removeCollaborators,
};
64 changes: 63 additions & 1 deletion controllers/user.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
const { User, Token } = require("../models");
const { randomBytes } = require("crypto");
const bcrypt = require("bcrypt");

const createToken = (user) => {
return Token({
Expand All @@ -13,20 +14,81 @@ 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 username = req.body.username;
const password = req.body.pwd;
let result = await User.findOne({username: username}).exec();
if(!result){
return res.status(401).send("Invalid username!");
}
bcrypt.compare(password, result.password, async (err, result2)=>{
if(result2){
let token = await Token.findOne({user: result._id}).exec();
return res.status(200).send(`Success! user loggen in with token ${token.token}`);
}
else{
return res.status(401).send("Invalid credentials!");
}
});
};

const signup = async (req, res) => {
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

//perform some security checks
if(!req.body.name || !req.body.email || !req.body.username || !req.body.pwd){
return res.status(422).send("Values cant be null!");
}

const name = req.body.name;
const email = req.body.email;
const username = req.body.username;
const password = req.body.pwd;


let result = await User.findOne({$or: [{email: email}, {username: username}]}).exec();
if(result){
return res.status(422).send("user already exists");
}

const saltRounds = 10;
bcrypt.hash(password, saltRounds, async (err, hash)=>{
let new_user = new User({
name: name,
email: email,
username: username,
password: hash
});

await new_user.save();

let token = createToken(new_user);
await token.save();
return res.status(201).send(`Success! user has been created with token ${token.token}`);
});
};

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 authToken = req.headers['authorization'];
const token = authToken.split(' ')[1];

let result = await Token.findOne({token: token}).populate('user').exec();
if(!result){
return res.status(401).send("Invalid Token");
}
let user = {
username: result.user.username,
name: result.user.name,
email: result.user.email,
createdAt: result.user.createdAt
}
return res.status(200).json(user);
};

module.exports = { login, signup, profile };
31 changes: 18 additions & 13 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,6 @@ const express = require("express");
const { json, urlencoded } = require("body-parser");
const cors = require("cors");
const mongoose = require("mongoose");

const { ToDoRoutes, UserRoutes } = require("./routes");

const app = express();

app.use(json());
Expand All @@ -14,19 +11,27 @@ app.use(cors());
// disable powered by cookies
app.disable("x-powered-by");

const { ToDoRoutes, UserRoutes } = require("./routes");
app.use("/api/auth", UserRoutes);
app.use("/api/todo", ToDoRoutes);

const PORT = process.env.PORT || 8000;
const mongoDB = "mongodb://127.0.0.1/my_database";

mongoose.set("useFindAndModify", false);
mongoose.set("useCreateIndex", true);
mongoose
.connect(mongoDB, { useNewUrlParser: true, useUnifiedTopology: true })
.then(() => {
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
})
.catch((err) => console.log(err.message));

// const mongoDB = "mongodb://127.0.0.1/my_database";
// using mongodb atlas for database
const dbURI = require("./config.js").dbURI;
mongoose.connect(dbURI, {
useNewUrlParser: true,
useFindAndModify: false,
useUnifiedTopology: true,
useCreateIndex: true
})
.then((result) => console.log("connected to db"))
.catch((err) => console.log(err));

const PORT = process.env.PORT || 3000;
app.listen(PORT, (req,res)=>{
console.log(`server running on port ${PORT}!`);
});
15 changes: 15 additions & 0 deletions middleware/auth.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
const Token = require("../models/token.js");

const userAuth = async (req, res, next)=>{
const authToken = req.headers['authorization'];
const token = authToken.split(' ')[1];

let result = await Token.findOne({token: token}).populate('user').exec();
if(!result){
return res.status(401).send("Invalid Token!");
}
res.locals.token = result;
next();
};

module.exports = userAuth;
1 change: 1 addition & 0 deletions models/todo.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const todoSchema = new Schema(
{
title: { type: String, required: true },
createdBy: { type: Schema.Types.ObjectId, ref: "User" },
collaborators: [{ type: Schema.Types.ObjectId, ref: "User" }],
},
{ timestamps: true }
);
Expand Down
Loading