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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Or Login Via Google
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ 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
+
+
+
+
+
+
+
+
+
+ ${e.quote.content}.
+
+
+
+
+
+
+ `)
+ } else {
+ $("#listTodos").append(`
+
+
+
+
+
+
${e.name}
+
${e.description}
+
+
+ - Due date : ${resultDate}
+ - Status : UnDone
+
+
+
+
+
+
+
+
+
+ ${e.quote.content}.
+
+
+
+
+
+
+ `)
+ }
+ })
+ }
+ })
+ .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
+
+
+
+
+
+
+
+
+
+ ${e.quote.content}.
+
+
+
+
+
+
+ `)
+ } else {
+ $("#listTodos").append(`
+
+
+
+
+
+
${e.name}
+
${e.description}
+
+
+ - Due date : ${resultDate}
+ - Status : UnDone
+
+
+
+
+
+
+
+
+
+ ${e.quote.content}.
+
+
+
+
+
+
+ `)
+ }
+ })
+ })
+}
\ 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