diff --git a/README.md b/README.md index 7e08b13..62f0d3a 100644 --- a/README.md +++ b/README.md @@ -1 +1,333 @@ -# fancy-todo \ No newline at end of file +# fancy-todo +### ESY + +Routes +--- +server: http://localhost:3000/
+client: http://localhost:8080/
+Access : http://localhost:3000/ + +Bellows are routes that used in the sever fancy-todo.js + +- base routes TODOS url : http://localhost:3000/todos + + - POST : / + - description : create new todo + - body : + ``` + { + name : String + description : String + due : date + } + ``` + - Headers : JWT Token + - Response : + - Success : + Status Code : 201 + ``` + { + "_id": "5dbef87fe3da1d19f4a5334e", + "name": "maen yugi", + "description": "maen kartu teruss", + "due": "2019-03-11T00:00:00.000Z", + "user": "5dbe73ee9cf1542872c68cec", + "status": false, + "quote": { + "content": "Be as you wish to seem.", + "author": "Socrates" + }, + "__v": 0 + } + ``` + - Error : + Status Code : 500 + ``` + {"message" : "Internal Server Error"} + ``` + + Status Code : 401 + ``` + { + "message": "Unauthorized user" + } + ``` + - GET : /:id + - description : Get one of todo + - body : none + - params : ToDo _id + - Headers : JWT Token + - Response : + - Success : + Status Code : 200 + ``` + { + "_id": "5dbec97c7f8ea54b2c8d39a6", + "name": "maen dota", + "description": "maen dota teruss", + "due": "2019-04-20T00:00:00.000Z", + "user": "5dbe73ee9cf1542872c68cec", + "status": false, + "quote": "Smile, breathe and go slowly.", + "__v": 0 + } + ``` + - Error : + Status Code : 500 + ``` + {"message" : "Internal Server Error"} + ``` + + Status Code : 401 + ``` + { + "message": "Unauthorized user" + } + ``` + - PUT : /:id + - description : update data of a todo + - body : data that may want to be updated + ``` + { + name : String, + status : boolean, + description : String, + due : date + } + ``` + - params : Todo _id + - Headers : JWT Token + - Response : + - Success : + Status Code : 200 + ``` + { + "message" : "succses update all data" + } + ``` + - Error : + Status Code : 500 + ``` + {"message" : "Internal Server Error"} + ``` + + Status Code : 401 + ``` + { + "message": "Unauthorized user" + } + ``` + - PATCH : /:id + - description : update status of a todo + - body : update data todo (status) + ``` + { + status : boolean + } + ``` + - params : Todo _id + - Headers : JWT Token + - Response : + - Success : + Status Code : 200 + ``` + { + "message" : "succses update status" + } + ``` + - Error : + Status Code : 500 + ``` + {"message" : "Internal Server Error"} + ``` + + Status Code : 401 + ``` + { + "message": "Unauthorized user" + } + ``` + - GET : / + - description : get all data todos of a user + - body : none + - Headers : JWT Token + - Response : + - Success : + Status Code : 200 + ``` + [ + { + "_id": "5dbec97c7f8ea54b2c8d39a6", + "name": "maen dota", + "description": "maen dota teruss", + "due": "2019-04-20T00:00:00.000Z", + "user": "5dbe73ee9cf1542872c68cec", + "status": false, + "quote": "Smile, breathe and go slowly.", + "__v": 0 + }, + { + "_id": "5dbeccbcb7c8094ee1388ebf", + "name": "maen kartu", + "description": "maen kartu teruss", + "due": "2019-03-11T00:00:00.000Z", + "user": "5dbe73ee9cf1542872c68cec", + "status": false, + "quote": "Never apologize for showing feelings. When you do so, you apologize for the truth.", + "__v": 0 + } + ] + ``` + - Error : + Status Code : 500 + ``` + {"message" : "Internal Server Error"} + ``` + + Status Code : 401 + ``` + { + "message": "Unauthorized user" + } + ``` + + - DELETE /:id + - description : delete a todo + - body : none + - params : ToDo _id + - Headers : JWT Token + - Response : + - Success : + Status Code : 200 + ``` + { + "message" : "Todo is deleted" + } + ``` + - Error : + Status Code : 500 + ``` + {"message" : "Internal Server Error"} + ``` + + Status Code : 401 + ``` + { + "message": "Unauthorized user" + } + ``` + + - Fect 3rd APIs Quote baseURL : 'https://api.quotable.io/random' + + - Hook : pre create for quote + - todoSchema.pre('save', function (next) { + axiosInstance.get('') + .then(respone => { + // console.log(respone.data); + this.status = false; + this.quote = { + content: respone.data.content, + author: respone.data.author + } + next() + }) + .catch(err => { + next(err); + }) + }) + + +- base routes USERS url : http://localhost:3000/users + + - POST : /register + - description : create a new user + - body : + ``` + { + name : String, + email : String, + password : String + } + ``` + - Headers : none + - Response : + - Success : + Status Code : 201 + ``` + { + "_id": "5dbf3cff9fde4d02bad97caf", + "name": "karina", + "email": "karina@gmail.com", + "password": "$2a$10$rrpFvMcOj.nYGPA3EnV.puWXwa5DatjVwajcuRbpu.HPKE0NoTW/K", + "__v": 0 + } + ``` + - Error : + Status Code : 500 + ``` + {"message" : "Internal Server Error"} + ``` + + - POST : /login + - description : login to the system + - body : + ``` + { + email : String, + password : String + } + ``` + - Headers : none + - Response : + - Success : + Status Code : 200 + ``` + { + "token" : "hcsuacnsdhidzuSDHBGASVGAwdudasdsadas" + } + ``` + - Error : + Status Code : 500 + ``` + {"message" : "Internal Server Error"} + ``` + Status Code : 404 + ``` + {"message" : "Username/Password is wrong!"} + ``` + + - POST : /googleLogin + - description : login to the system using google + - body : Google OAuth Token + - Headers : none + - Response : + - Success : + Status Code : 200 + ``` + { + "token" : "hcsuacnsdhidzuSDHBGASVGAwdudsadSDSD" + } + ``` + - Error : + Status Code : 500 + ``` + {"message" : "Internal Server Error"} + ``` + + +Usage +---- + +Make sure you have node js has been installed in your computer, then run the folder server the commands bellow in your terminal. + +``` + $ npm init -y + $ npm install + $ npm run dev +``` +Make sure you have node js has been installed in your computer, then run the folder client the commands bellow in your terminal. + +``` + $ live-server --host=localhost +``` +## By : Edwin Satya Yudistira \ No newline at end of file diff --git a/client/.gitignore b/client/.gitignore new file mode 100644 index 0000000..b512c09 --- /dev/null +++ b/client/.gitignore @@ -0,0 +1 @@ +node_modules \ No newline at end of file diff --git a/client/index.html b/client/index.html new file mode 100644 index 0000000..568a512 --- /dev/null +++ b/client/index.html @@ -0,0 +1,297 @@ + + + + + + + + + + + + + + + + + + + fancyTodo + + + + + + + + + + + + +
+ +
+ + + +
+
+ +
+
+ +
+
+
+
+
+
+ + +
+
+
+ + +
+
+
+ +
+ +
+ + +
+ +
+
+ +
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/package-lock.json b/client/package-lock.json new file mode 100644 index 0000000..48e341a --- /dev/null +++ b/client/package-lock.json @@ -0,0 +1,3 @@ +{ + "lockfileVersion": 1 +} diff --git a/client/public/css/formLogin.css b/client/public/css/formLogin.css new file mode 100644 index 0000000..5cc7d65 --- /dev/null +++ b/client/public/css/formLogin.css @@ -0,0 +1,34 @@ +.login-container { + width: 30%; + margin-top: 9%; + margin-left: 35%; + box-shadow: 0 3px 20px rgba(0, 0, 0, 0.3); + padding: 40px; +} + + +#detail-login { + + width: 100%; + min-height: 100vh; +} + +.register-container { + width: 30%; + margin-top: 9%; + margin-left: 35%; + box-shadow: 0 3px 20px rgba(0, 0, 0, 0.3); + padding: 40px; +} + + +#detail-register { + + width: 100%; + min-height: 100vh; +} + +#btn-login, +#btn-register { + width: 49%; +} \ No newline at end of file diff --git a/client/public/css/style.css b/client/public/css/style.css new file mode 100644 index 0000000..b7e3de5 --- /dev/null +++ b/client/public/css/style.css @@ -0,0 +1,21 @@ +body { + font-family: 'Poppins', sans-serif; +} + +.navbar { + transition: top 0.5s ease; +} + +.navbar-hide { + top: -56px; +} + +.carousel-item { + height: 100vh; + min-height: 350px; + background: no-repeat center center scroll; + -webkit-background-size: cover; + -moz-background-size: cover; + -o-background-size: cover; + background-size: cover; +} \ No newline at end of file diff --git a/client/public/img/slide1.jpg b/client/public/img/slide1.jpg new file mode 100644 index 0000000..12489fc Binary files /dev/null and b/client/public/img/slide1.jpg differ diff --git a/client/public/img/slide2.jpg b/client/public/img/slide2.jpg new file mode 100644 index 0000000..097aea6 Binary files /dev/null and b/client/public/img/slide2.jpg differ diff --git a/client/public/img/slide3.jpg b/client/public/img/slide3.jpg new file mode 100644 index 0000000..5a89ab9 Binary files /dev/null and b/client/public/img/slide3.jpg differ diff --git a/client/public/img/slide4.jpg b/client/public/img/slide4.jpg new file mode 100644 index 0000000..8607d43 Binary files /dev/null and b/client/public/img/slide4.jpg differ diff --git a/client/public/img/slide5.jpg b/client/public/img/slide5.jpg new file mode 100644 index 0000000..d1def8e Binary files /dev/null and b/client/public/img/slide5.jpg differ diff --git a/client/public/js/main.js b/client/public/js/main.js new file mode 100644 index 0000000..e8e84ea --- /dev/null +++ b/client/public/js/main.js @@ -0,0 +1,725 @@ +$(document).ready(function () { + if (!localStorage.getItem('token')) { + $("#login-wrap").show(); + $(".navbar").hide(); + $("#slideshow").hide(); + $("#todoList").hide(); + } else { + $("#login-wrap").hide(); + $("#register-wrap").hide(); + $(".navbar").show(); + $("#slideshow").show(); + $("#todoList").show(); + } +}) + +//popup message di menu search +$(function () { + $('[data-toggle="popover"]').popover() +}) + +//nav hide +$(window).scroll(function (e) { + + // add/remove class to navbar when scrolling to hide/show + $('.navbar')[$(window).scrollTop() >= 150 ? 'addClass' : 'removeClass']('navbar-hide'); + +}); + +function manualLogin(event) { + event.preventDefault(); + const email = $('#email-login').val(); + const password = $('#pass-login').val(); + Swal.showLoading(); + $.ajax({ + url: "http://localhost:3000/users/login", + method: "POST", + data: { + email, + password + } + }) + .done(respone => { + Swal.close(); + if (!localStorage.getItem('token')) { + Swal.fire({ + type: 'success', + title: 'Login success!', + showConfirmButton: false, + timer: 1500 + }) + } + console.log('success login manual'); + $("#login-wrap").hide(); + $("#register-wrap").hide(); + $(".navbar").show(); + $("#slideshow").show(); + $("#todoList").show(); + localStorage.setItem('token', respone.token); + }) + .fail(function (err) { + Swal.close() + let msg = err.responseJSON.errors + let text = "" + msg.forEach(el => { + text += el + ', ' + }); + Swal.fire({ + type: 'error', + title: 'Oops....', + text, + }) + }) + .always(function () { + $("#email-login").val('') + $("#pass-login").val('') + }) +} + +function manualRegister(event) { + $("#login-wrap").hide() + event.preventDefault(); + Swal.showLoading() + const name = $('#name-register').val(); + const email = $('#email-register').val(); + const password = $('#pass-register').val(); + $.ajax({ + url: "http://localhost:3000/users/register", + method: "POST", + data: { + name, + email, + password + } + }) + .done(respone => { + Swal.close() + Swal.fire({ + type: 'success', + title: `Register success! + Please, login first!`, + showConfirmButton: false, + timer: 1500 + }) + console.log('success register manual'); + $("#register-wrap").hide(); + $("#login-wrap").show(); + }) + .fail(function (jqXHR, textStatus, errorThrown) { + console.log(textStatus); + let msg = err.responseJSON.errors + let text = "" + msg.forEach(el => { + text += el + ', ' + }); + Swal.fire({ + type: 'error', + title: 'Oops....', + text, + }) + }) + .always(function () { + $('#name-register').val('') + $('#email-register').val('') + $('#pass-register').val('') + }) +} + +function onSuccess(googleUser) { + const id_token = googleUser.getAuthResponse().id_token; + Swal.showLoading(); + $.ajax({ + url: "http://localhost:3000/users/googleLogin", + method: "POST", + data: { + id_token + } + }) + .done(respone => { + Swal.close(); + if (!localStorage.getItem('token')) { + Swal.fire({ + type: 'success', + title: 'Login success!', + showConfirmButton: false, + timer: 1500 + }) + } + console.log('succes googleLogin =>', respone); + $("#login-wrap").hide(); + $("#register-wrap").hide(); + $(".navbar").show(); + $("#slideshow").show(); + $("#todoList").show(); + localStorage.setItem('token', respone.token); + }) + .fail(function (jqXHR, textStatus, errorThrown) { + console.log(textStatus); + let msg = err.responseJSON.errors + let text = "" + msg.forEach(el => { + text += el + ', ' + }); + Swal.fire({ + type: 'error', + title: 'Oops....', + text, + }) + + }) + .always(function () { + console.log("complete"); + + }) +} + +function onFailure(error) { + console.log(error); +} + +// window.onbeforeunload = function (e) { +// gapi.auth2.getAuthInstance().signOut(); +// }; + +function renderButton() { + gapi.signin2.render('#my-signin2', { + 'scope': 'profile email', + 'width': 380, + 'height': 50, + 'longtitle': true, + 'theme': 'light', + 'onsuccess': onSuccess, + 'onfailure': onFailure + }); +} + +function renderRegister() { + $("#register-wrap").show(); + $("#login-wrap").hide(); +} + +function signOut() { + localStorage.removeItem('token'); + console.log('success logout'); + $(".navbar").hide(); + $("#slideshow").hide(); + $("#todoList").hide(); + $("#login-wrap").show(); + $("#listTodos").empty() + if (gapi.auth2 !== undefined) { + var auth2 = gapi.auth2.getAuthInstance(); + auth2.signOut().then(function () { + console.log('User via google signed out.'); + }); + } +} + +//add new todo +$("#btn-create").click('submit', function (event) { + // event.preventDefault(); + console.log('masuk woi') + let name = $("#inputName").val(); + let description = $("#inputDescription").val(); + let due = $("#inputDueDate").val(); + $.ajax({ + url: 'http://localhost:3000/todos/', + method: "POST", + headers: { + token: localStorage.getItem('token') + }, + data: { + name, + description, + due + } + }) + .done(respone => { + Swal.fire({ + type: 'success', + title: `success add new Todo!`, + showConfirmButton: false, + timer: 1500 + }) + // todoList() + }) + .fail(err => { + let msg = err.responseJSON.errors + let text = "" + msg.forEach(el => { + text += el + ', ' + }); + Swal.fire({ + type: 'error', + title: 'Oops....', + text, + }) + }) + .always(function () { + $('#inputName').val('') + $('#inputDescription').val('') + $('#inputDueDate').val('') + }) +}) + +//dashboard +function todoList() { + Swal.showLoading(); + $("#listTodos").empty() + $.ajax({ + url: 'http://localhost:3000/todos/', + method: "GET", + headers: { + token: localStorage.getItem('token') + } + }) + .done(respone => { + if (respone.length == 0) { + Swal.close() + Swal.fire({ + type: 'error', + title: 'Oops....', + text: 'no have todo list' + }) + } else { + respone.forEach(e => { + Swal.close() + let date = new Date(e.due) + let resultDate = `${date.getDate()} - ${date.getMonth()} - ${date.getFullYear()}`; + if (!e.description) e.description = " " + if (e.status) { + $("#listTodos").append(` +
+
+
+
+
+
${e.name}
+

${e.description}

+
+
    +
  • Due date : ${resultDate}
  • +
  • Status : Done
  • +
  • +
+
+ Edit + Delete +
+
+
+
+
+ Quote +
+
+
+

${e.quote.content}.

+
Someone famous in ${e.quote.author}
+
+
+
+
+
+ `) + } else { + $("#listTodos").append(` +
+
+
+
+
+
${e.name}
+

${e.description}

+
+
    +
  • Due date : ${resultDate}
  • +
  • Status : UnDone
  • +
  • +
+
+ Edit + Delete +
+
+
+
+
+ Quote +
+
+
+

${e.quote.content}.

+
Someone famous : ${e.quote.author}
+
+
+
+
+
+ `) + } + }) + } + }) + .fail(function (err) { + Swal.close() + let msg = err.responseJSON.errors + let text = "" + msg.forEach(el => { + text += el + ', ' + }); + Swal.fire({ + type: 'error', + title: 'Oops....', + text, + }) + }) + +} + +function deleteone(id) { + Swal.fire({ + title: "Are you sure want to delete this todo?", + type: "question", + showConfirmButton: true, + showCancelButton: true, + }) + .then(isConfirm => { + if (isConfirm) { + $.ajax({ + url: `http://localhost:3000/todos/${id}`, + method: "DELETE", + headers: { + token: localStorage.getItem('token') + } + }) + .done(respone => { + Swal.fire({ + type: 'success', + title: 'ToDo has been deleted!', + showConfirmButton: false, + timer: 1500 + }) + todoList(); + }) + .fail(function (jqXHR, textStatus, errorThrown) { + console.log(textStatus); + let msg = err.responseJSON.errors + let text = "" + msg.forEach(el => { + text += el + ', ' + }); + Swal.fire({ + type: 'error', + title: 'Oops....', + text, + }) + }) + } + }) +} + +function markdone(id) { + Swal.fire({ + title: "Do You Want to Change This Todo Status to Done?", + type: "question", + showConfirmButton: true, + showCancelButton: true, + }) + .then(isConfirm => { + if (isConfirm) { + $.ajax({ + url: `http://localhost:3000/todos/${id}`, + method: "PATCH", + headers: { + token: localStorage.getItem('token') + }, + data: { + status: true + } + }) + .done(respone => { + Swal.fire({ + type: 'success', + title: 'Todo mark now is done!', + showConfirmButton: false, + timer: 1500 + }) + todoList(); + }) + .fail(function (jqXHR, textStatus, errorThrown) { + console.log(textStatus); + let msg = err.responseJSON.errors + let text = "" + msg.forEach(el => { + text += el + ', ' + }); + Swal.fire({ + type: 'error', + title: 'Oops....', + text, + }) + }) + } + }) +} + +function markundone(id) { + console.log('masuukkk markun') + Swal.fire({ + title: "Do You Want to Change This Todo Status to UnDone?", + type: "question", + showConfirmButton: true, + showCancelButton: true, + }) + .then(isConfirm => { + if (isConfirm) { + $.ajax({ + url: `http://localhost:3000/todos/${id}`, + method: "PATCH", + headers: { + token: localStorage.getItem('token') + }, + data: { + status: false + } + }) + .done(respone => { + Swal.fire({ + type: 'success', + title: 'Todo mark now is undone!', + showConfirmButton: false, + timer: 1500 + }) + todoList(); + }) + .fail(function (jqXHR, textStatus, errorThrown) { + console.log(textStatus); + let msg = err.responseJSON.errors + let text = "" + msg.forEach(el => { + text += el + ', ' + }); + Swal.fire({ + type: 'error', + title: 'Oops....', + text, + }) + }) + } + }) +} + +function edittodo(id) { + $.ajax({ + url: `http://localhost:3000/todos/${id}`, + method: "GET", + headers: { + token: localStorage.getItem('token') + } + }) + .done(respone => { + $("#detail-update").append(` + + `) + //update + $("#btn-update").click('submit', function (event) { + // event.preventDefault(); + console.log('masukkkkkkk') + let name = $("#updateName").val(); + let description = $("#updateDescription").val(); + let status = $("#updateStatus").val(); + let due = $("#updateDueDate").val(); + $.ajax({ + url: `http://localhost:3000/todos/${id}`, + method: "PUT", + headers: { + token: localStorage.getItem('token') + }, + data: { + name, + description, + status, + due + } + }) + .done(respone => { + Swal.fire({ + type: 'success', + title: `success update data Todo!`, + showConfirmButton: false, + timer: 1500 + }) + todoList() + }) + .fail(err => { + let msg = err.responseJSON.errors + let text = "" + msg.forEach(el => { + text += el + ', ' + }); + Swal.fire({ + type: 'error', + title: 'Oops....', + text, + }) + }) + .always(function () { + // $("#updateName").val(''); + // $("#updateDescription").val(''); + // $("#updateStatus").val(''); + // $("#updateDueDate").val(''); + }) + }) + }) + .fail(function (jqXHR, textStatus, errorThrown) { + console.log(textStatus); + let msg = err.responseJSON.errors + let text = "" + msg.forEach(el => { + text += el + ', ' + }); + Swal.fire({ + type: 'error', + title: 'Oops....', + text, + }) + }) +} + +function searchTodo(value) { + console.log('masuk search') + let todosSearch = []; + let search = new RegExp(value, 'i') + let result; + $("#listTodos").empty() + $.ajax({ + url: 'http://localhost:3000/todos/', + method: "GET", + headers: { + token: localStorage.getItem('token') + } + }) + .done(respone => { + respone.forEach(e => { + let todoName = e.name + if (result = search.exec(todoName) !== null) { + todosSearch.push(e) + } + }) + + todosSearch.forEach(e => { + let date = new Date(e.due) + let resultDate = `${date.getDate()} - ${date.getMonth()} - ${date.getFullYear()}`; + if (!e.description) e.description = " " + if (e.status) { + $("#listTodos").append(` +
+
+
+
+
+
${e.name}
+

${e.description}

+
+
    +
  • Due date : ${resultDate}
  • +
  • Status : Done
  • +
  • +
+
+ Edit + Delete +
+
+
+
+
+ Quote +
+
+
+

${e.quote.content}.

+
Someone famous in ${e.quote.author}
+
+
+
+
+
+ `) + } else { + $("#listTodos").append(` +
+
+
+
+
+
${e.name}
+

${e.description}

+
+
    +
  • Due date : ${resultDate}
  • +
  • Status : UnDone
  • +
  • +
+
+ Edit + Delete +
+
+
+
+
+ Quote +
+
+
+

${e.quote.content}.

+
Someone famous : ${e.quote.author}
+
+
+
+
+
+ `) + } + }) + }) +} \ No newline at end of file diff --git a/server/.gitignore b/server/.gitignore new file mode 100644 index 0000000..1dcef2d --- /dev/null +++ b/server/.gitignore @@ -0,0 +1,2 @@ +node_modules +.env \ No newline at end of file diff --git a/server/api/quote.js b/server/api/quote.js new file mode 100644 index 0000000..fe596cc --- /dev/null +++ b/server/api/quote.js @@ -0,0 +1,7 @@ +const axios = require('axios'); + +const instance = axios.create({ + baseURL: 'https://api.quotable.io/random' +}) + +module.exports = instance; \ No newline at end of file diff --git a/server/app.js b/server/app.js new file mode 100644 index 0000000..3f01a64 --- /dev/null +++ b/server/app.js @@ -0,0 +1,36 @@ +if (process.env.NODE_ENV === 'development') { + require('dotenv').config(); +} + +const express = require('express'); +const app = express(); +const mongoose = require('mongoose'); +const cors = require('cors'); +const morgan = require('morgan'); +const routes = require('./routes'); +const errorHandler = require('./middleware/errorHandling'); +const PORT = process.env.PORT || 3000; + +//connection mongoose +mongoose.connect('mongodb://localhost:27017/fancyTodo', { + useNewUrlParser: true, + useUnifiedTopology: true +}, (err) => { + if (err) console.log('Failed To Connect DB'); + else console.log('Connected to DB'); +}); + +app.use(cors()); +app.use(morgan('dev')); +app.use(express.urlencoded({ + extended: false +})); +app.use(express.json()); +app.use('/', routes); +app.use(errorHandler); + +app.listen(PORT, () => { + console.log(`Listen to Port : ${PORT}`); +}) + +module.exports = app; \ No newline at end of file diff --git a/server/controllers/todoController.js b/server/controllers/todoController.js new file mode 100644 index 0000000..cf0a659 --- /dev/null +++ b/server/controllers/todoController.js @@ -0,0 +1,96 @@ +const Todo = require('../models/todo'); + +class TodoController { + static create(req, res, next) { + console.log('masukk create', req.decoded) + let { + name, + description, + due + } = req.body + Todo.create({ + name, + description, + due, + user: req.decoded.id + }) + // }) + .then(todo => { + res.status(201).json(todo) + }) + .catch(next) + } + + static read(req, res, next) { + Todo.find({ + user: req.decoded.id + }) + .then(todos => { + res.status(200).json(todos) + }) + .catch(next) + } + + static delete(req, res, next) { + Todo.deleteOne({ + _id: req.params.id + }) + .then(() => { + res.status(200).json({ + message: 'todo is deleted' + }) + }) + .catch(next) + } + + static updateMark(req, res, next) { + let update = { + status: req.body.status + } + Todo.where({ + _id: req.params.id + }).update(update) + .then(data => { + res.status(200).json({ + message: "success update status" + }) + }) + .catch(next) + } + + static updateData(req, res, next) { + let { + name, + description, + status, + due + } = req.body + let update = { + name, + description, + status, + due + } + Todo.where({ + _id: req.params.id + }).update(update) + .then(data => { + res.status(200).json({ + message: "succses update all data" + }) + }) + .catch(next) + } + + static findOne(req, res, next) { + Todo.findById({ + _id: req.params.id + }) + .then(data => { + res.status(200).json(data) + }) + .catch(next) + } +} + +module.exports = TodoController; \ No newline at end of file diff --git a/server/controllers/userController.js b/server/controllers/userController.js new file mode 100644 index 0000000..b93b8f1 --- /dev/null +++ b/server/controllers/userController.js @@ -0,0 +1,94 @@ +const User = require('../models/user'); +const { + hashPassword, + comparePassword +} = require('../helpers/bcrypt'); +const jwt = require('../helpers/jwt'); +class UserController { + static register(req, res, next) { + // console.log('masuk'); + let { + name, + email, + password + } = req.body; + password = hashPassword(password); + User.create({ + name, + email, + password + }) + .then(user => { + res.status(201).json(user); + }) + .catch(next) + } + + static login(req, res, next) { + // console.log('masuk') + let { + email, + password + } = req.body; + User.findOne({ + email + }) + .then(user => { + if (!user) { + next({ + message: 'Username/Password is wrong!' + }) + } else { + if (!comparePassword(password, user.password)) { + next({ + message: 'Username/Password is wrong!' + }); + } else { + const payloadjwt = { + id: user._id, + email: user._email + } + let token = jwt.getToken(payloadjwt); + res.status(200).json({ + token + }) + } + } + }) + .catch(next); + } + + static googleLogin(req, res, next) { + // console.log('masuk') + // console.log(req.decoded); + User.findOne({ + email: req.decoded.email + }) + .then(user => { + if (user) { + console.log('user sudah ada'); + return user; + } else { + console.log('user belum ada'); + return User.create({ + name: req.decoded.given_name, + email: req.decoded.email, + password: hashPassword(process.env.DEFAULT_PASSWORD) + }) + } + }) + .then(user => { + const payloadjwt = { + id: user._id, + email: user.email + } + let token = jwt.getToken(payloadjwt); + res.status(201).json({ + token + }) + }) + .catch(next); + } +}; + +module.exports = UserController; \ No newline at end of file diff --git a/server/helpers/bcrypt.js b/server/helpers/bcrypt.js new file mode 100644 index 0000000..d06f77e --- /dev/null +++ b/server/helpers/bcrypt.js @@ -0,0 +1,16 @@ +const bcrypt = require('bcryptjs'); + +function hashPassword(password) { + const salt = bcrypt.genSaltSync(10); + const hash = bcrypt.hashSync(password, salt); + return hash +} + +function comparePassword(password, hash) { + return bcrypt.compareSync(password, hash); +} + +module.exports = { + hashPassword, + comparePassword +} \ No newline at end of file diff --git a/server/helpers/jwt.js b/server/helpers/jwt.js new file mode 100644 index 0000000..7759998 --- /dev/null +++ b/server/helpers/jwt.js @@ -0,0 +1,14 @@ +const jwt = require('jsonwebtoken'); + +function getToken(data) { + return jwt.sign(data, process.env.JWT_SECRET); +} + +function verifyToken(token) { + return jwt.verify(token, process.env.JWT_SECRET); +} + +module.exports = { + getToken, + verifyToken +} \ No newline at end of file diff --git a/server/middleware/auth.js b/server/middleware/auth.js new file mode 100644 index 0000000..7589756 --- /dev/null +++ b/server/middleware/auth.js @@ -0,0 +1,37 @@ +const jwt = require('../helpers/jwt'); +const Todo = require('../models/todo'); + +function authentication(req, res, next) { + try { + let decoded = jwt.verifyToken(req.headers.token); + req.decoded = decoded; + next() + } catch (err) { + next(err); + } +}; + +function authorization(req, res, next) { + Todo.findById(req.params.id) + .then(todo => { + if (todo) { + if (todo.user == req.decoded.id) { + next() + } else { + res.status(401).json({ + message: 'Unauthorized user' + }) + } + } else { + res.status(404).json({ + message: 'todo is not found!' + }) + } + }) + .catch(next); +}; + +module.exports = { + authentication, + authorization +} \ No newline at end of file diff --git a/server/middleware/errorHandling.js b/server/middleware/errorHandling.js new file mode 100644 index 0000000..f3eebdb --- /dev/null +++ b/server/middleware/errorHandling.js @@ -0,0 +1,27 @@ +module.exports = (err, req, res, next) => { + let status; + let message; + let errors = []; + console.log(err); + + if (err.name === 'JsonWebTokenError' || err.name === 'TokenExpiredError') { + status = 401 + message = 'You should log in first!' + + errors = [message] + } else if (err.name === 'ValidationError') { + status = 400 + + for (let key in err.errors) { + errors.push(err.errors[key].message) + } + } else { + status = err.status || 500 + message = err.message || `Internal server error` + + errors = [message] + } + res.status(status).json({ + errors + }) +} \ No newline at end of file diff --git a/server/middleware/googleVerify.js b/server/middleware/googleVerify.js new file mode 100644 index 0000000..d019a5d --- /dev/null +++ b/server/middleware/googleVerify.js @@ -0,0 +1,24 @@ +const { + OAuth2Client +} = require('google-auth-library'); +const client = new OAuth2Client(process.env.CLIENT_ID); + +function verify(req, res, next) { + client.verifyIdToken({ + idToken: req.body.id_token, + audience: process.env.CLIENT_ID + }) + .then(ticket => { + const payload = ticket.getPayload(); + req.decoded = payload; + // console.log(req.decoded) + next() + }) + .catch(err => { + res.status(500).json({ + err + }) + }) +} + +module.exports = verify; \ No newline at end of file diff --git a/server/models/todo.js b/server/models/todo.js new file mode 100644 index 0000000..539e95b --- /dev/null +++ b/server/models/todo.js @@ -0,0 +1,48 @@ +const mongoose = require('mongoose'); +const Schema = mongoose.Schema; +const model = mongoose.model; +const ObjectId = Schema.Types.ObjectId; +const axiosInstance = require('../api/quote'); + +const todoSchema = new Schema({ + name: { + type: String, + required: true + }, + description: { + type: String, + required: true + }, + status: { + type: Boolean + }, + due: { + type: Date + }, + quote: { + type: {} + }, + user: { + type: ObjectId, + ref: 'User' + } +}) + +todoSchema.pre('save', function (next) { + axiosInstance.get('') + .then(respone => { + // console.log(respone.data); + this.status = false; + this.quote = { + content: respone.data.content, + author: respone.data.author + } + next() + }) + .catch(err => { + next(err); + }) +}) + +const Todo = model('Todo', todoSchema); +module.exports = Todo; \ No newline at end of file diff --git a/server/models/user.js b/server/models/user.js new file mode 100644 index 0000000..f0ef7a2 --- /dev/null +++ b/server/models/user.js @@ -0,0 +1,23 @@ +const mongoose = require('mongoose'); +const Schema = mongoose.Schema; +const model = mongoose.model; + +const userSchema = new Schema({ + name: { + type: String, + required: true + }, + email: { + type: String, + required: true, + unique: true + }, + password: { + type: String, + required: true + } +}) + +const User = model('User', userSchema); + +module.exports = User; \ No newline at end of file diff --git a/server/package-lock.json b/server/package-lock.json new file mode 100644 index 0000000..cf97178 --- /dev/null +++ b/server/package-lock.json @@ -0,0 +1,869 @@ +{ + "name": "server", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "requires": { + "event-target-shim": "^5.0.0" + } + }, + "accepts": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "requires": { + "mime-types": "~2.1.24", + "negotiator": "0.6.2" + } + }, + "agent-base": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", + "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", + "requires": { + "es6-promisify": "^5.0.0" + } + }, + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + }, + "arrify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz", + "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==" + }, + "axios": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.19.0.tgz", + "integrity": "sha512-1uvKqKQta3KBxIz14F2v06AEHZ/dIoeKfbTRkK1E5oqjDnuEerLmYTgJB5AiQZHJcljpg1TuRzdjDR06qNk0DQ==", + "requires": { + "follow-redirects": "1.5.10", + "is-buffer": "^2.0.2" + } + }, + "base64-js": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", + "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==" + }, + "basic-auth": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", + "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", + "requires": { + "safe-buffer": "5.1.2" + } + }, + "bcryptjs": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz", + "integrity": "sha1-mrVie5PmBiH/fNrF2pczAn3x0Ms=" + }, + "bignumber.js": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-7.2.1.tgz", + "integrity": "sha512-S4XzBk5sMB+Rcb/LNcpzXr57VRTxgAvaAEDAl1AwRx27j00hT84O6OkteE7u8UB3NuaaygCRrEpqox4uDOrbdQ==" + }, + "bluebird": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz", + "integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA==" + }, + "body-parser": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "requires": { + "bytes": "3.1.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.7.0", + "raw-body": "2.4.0", + "type-is": "~1.6.17" + } + }, + "bson": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/bson/-/bson-1.1.1.tgz", + "integrity": "sha512-jCGVYLoYMHDkOsbwJZBCqwMHyH4c+wzgI9hG7Z6SZJRXWr+x58pdIbm2i9a/jFGCkRJqRUr8eoI7lDWa0hTkxg==" + }, + "buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=" + }, + "bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" + }, + "content-disposition": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", + "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", + "requires": { + "safe-buffer": "5.1.2" + } + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" + }, + "cookie": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", + "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + }, + "cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "requires": { + "object-assign": "^4", + "vary": "^1" + } + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "dotenv": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz", + "integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==", + "dev": true + }, + "ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" + }, + "es6-promise": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", + "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==" + }, + "es6-promisify": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", + "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", + "requires": { + "es6-promise": "^4.0.3" + } + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" + }, + "event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==" + }, + "express": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", + "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", + "requires": { + "accepts": "~1.3.7", + "array-flatten": "1.1.1", + "body-parser": "1.19.0", + "content-disposition": "0.5.3", + "content-type": "~1.0.4", + "cookie": "0.4.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.1.2", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.5", + "qs": "6.7.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.1.2", + "send": "0.17.1", + "serve-static": "1.14.1", + "setprototypeof": "1.1.1", + "statuses": "~1.5.0", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + } + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "fast-text-encoding": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fast-text-encoding/-/fast-text-encoding-1.0.0.tgz", + "integrity": "sha512-R9bHCvweUxxwkDwhjav5vxpFvdPGlVngtqmx4pIZfSUhM/Q4NiIUHB456BAf+Q1Nwu3HEZYONtu+Rya+af4jiQ==" + }, + "finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + } + }, + "follow-redirects": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz", + "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==", + "requires": { + "debug": "=3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "requires": { + "ms": "2.0.0" + } + } + } + }, + "forwarded": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" + }, + "gaxios": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-2.1.0.tgz", + "integrity": "sha512-Gtpb5sdQmb82sgVkT2GnS2n+Kx4dlFwbeMYcDlD395aEvsLCSQXJJcHt7oJ2LrGxDEAeiOkK79Zv2A8Pzt6CFg==", + "requires": { + "abort-controller": "^3.0.0", + "extend": "^3.0.2", + "https-proxy-agent": "^3.0.0", + "is-stream": "^2.0.0", + "node-fetch": "^2.3.0" + } + }, + "gcp-metadata": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-3.2.0.tgz", + "integrity": "sha512-ympv+yQ6k5QuWCuwQqnGEvFGS7MBKdcQdj1i188v3bW9QLFIchTGaBCEZxSQapT0jffdn1vdt8oJhB5VBWQO1Q==", + "requires": { + "gaxios": "^2.0.1", + "json-bigint": "^0.3.0" + } + }, + "google-auth-library": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-5.5.1.tgz", + "integrity": "sha512-zCtjQccWS/EHYyFdXRbfeSGM/gW+d7uMAcVnvXRnjBXON5ijo6s0nsObP0ifqileIDSbZjTlLtgo+UoN8IFJcg==", + "requires": { + "arrify": "^2.0.0", + "base64-js": "^1.3.0", + "fast-text-encoding": "^1.0.0", + "gaxios": "^2.1.0", + "gcp-metadata": "^3.2.0", + "gtoken": "^4.1.0", + "jws": "^3.1.5", + "lru-cache": "^5.0.0" + } + }, + "google-p12-pem": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-2.0.2.tgz", + "integrity": "sha512-UfnEARfJKI6pbmC1hfFFm+UAcZxeIwTiEcHfqKe/drMsXD/ilnVjF7zgOGpHXyhuvX6jNJK3S8A0hOQjwtFxEw==", + "requires": { + "node-forge": "^0.9.0" + } + }, + "gtoken": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-4.1.0.tgz", + "integrity": "sha512-wqyn2gf5buzEZN4QNmmiiW2i2JkEdZnL7Z/9p44RtZqgt4077m4khRgAYNuu8cBwHWCc6MsP6eDUn/KkF6jFIw==", + "requires": { + "gaxios": "^2.0.0", + "google-p12-pem": "^2.0.0", + "jws": "^3.1.5", + "mime": "^2.2.0" + }, + "dependencies": { + "mime": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz", + "integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA==" + } + } + }, + "http-errors": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", + "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + } + }, + "https-proxy-agent": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-3.0.1.tgz", + "integrity": "sha512-+ML2Rbh6DAuee7d07tYGEKOEi2voWPUGan+ExdPbPW6Z3svq+JCqr0v8WmKPOkz1vOVykPCBSuobe7G8GJUtVg==", + "requires": { + "agent-base": "^4.3.0", + "debug": "^3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "ipaddr.js": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.0.tgz", + "integrity": "sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA==" + }, + "is-buffer": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz", + "integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A==" + }, + "is-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", + "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==" + }, + "json-bigint": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-0.3.0.tgz", + "integrity": "sha1-DM2RLEuCcNBfBW+9E4FLU9OCWx4=", + "requires": { + "bignumber.js": "^7.0.0" + } + }, + "jsonwebtoken": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz", + "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==", + "requires": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^5.6.0" + }, + "dependencies": { + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "requires": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "requires": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, + "kareem": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.3.1.tgz", + "integrity": "sha512-l3hLhffs9zqoDe8zjmb/mAN4B8VT3L56EUvKNqLFVs9YlFA+zx7ke1DO8STAdDyYNkeSo1nKmjuvQeI12So8Xw==" + }, + "lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8=" + }, + "lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY=" + }, + "lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha1-YZwK89A/iwTDH1iChAt3sRzWg0M=" + }, + "lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w=" + }, + "lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=" + }, + "lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=" + }, + "lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=" + }, + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "requires": { + "yallist": "^3.0.2" + } + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" + }, + "memory-pager": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", + "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==", + "optional": true + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" + }, + "mime-db": { + "version": "1.40.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", + "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==" + }, + "mime-types": { + "version": "2.1.24", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", + "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", + "requires": { + "mime-db": "1.40.0" + } + }, + "mongodb": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.3.3.tgz", + "integrity": "sha512-MdRnoOjstmnrKJsK8PY0PjP6fyF/SBS4R8coxmhsfEU7tQ46/J6j+aSHF2n4c2/H8B+Hc/Klbfp8vggZfI0mmA==", + "requires": { + "bson": "^1.1.1", + "require_optional": "^1.0.1", + "safe-buffer": "^5.1.2", + "saslprep": "^1.0.0" + } + }, + "mongoose": { + "version": "5.7.7", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-5.7.7.tgz", + "integrity": "sha512-FU59waB4LKBa9KOnqBUcCcMIVRc09TFo1F8nMxrzSiIWATaJpjxxSSH5FBVUDxQfNdJLfg9uFHxaTxhhwjsZOQ==", + "requires": { + "bson": "~1.1.1", + "kareem": "2.3.1", + "mongodb": "3.3.3", + "mongoose-legacy-pluralize": "1.0.2", + "mpath": "0.6.0", + "mquery": "3.2.2", + "ms": "2.1.2", + "regexp-clone": "1.0.0", + "safe-buffer": "5.1.2", + "sift": "7.0.1", + "sliced": "1.0.1" + }, + "dependencies": { + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "mongoose-legacy-pluralize": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/mongoose-legacy-pluralize/-/mongoose-legacy-pluralize-1.0.2.tgz", + "integrity": "sha512-Yo/7qQU4/EyIS8YDFSeenIvXxZN+ld7YdV9LqFVQJzTLye8unujAWPZ4NWKfFA+RNjh+wvTWKY9Z3E5XM6ZZiQ==" + }, + "morgan": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.9.1.tgz", + "integrity": "sha512-HQStPIV4y3afTiCYVxirakhlCfGkI161c76kKFca7Fk1JusM//Qeo1ej2XaMniiNeaZklMVrh3vTtIzpzwbpmA==", + "requires": { + "basic-auth": "~2.0.0", + "debug": "2.6.9", + "depd": "~1.1.2", + "on-finished": "~2.3.0", + "on-headers": "~1.0.1" + } + }, + "mpath": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.6.0.tgz", + "integrity": "sha512-i75qh79MJ5Xo/sbhxrDrPSEG0H/mr1kcZXJ8dH6URU5jD/knFxCVqVC/gVSW7GIXL/9hHWlT9haLbCXWOll3qw==" + }, + "mquery": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/mquery/-/mquery-3.2.2.tgz", + "integrity": "sha512-XB52992COp0KP230I3qloVUbkLUxJIu328HBP2t2EsxSFtf4W1HPSOBWOXf1bqxK4Xbb66lfMJ+Bpfd9/yZE1Q==", + "requires": { + "bluebird": "3.5.1", + "debug": "3.1.0", + "regexp-clone": "^1.0.0", + "safe-buffer": "5.1.2", + "sliced": "1.0.1" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "requires": { + "ms": "2.0.0" + } + } + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "negotiator": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" + }, + "node-fetch": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz", + "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==" + }, + "node-forge": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.9.1.tgz", + "integrity": "sha512-G6RlQt5Sb4GMBzXvhfkeFmbqR6MzhtnT7VTHuLadjkii3rdYHNdw0m8zA4BTxVIh68FicCQ2NSUANpsqkr9jvQ==" + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "requires": { + "ee-first": "1.1.1" + } + }, + "on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==" + }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + }, + "proxy-addr": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.5.tgz", + "integrity": "sha512-t/7RxHXPH6cJtP0pRG6smSr9QJidhB+3kXu0KgXnbGYMgzEnUxRQ4/LDdfOwZEMyIh3/xHb8PX3t+lfL9z+YVQ==", + "requires": { + "forwarded": "~0.1.2", + "ipaddr.js": "1.9.0" + } + }, + "qs": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" + }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" + }, + "raw-body": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", + "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", + "requires": { + "bytes": "3.1.0", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + } + }, + "regexp-clone": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/regexp-clone/-/regexp-clone-1.0.0.tgz", + "integrity": "sha512-TuAasHQNamyyJ2hb97IuBEif4qBHGjPHBS64sZwytpLEqtBQ1gPJTnOaQ6qmpET16cK14kkjbazl6+p0RRv0yw==" + }, + "require_optional": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require_optional/-/require_optional-1.0.1.tgz", + "integrity": "sha512-qhM/y57enGWHAe3v/NcwML6a3/vfESLe/sGM2dII+gEO0BpKRUkWZow/tyloNqJyN6kXSl3RyyM8Ll5D/sJP8g==", + "requires": { + "resolve-from": "^2.0.0", + "semver": "^5.1.0" + } + }, + "resolve-from": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz", + "integrity": "sha1-lICrIOlP+h2egKgEx+oUdhGWa1c=" + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "saslprep": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/saslprep/-/saslprep-1.0.3.tgz", + "integrity": "sha512-/MY/PEMbk2SuY5sScONwhUDsV2p77Znkb/q3nSVstq/yQzYJOH/Azh29p9oJLsl3LnQwSvZDKagDGBsBwSooag==", + "optional": true, + "requires": { + "sparse-bitfield": "^3.0.3" + } + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + }, + "send": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", + "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", + "requires": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.7.2", + "mime": "1.6.0", + "ms": "2.1.1", + "on-finished": "~2.3.0", + "range-parser": "~1.2.1", + "statuses": "~1.5.0" + }, + "dependencies": { + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + } + } + }, + "serve-static": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", + "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.17.1" + } + }, + "setprototypeof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" + }, + "sift": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/sift/-/sift-7.0.1.tgz", + "integrity": "sha512-oqD7PMJ+uO6jV9EQCl0LrRw1OwsiPsiFQR5AR30heR+4Dl7jBBbDLnNvWiak20tzZlSE1H7RB30SX/1j/YYT7g==" + }, + "sliced": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sliced/-/sliced-1.0.1.tgz", + "integrity": "sha1-CzpmK10Ewxd7GSa+qCsD+Dei70E=" + }, + "sparse-bitfield": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", + "integrity": "sha1-/0rm5oZWBWuks+eSqzM004JzyhE=", + "optional": true, + "requires": { + "memory-pager": "^1.0.2" + } + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" + }, + "toidentifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" + }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + }, + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" + } + } +} diff --git a/server/package.json b/server/package.json new file mode 100644 index 0000000..7cb5ce1 --- /dev/null +++ b/server/package.json @@ -0,0 +1,26 @@ +{ + "name": "server", + "version": "1.0.0", + "description": "Fancy-Todo", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "dev": "NODE_ENV=development nodemon app.js" + }, + "keywords": [], + "author": "Edwin Satya Yudistira", + "license": "ISC", + "dependencies": { + "axios": "^0.19.0", + "bcryptjs": "^2.4.3", + "cors": "^2.8.5", + "express": "^4.17.1", + "google-auth-library": "^5.5.1", + "jsonwebtoken": "^8.5.1", + "mongoose": "^5.7.7", + "morgan": "^1.9.1" + }, + "devDependencies": { + "dotenv": "^8.2.0" + } +} diff --git a/server/routes/index.js b/server/routes/index.js new file mode 100644 index 0000000..0fb8a6e --- /dev/null +++ b/server/routes/index.js @@ -0,0 +1,8 @@ +const router = require('express').Router(); +const userRouter = require('./user'); +const todoRouter = require('./todo'); + +router.use('/users', userRouter); +router.use('/todos', todoRouter); + +module.exports = router; \ No newline at end of file diff --git a/server/routes/todo.js b/server/routes/todo.js new file mode 100644 index 0000000..8b7aeab --- /dev/null +++ b/server/routes/todo.js @@ -0,0 +1,16 @@ +const router = require('express').Router(); +const Todo = require('../controllers/todoController'); +const { + authentication, + authorization +} = require('../middleware/auth'); + +router.use(authentication); +router.post('/', Todo.create); +router.get('/', Todo.read); +//need author +router.delete('/:id', authorization, Todo.delete); +router.patch('/:id', authorization, Todo.updateMark); +router.put('/:id', authorization, Todo.updateData); +router.get('/:id', authorization, Todo.findOne); +module.exports = router; \ No newline at end of file diff --git a/server/routes/user.js b/server/routes/user.js new file mode 100644 index 0000000..d09805b --- /dev/null +++ b/server/routes/user.js @@ -0,0 +1,9 @@ +const router = require('express').Router(); +const User = require('../controllers/userController'); +const verify = require('../middleware/googleVerify'); + +router.post('/register', User.register); +router.post('/login', User.login); +router.post('/googleLogin', verify, User.googleLogin); + +module.exports = router \ No newline at end of file