├── .gitignore ├── appcontroller.js ├── email.js ├── index.js ├── package-lock.json ├── package.json ├── promise chaining.js ├── src ├── db │ ├── mongoose.js │ ├── task_module.js │ └── user_module.js ├── middelware │ └── auth_ware.js └── routers │ ├── task_routers.js │ └── user_routers.js └── tests ├── app.js ├── fixtures └── pic.jpg └── user.test.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | 3 | config/ 4 | -------------------------------------------------------------------------------- /appcontroller.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | require('./src/db/mongoose'); 3 | var UserRouter = require("./src/routers/user_routers"); 4 | var TaskRouter = require("./src/routers/task_routers"); 5 | module.exports= 6 | function(app) 7 | { 8 | app.use(express.json()); 9 | app.use(UserRouter); 10 | app.use(TaskRouter); 11 | 12 | app.get('/',function(req,res) 13 | { 14 | res.send("hi there"); 15 | res.end(); 16 | }); 17 | 18 | 19 | 20 | 21 | 22 | } 23 | -------------------------------------------------------------------------------- /email.js: -------------------------------------------------------------------------------- 1 | 2 | const sgMail = require('@sendgrid/mail'); 3 | sgMail.setApiKey(process.env.SENDGRID_API_KEY); 4 | 5 | 6 | 7 | module.exports.sendmail=function(reciever,subject,body){ 8 | const msg = { 9 | to: reciever, 10 | from: 'jainsanyamco@gmail.com', 11 | subject: subject, 12 | text: 'WhatsUP kiddo?', 13 | html: '' + body + '', 14 | }; 15 | sgMail.send(msg); 16 | //console.log(reciever); 17 | }; 18 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var express=require('express'); 2 | var appcontroller = require('./appcontroller'); 3 | var app= express(); 4 | appcontroller(app); 5 | 6 | var port = process.env.PORT ; 7 | app.listen(port ,() => { 8 | console.log("server is running" + process.env.PORT)} 9 | ); 10 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "task_manger", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "app.js", 6 | "scripts": { 7 | "start": "node index.js", 8 | "dev": "env-cmd -f ./config/dev.env nodemon index", 9 | "test":"env-cmd -f ./config/test.env jest --watch" 10 | }, 11 | "jest":{ 12 | "testEnvironment":"node" 13 | }, 14 | "author": "", 15 | "license": "ISC", 16 | "dependencies": { 17 | "@sendgrid/mail": "^6.4.0", 18 | "bcryptjs": "^2.4.3", 19 | "express": "^4.17.1", 20 | "jsonwebtoken": "^8.5.1", 21 | "mongoose": "^5.7.5", 22 | "multer": "^1.4.2", 23 | "sharp": "^0.23.2", 24 | "validator": "^11.1.0" 25 | }, 26 | "devDependencies": { 27 | "env-cmd": "^10.0.1", 28 | "jest": "^24.9.0", 29 | "jira": "^0.9.2", 30 | "nodemon": "^1.19.3", 31 | "supertest": "^4.0.2" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /promise chaining.js: -------------------------------------------------------------------------------- 1 | // this file is redundent... 2 | // its just for practice...no role in working of app 3 | var add= (num1 , num2) =>{ 4 | return new Promise((resolve ,reject ) => { 5 | 6 | 7 | setTimeout(function () { 8 | 9 | resolve(num1 + num2); 10 | }, 2000); 11 | }); 12 | } 13 | const func = async () =>{ 14 | var sum1 = await add(1,2); 15 | var sum2 = await add(sum1,2); 16 | var sum3 = await add(sum2,3); 17 | return sum3; 18 | } 19 | 20 | 21 | 22 | 23 | func().then( (user)=>{ 24 | console.log(user); 25 | }).catch((err) => { 26 | console.log("error" + err); 27 | }); 28 | -------------------------------------------------------------------------------- /src/db/mongoose.js: -------------------------------------------------------------------------------- 1 | var mongoose = require('mongoose'); 2 | 3 | mongoose.connect(process.env.MONGODB_LINK , {useNewUrlParser:true}).then(()=>{ 4 | console.log("succesfulluy connected to mongodb"); 5 | }) 6 | .catch((error)=>{console.log("error in connnecting to mongo server" , error)}); 7 | -------------------------------------------------------------------------------- /src/db/task_module.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose') 2 | const validator = require('validator'); 3 | const User = require("./user_module"); 4 | var taskSchema=new mongoose.Schema({ 5 | work: 6 | { 7 | required:true, 8 | type:String, 9 | trim:true 10 | 11 | }, 12 | completed:{ 13 | type:Boolean, 14 | default:false 15 | }, 16 | owner:{ 17 | type:mongoose.Schema.Types.ObjectID, 18 | required:true, 19 | ref:'User' 20 | } 21 | 22 | }, 23 | { 24 | timestamps:true 25 | }); 26 | var Task= mongoose.model('Task' , taskSchema); 27 | module.exports = Task; 28 | -------------------------------------------------------------------------------- /src/db/user_module.js: -------------------------------------------------------------------------------- 1 | var validator= require('validator'); 2 | var mongoose= require('mongoose'); 3 | var bcrypt = require('bcryptjs'); 4 | var jwt = require("jsonwebtoken"); 5 | const Task = require("./task_module"); 6 | var userSchema = new mongoose.Schema({ 7 | name: { 8 | required:true, 9 | type:String, 10 | trim:true, 11 | }, 12 | age:{ 13 | type:Number, 14 | default:10, 15 | required:true, 16 | min:0, 17 | 18 | } , 19 | email:{ 20 | type:String, 21 | required:true, 22 | trim:true, 23 | lowercase:true, 24 | validate(value){ 25 | if(!validator.isEmail(value)) 26 | throw "error:is not email"; 27 | } 28 | }, 29 | password:{ 30 | type:String, 31 | trim:true, 32 | minlength:8, 33 | required:true 34 | }, 35 | tokens:[ 36 | { 37 | token:{ 38 | required:true, 39 | type:String 40 | } 41 | } 42 | ], 43 | avatar:{ 44 | type:Buffer 45 | } 46 | }, 47 | { 48 | timestamps:true 49 | } ); 50 | 51 | //a virtual field.. 52 | // to show tasks against his user 53 | userSchema.virtual("tasks",{ 54 | ref:'Task', 55 | localField:"_id", 56 | foreignField:"owner" 57 | }); 58 | 59 | //function to hashd the password before save 60 | userSchema.pre('save' , async function(next){ 61 | var user = this; 62 | 63 | 64 | var hashedPass =""; 65 | if(user.isModified('password')) 66 | { 67 | hashedPass = await bcrypt.hash(user.password,8); 68 | //console.log(hashedPass); 69 | user.password =hashedPass; 70 | } 71 | 72 | 73 | next(); 74 | }); 75 | 76 | 77 | 78 | //function to get a jwt token 79 | userSchema.methods.getAuthToken = async function(){ 80 | var user = this; 81 | var token = await jwt.sign({_id:user._id.toString()},"secretkey"); 82 | user.tokens= user.tokens.concat({token}); 83 | await user.save(); 84 | return token; 85 | 86 | } 87 | 88 | userSchema.methods.toJSON = function (){ 89 | var user= this; 90 | user = user.toObject(); 91 | delete user.tokens; 92 | delete user.password; 93 | delete user.__v; 94 | delete user.avatar; 95 | //console.log(user); 96 | return user; 97 | } 98 | 99 | //this function is called at time of login 100 | userSchema.statics.findByCredentials = async (email,password) => 101 | { 102 | var user = await User.findOne({email}); 103 | if(!user){ 104 | // throw new Error("unable to login"); 105 | return undefined; 106 | } 107 | var isFound = bcrypt.compare(password,user.password); 108 | //console.log("value of is found"+ JSON.stringify(isFound)); 109 | if(!isFound){ 110 | console.log("error thrrownnnnn"); 111 | throw new Error("unable to login"); 112 | } 113 | return user 114 | } 115 | 116 | var User = mongoose.model('Users', userSchema); 117 | module.exports =User; 118 | -------------------------------------------------------------------------------- /src/middelware/auth_ware.js: -------------------------------------------------------------------------------- 1 | var jwt = require("jsonwebtoken"); 2 | var User = require("../db/user_module"); 3 | 4 | async function auth(req,res,next){ 5 | try{ 6 | var token = req.header("Authorization").replace("Bearer ",""); 7 | var decoded = jwt.verify(token,process.env.JWT_SECRET_KEY); 8 | var user = await User.findOne({_id:decoded._id,"tokens.token":token}); 9 | if(!user) 10 | { 11 | throw new Error("please authenticiate again"); 12 | } 13 | else{ 14 | req.token= token; 15 | req.user = user; 16 | next(); 17 | 18 | } 19 | } 20 | catch(e) 21 | { 22 | res.status(401).send("please authenticiate again"); 23 | } 24 | 25 | } 26 | module.exports = auth; 27 | -------------------------------------------------------------------------------- /src/routers/task_routers.js: -------------------------------------------------------------------------------- 1 | var express = require("express"); 2 | var router = express.Router(); 3 | var Task= require('../db/task_module'); 4 | var auth = require("../middelware/auth_ware"); 5 | 6 | 7 | //creating a new task 8 | router.post('/task',auth, async (req, res) => { 9 | try{ 10 | 11 | var task = new Task({ ...req.body,owner:req.user._id}) 12 | var task_data = await task.save(); 13 | //await task_data.populate("owner").execPopulate(); 14 | res.status(201).send(task_data); 15 | } 16 | catch(e){ 17 | console.log(e); 18 | res.status(400).send(e); 19 | } 20 | 21 | }) 22 | 23 | //to get all task of a user 24 | router.get('/tasks',auth, async function(req,res){ 25 | try{ 26 | var match={}; 27 | if(req.query.completed) 28 | { 29 | match.completed = req.query.completed === "true" 30 | } 31 | 32 | var sort={}; 33 | if(req.query.sortBy) 34 | { 35 | var parts=req.query.sortBy.split(":"); 36 | sort[parts[0]] = parts[1] ==="desc"? -1:1; 37 | } 38 | 39 | await req.user.populate({ path:"tasks", 40 | match, 41 | options:{ 42 | limit:parseInt(req.query.limit), 43 | skip:parseInt(req.query.skip), 44 | sort 45 | }}).execPopulate(); 46 | 47 | res.send(req.user.tasks); 48 | } 49 | catch(e){ 50 | res.send(e); 51 | 52 | } 53 | 54 | }); 55 | 56 | //to get a task details by his id 57 | router.get('/tasks/:id',auth, async function(req,res){ 58 | try{ 59 | var task = await Task.findOne({_id:req.params.id,owner:req.user._id}); 60 | if(!task){ 61 | throw new Error("cant find such task"); } 62 | res.send(task); 63 | } 64 | catch(e){ 65 | res.status(404).send(e.toString()); 66 | 67 | } 68 | }); 69 | 70 | //to update a task by his id 71 | router.patch("/tasks/:id" , auth,async (req, res ) =>{ 72 | var allowedUpdates = ["work", "completed"]; 73 | var updates = Object.keys(req.body); 74 | var isValidOperation = updates.every( (item)=> allowedUpdates.includes(item)) 75 | if(!isValidOperation) 76 | res.status(400).send("ERROR:cant find the property you requested "); 77 | try{ 78 | var task = await Task.findOneAndUpdate({_id:req.params.id,owner:req.user._id},req.body,{new:true, runValidators:true}); 79 | // var task = await Task.findByIdAndUpdate(req.params.id,req.body,{new:true, runValidators:true}); 80 | if(!task) 81 | res.status(400).send("error") 82 | 83 | res.send(task); 84 | } 85 | catch(e){ 86 | res.status(400).send(e); 87 | } 88 | }); 89 | 90 | //to delete a task by his id 91 | router.delete("/tasks/:id" , auth , async (req,res) =>{ 92 | try{ 93 | var task = await Task.findOneAndRemove({_id:req.params.id,owner:req.user._id}); 94 | if(!task) 95 | res.status(404).send(); 96 | 97 | res.send(task); 98 | } 99 | catch(e) 100 | { 101 | res.status(400).send(e.toString()); 102 | } 103 | }); 104 | module.exports = router; 105 | -------------------------------------------------------------------------------- /src/routers/user_routers.js: -------------------------------------------------------------------------------- 1 | var express = require("express"); 2 | var multer = require('multer'); 3 | var router = express.Router(); 4 | var User= require('../db/user_module'); 5 | var Task =require('../db/task_module'); 6 | var sharp = require("sharp"); 7 | var auth = require('../middelware/auth_ware'); 8 | var emailutil = require('../../email'); 9 | // uploading a profile pic 10 | var upload=multer({ 11 | limits: 12 | { 13 | fileSize:1000000 14 | }, 15 | fileFilter(req,file,cb){ 16 | if(!file.originalname.match(/\.(jpg|png|jpeg)$/)){ 17 | return cb(new Error("Please upload a jpg img")); 18 | } 19 | cb(undefined,true); 20 | } 21 | }); 22 | router.post('/users/me/avatar',auth,upload.single("avatar"), async function(req,res) 23 | { 24 | var buffer = await sharp(req.file.buffer).resize({ 25 | width:250, 26 | height:250 27 | }).png().toBuffer(); 28 | req.user.avatar= buffer; 29 | await req.user.save(); 30 | res.send("uploaded"); 31 | 32 | },(err,req,res,next)=>{ 33 | //console.log(err.message); 34 | res.status(400).send({error:err.message}); 35 | }); 36 | 37 | 38 | // deleting a profile pic 39 | router.delete('/users/me/avatar',auth, async function(req,res) 40 | { 41 | req.user.avatar= undefined; 42 | await req.user.save(); 43 | res.send("deleted"); 44 | 45 | },(err,req,res,next)=>{ 46 | res.status(400).send({error:err.message}); 47 | }); 48 | 49 | 50 | // getting a avatar 51 | router.get('/users/me/avatar',async function(req,res) 52 | { 53 | res.set("Content-Type","image/jpg"); 54 | var user = await User.findById("5db71379e308cb343441695c"); 55 | // var avatar = user.avatar; 56 | // console.log(user.avatar); 57 | res.send(user.avatar); 58 | 59 | },(err,req,res,next)=>{ 60 | res.status(400).send({error:err.message}); 61 | }); 62 | 63 | 64 | // getting a user profile... 65 | router.get('/users/me',auth, async function(req,res) 66 | { 67 | // try 68 | // { 69 | // 70 | // //var users = await User.find({}); 71 | // res.send(req.user); 72 | // } catch (e) { 73 | // res.status(400).send(e); 74 | 75 | // } 76 | await req.user.populate("tasks").execPopulate(); 77 | //console.log( req.user.tasks) 78 | res.send(req.user); 79 | 80 | }); 81 | 82 | 83 | //getting user by id 84 | // router.get('/users/:id',async function(req,res){ 85 | // try{ 86 | // var users = await User.findById(req.params.id); 87 | // if(!users) 88 | // { 89 | // throw new Error("cant find any users with given id "); 90 | // } 91 | // res.send(users); 92 | // } 93 | // catch(e){ 94 | // res.send(e); 95 | // } 96 | // 97 | // }); 98 | 99 | 100 | // updating user 101 | router.patch("/users/me" , auth ,async (req, res ) =>{ 102 | var allowedUpdates = ["name", "email", "password","age"]; 103 | var updates = Object.keys(req.body); 104 | var isValidOperation = updates.every( (item)=> allowedUpdates.includes(item)) 105 | if(!isValidOperation) 106 | res.status(400).send("ERROR:cant find the property you requested "); 107 | try{ 108 | var user = req.user; 109 | updates.forEach((key)=>{user[key] = req.body[key]}); 110 | await user.save(); 111 | //var user = await User.findByIdAndUpdate(req.params.id,req.body,{new:true, runValidators:true}); 112 | if(!user) 113 | res.status(400).send("error"); 114 | 115 | res.status(200).send(user); 116 | } 117 | catch(e){ 118 | res.status(400).send(e); 119 | } 120 | }); 121 | 122 | 123 | //delting user 124 | router.delete("/users/me" ,auth, async (req,res) =>{ 125 | try{ 126 | var user = req.user; 127 | await user.remove(); 128 | await Task.deleteMany({owner:user._id}); 129 | emailutil.sendmail(user.email,"welcome to my new app","please share your experience"); 130 | res.send(req.user); 131 | } 132 | catch(e) 133 | { 134 | res.status(400).send(e); 135 | } 136 | }) 137 | 138 | 139 | //creating a new user 140 | router.post('/users', async function(req,res){ 141 | try{ 142 | var user = new User(req.body); 143 | var user = await user.save(); 144 | var token = await user.getAuthToken(); 145 | // emailutil.sendmail(user.email,"welcome to my new app","please share your experience"); 146 | 147 | res.status(201).send({user,token}); 148 | }catch(e) 149 | { 150 | console.log(e); 151 | res.status(400).send("error"); 152 | } 153 | 154 | }); 155 | 156 | //login a user 157 | router.post('/users/login', async function(req,res){ 158 | try{ 159 | var user = await User.findByCredentials(req.body.email,req.body.password); 160 | //console.log(req.body.password); 161 | //console.log(user); 162 | 163 | if(!user){ 164 | throw new Error("unable to login"); 165 | } 166 | var token = await user.getAuthToken(); 167 | 168 | res.status(200).send({user,token}); 169 | } 170 | catch(e) 171 | { 172 | //console.log(e); 173 | res.status(400).send("unable to login"); 174 | } 175 | 176 | }); 177 | 178 | //log out user 179 | router.post("/users/logout",auth, async function(req,res){ 180 | try{ 181 | req.user.tokens = req.user.tokens.filter((token_obj)=>{ 182 | return token_obj.token !==req.token; 183 | }) 184 | await req.user.save(); 185 | res.status(200).send("logout out successfully"); 186 | } 187 | catch(e) 188 | { 189 | res.status(500).send(e); 190 | } 191 | } ); 192 | 193 | 194 | //logout from alll devices 195 | router.post("/users/logoutALL",auth, async function(req,res){ 196 | try{ 197 | req.user.tokens = req.user.tokens.filter((token_obj)=>{ 198 | return 0; 199 | }) 200 | await req.user.save(); 201 | res.status(200).send("logout out successfully"); 202 | } 203 | catch(e) 204 | { 205 | res.status(500).send(e); 206 | } 207 | } ); 208 | 209 | 210 | module.exports= router; 211 | -------------------------------------------------------------------------------- /tests/app.js: -------------------------------------------------------------------------------- 1 | var express=require('express'); 2 | var appcontroller = require('../appcontroller'); 3 | var app= express(); 4 | appcontroller(app); 5 | module.exports = app; 6 | -------------------------------------------------------------------------------- /tests/fixtures/pic.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IZUNA894/Task-manager-api/49ad63afad246af3a5c48c0917cd4447578b7317/tests/fixtures/pic.jpg -------------------------------------------------------------------------------- /tests/user.test.js: -------------------------------------------------------------------------------- 1 | const request = require("supertest"); 2 | var mongoose = require("mongoose"); 3 | var jwt= require("jsonwebtoken"); 4 | const app = require("./app"); 5 | const user = require("../src/db/user_module.js"); 6 | 7 | //cleaning the database... 8 | var userOneId =new mongoose.Types.ObjectId(); 9 | var userOne = { 10 | _id:userOneId, 11 | name:"sam", 12 | age:20, 13 | email:"sanyamco@gmail.com", 14 | password:"blablabla", 15 | tokens:[ 16 | { 17 | token:jwt.sign({_id:userOneId},process.env.JWT_SECRET_KEY) 18 | } 19 | ]} 20 | 21 | beforeEach(async ()=>{ 22 | await user.deleteMany(); 23 | await new user(userOne).save(); 24 | }); 25 | //basic test.... 26 | test("basic testup",async ()=>{ 27 | await request(app).get("/").expect("hi there"); 28 | }); 29 | 30 | //creating a new user... 31 | test("creating new user",async ()=>{ 32 | await request(app) 33 | .post('/users') 34 | .send({ 35 | "name":"sam112", 36 | "age":20, 37 | "email":"kknsanyamco@gmail.com", 38 | "password":"akdsafsakf"}) 39 | .expect(201); 40 | }); 41 | 42 | //login a user... 43 | test("login a user",async ()=>{ 44 | await request(app) 45 | .post('/users/login') 46 | .send({ 47 | 48 | "email":"sanyamco@gmail.com", 49 | "password":"blablabla"}) 50 | .expect(200); 51 | }); 52 | 53 | 54 | //login a user.. 55 | // test("should not login a wrong user",async ()=>{ 56 | // await request(app) 57 | // .post('/users/login') 58 | // .send({ 59 | // 60 | // "email":"sanyamco@gmail.com", 61 | // "password":"slfja;sfj;a"}) 62 | // .expect(400); 63 | // }); 64 | 65 | 66 | //getting a user profile... 67 | test("getting a user profile ",async ()=>{ 68 | await request(app) 69 | .get('/users/me') 70 | .set("Authorization", `Bearer ${userOne.tokens[0].token}`) 71 | .send() 72 | .expect(200); 73 | }); 74 | 75 | //getting a user profile... 76 | test(" should not get a user profile",async ()=>{ 77 | await request(app) 78 | .get('/users/me') 79 | .send() 80 | .expect(401); 81 | }); 82 | 83 | //deleting a user ... 84 | test("deleting a user profile ",async ()=>{ 85 | await request(app) 86 | .delete('/users/me') 87 | .set("Authorization", `Bearer ${userOne.tokens[0].token}`) 88 | .send() 89 | .expect(200); 90 | }); 91 | 92 | //deleting a user profile... 93 | test("should not be deleting a user profile ",async ()=>{ 94 | await request(app) 95 | .delete('/users/me') 96 | .send() 97 | .expect(401); 98 | }); 99 | 100 | 101 | //uploading a avatar... 102 | test("uploading a user aavatar " , async ()=>{ 103 | var response = await request(app) 104 | .post('/users/me/avatar') 105 | .set("Authorization", `Bearer ${userOne.tokens[0].token}`) 106 | .attach("avatar","tests/fixtures/pic.jpg") 107 | .expect(200); 108 | //console.log(response); 109 | }) 110 | 111 | 112 | //updating usr info ... 113 | test("updating a user info",async ()=>{ 114 | await request(app) 115 | .patch('/users/me') 116 | .set("Authorization", `Bearer ${userOne.tokens[0].token}`) 117 | .send({ 118 | "name":"sam112", 119 | "age":22}) 120 | .expect(200); 121 | }) 122 | 123 | 124 | //updating usr info ... 125 | test("should not updating awrong user field info",async ()=>{ 126 | await request(app) 127 | .patch('/users/me') 128 | .set("Authorization", `Bearer ${userOne.tokens[0].token}`) 129 | .send({ 130 | "name":"sam112", 131 | "wife":22}) 132 | .expect(400); 133 | }) 134 | --------------------------------------------------------------------------------