diff --git a/README.md b/README.md
index b83fed8..6fb762e 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,700 @@
-# FancyTodo
\ No newline at end of file
+# FancyTodo
+
+* Web:
+ https://fancy-to-do-5ee22.web.app/
+----
+List of available endpoints:
+* POST /register
+* POST /login
+* GET /todos
+* POST /todos
+* GET /todos/:id
+* PUT /todos/:id
+* PATCH /todos/:id
+* DELETE /todos/:id
+* GET /weathes
+
+----
+**REGISTER**
+----
+ Register to the app
+
+* **URL**
+
+ /users/register
+
+* **Method:**
+
+ `POST`
+
+* **Data Params**
+
+ ```javascript
+ {
+ email: "string",
+ password: "string"
+ }
+ ```
+
+* **Success Response:**
+
+ * **Code:** 201
+ **Content:**
+ ```javascript
+ {
+ id: "integer",
+ email: "string"
+ }
+ ```
+
+* **Error Response:**
+ * **Code:** 400
+ **Content:**
+ ```javascript
+ {
+ msg: "errors"
+ }
+ ```
+
+ OR
+
+ * **Code:** 500
+ **Content:**
+ ```javascript
+ {
+ msg: "internal server error"
+ }
+ ```
+* **Sample Call**
+```javascript
+ $.ajax({
+ method: 'POST',
+ url: `${SERVER}/users/register`,
+ data: {
+ email,
+ password
+ }
+ })
+```
+----
+
+**LOGIN**
+----
+ Log into the app
+
+* **URL**
+
+ /users/login
+
+* **Method:**
+
+ `POST`
+
+* **Data Params**
+
+ ```javascript
+ {
+ email: "string",
+ password: "string"
+ }
+ ```
+
+* **Success Response:**
+
+ * **Code:** 200
+ **Content:**
+ ```javascript
+ {
+ access_token: "string"
+ }
+ ```
+
+* **Error Response:**
+ * **Code:** 401
+ **Content:**
+ ```javascript
+ {
+ msg: "errors"
+ }
+ ```
+
+ OR
+
+ * **Code:** 500
+ **Content:**
+ ```javascript
+ {
+ msg: "internal server error"
+ }
+ ```
+* **Sample Call**
+```javascript
+ $.ajax({
+ method: 'POST',
+ url: `${SERVER}/users/login`,
+ data: {
+ email,
+ password
+ }
+ })
+```
+----
+
+**CREATE TODO**
+----
+ Returns JSON data from new todo
+
+* **URL**
+
+ /todos
+
+* **Method:**
+
+ `POST`
+
+* **Data Params**
+ * **Data**
+ ```javascript
+ {
+ title: "string",
+ description: "string",
+ due_date: date
+ }
+ ```
+ * **Headers**
+ ```javascript
+ access_token = "string"
+ ```
+
+* **Success Response:**
+
+ * **Code:** 201
+ **Content:**
+ ```javascript
+ {
+ id: integer,
+ title: "string",
+ description: "string",
+ status: boolean
+ due_date: date
+ }
+ ```
+* **Error Response:**
+ * **Code:** 400
+ **Content:**
+ ```javascript
+ {
+ msg: "errors"
+ }
+ ```
+
+ OR
+
+ * **Code:** 500
+ **Content:**
+ ```javascript
+ {
+ msg: "internal server error"
+ }
+ ```
+
+ OR
+
+ * **Code** 401
+ **Content:**
+ ```javascript
+ {
+ msg: 'authentication failed'
+ }
+ ```
+
+* **Sample Call**
+```javascript
+ $.ajax({
+ method: 'POST',
+ url: `${SERVER}/todos`,
+ data: {
+ title,
+ description,
+ due_date
+ },
+ headers: {
+ access_token
+ }
+ })
+```
+----
+
+**READ TODO**
+----
+ Returns JSON data about all todos
+
+* **URL**
+
+ /todos
+
+* **Method:**
+
+ `GET`
+
+* **Data Params**
+
+ * **Headers**
+ ```javascript
+ access_token = "string"
+ ```
+
+* **Success Response:**
+
+ * **Code:** 200
+ **Content:**
+ ```javascript
+ [
+ {
+ id: integer,
+ title: "string"
+ description: "string",
+ status: boolean,
+ due_date: date
+ },
+ {
+ ..
+ }
+ ]
+ ```
+
+* **Error Response:**
+
+ * **Code:** 500
+ **Content:**
+ ```javascript
+ {
+ msg: 'errors'
+ }
+ ```
+ OR
+
+ * **Code** 401
+ **Content:**
+ ```javascript
+ {
+ msg: 'authentication failed'
+ }
+ ```
+
+* **Sample Call**
+```javascript
+ $.ajax({
+ method: 'GET',
+ url: `${SERVER}/todos`,
+ headers: {
+ access_token: access_token
+ }
+ })
+```
+----
+
+**GET A SINGLE TODO**
+----
+ Returns JSON data about a single todo
+
+* **URL**
+
+ /todos/:id
+
+* **Method:**
+
+ `GET`
+
+* **URL Params**
+
+ **Required:**
+ ```
+ id = integer
+ ```
+
+* **Data Params**
+
+ * **Headers**
+ ```javascript
+ access_token = "string"
+ ```
+
+* **Success Response:**
+
+ * **Code:** 200
+ **Content:**
+ ```javascript
+ {
+ id: integer,
+ title: "string",
+ description: "string",
+ status: boolean
+ due_date: date
+ }
+ ```
+
+* **Error Response:**
+
+ * **Code:** 404
+ **Content:**
+ ```javascript
+ {
+ msg: "todo with id ... is not found"
+ }
+ ```
+
+ OR
+
+ * **Code:** 500
+ **Content:**
+ ```javascript
+ {
+ msg: 'internal server error'
+ }
+ ```
+
+ OR
+
+ * **Code** 401
+ **Content:**
+ ```javascript
+ {
+ msg: 'authentication failed'
+ }
+ ```
+
+----
+
+**EDIT TODO**
+----
+ Returns JSON data about updated todo
+
+* **URL**
+
+ /todos/:id
+
+* **Method:**
+
+ `PUT`
+
+* **URL Params**
+
+ **Required:**
+ ```
+ id=integer
+ ```
+
+* **Data Params**
+
+ * **Data**
+ ```javascript
+ {
+ title: "string",
+ description: "string",
+ due_date: date
+ }
+ ```
+ * **Headers**
+ ```javascript
+ access_token = "string"
+ ```
+
+* **Success Response:**
+
+ * **Code:** 200
+ **Content:**
+ ```javascript
+ {
+ id: integer,
+ title: "string",
+ description: "string",
+ status: boolean,
+ due_date: date
+ }
+ ```
+
+* **Error Response:**
+
+ * **Code:** 404
+ **Content:**
+ ```javascript
+ {
+ msg: "todo with id ... is not found"
+ }
+ ```
+
+ OR
+
+ * **Code:** 500
+ **Content:**
+ ```javascript
+ {
+ msg: "internal server error"
+ }
+ ```
+
+ OR
+
+ * **Code** 401
+ **Content:**
+ ```javascript
+ {
+ msg: 'authentication failed'
+ }
+ ```
+* **Sample Call**
+```javascript
+ $.ajax({
+ method: 'PUT',
+ url: `${SERVER}/todos/${idTemp}`,
+ headers: {
+ access_token: access_token
+ },
+ data: {
+ title,
+ description,
+ due_date
+ }
+ })
+```
+
+----
+
+**FINISH TODO**
+----
+ Finish a todo
+
+* **URL**
+
+ /todos/:id
+
+* **Method:**
+
+ `PATCH`
+
+* **URL Params**
+
+ **Required:**
+ ```
+ id=integer
+ ```
+
+* **Data Params**
+
+ * **Headers**
+ ```javascript
+ access_token = "string"
+ ```
+
+* **Success Response:**
+
+ * **Code:** 200
+ **Content:**
+ ```javascript
+ {
+ id: integer,
+ title: "string",
+ description: "string",
+ status: boolean
+ due_date: date
+ }
+ ```
+
+* **Error Response:**
+
+ * **Code:** 401
+ **Content:**
+ ```javascript
+ {
+ msg: 'authentication failed'
+ }
+ ```
+
+ OR
+
+ * **Code:** 404
+ **Content:**
+ ```javascript
+ {
+ msg: 'todo is not found'
+ }
+ ```
+
+ OR
+
+ * **Code:** 500
+ **Content:**
+ ```javascript
+ {
+ msg: 'internal server error'
+ }
+ ```
+* **Sample Call**
+```javascript
+ $.ajax({
+ method: 'PATCH',
+ url: `${SERVER}/todos/${id}`,
+ headers: {
+ access_token: access_token
+ }
+ })
+```
+
+----
+
+**DELETE TODO**
+----
+ Returns message
+
+* **URL**
+
+ /todos/:id
+
+* **Method:**
+
+ `DELETE`
+
+* **URL Params**
+
+ **Required:**
+ ```
+ id=integer
+ ```
+
+* **Data Params**
+
+ * **Headers**
+ ```javascript
+ access_token = "string"
+
+* **Success Response:**
+
+ * **Code:** 200
+ **Content:**
+ ```javascript
+ {
+ msg: 'todo deleted successfully'
+ }
+ ```
+
+* **Error Response:**
+
+ * **Code:** 404
+ **Content:**
+ ```javascript
+ {
+ msg: "todo is not found"
+ }
+ ```
+
+ OR
+
+ * **Code:** 500
+ **Content:**
+ ```javascript
+ {
+ msg: "internal server error"
+ }
+ ```
+
+ OR
+
+ * **Code** 401
+ **Content:**
+ ```javascript
+ {
+ msg: 'authentication failed'
+ }
+ ```
+
+**Sample Call**
+```javascript
+ $.ajax({
+ method: 'DELETE',
+ url: `${SERVER}/todos/${id}`,
+ headers: {
+ access_token: access_token
+ }
+ })
+```
+----
+
+**WEATHER**
+----
+ Returns current weather
+
+* **URL**
+
+ /weathers
+
+* **Method:**
+
+ `GET`
+
+* **Success Response:**
+
+ * **Code:** 200
+ **Content:**
+ ```javascript
+ {
+ base: "stations",
+ clouds: {
+ all: integer
+ },
+ coord: {
+ lon: integer,
+ lat: integer
+ },
+ main: {
+ temp: integer,
+ feels_like: integer,
+ temp_min: integer,
+ temp_max: integer,
+ humidity: integer,
+ pressure: integer
+ },
+ weather: [
+ {
+ id: integer,
+ main: "string",
+ description: "string",
+ }
+ ],
+ name: "string",
+ wind: {
+ speed: integer,
+ deg: integer
+ },
+ ...
+ }
+ ```
+
+* **Error Response:**
+
+ * **Code:** 404
+ **Content:**
+ ```javascript
+ {
+ msg: "location is not found"
+ }
+ ```
+
+ OR
+
+ * **Code:** 500
+ **Content:**
+ ```javascript
+ {
+ msg: "internal server error"
+ }
+ ```
+
+**Sample Call**
+```javascript
+ $.ajax({
+ method: 'GET',
+ url: `${SERVER}/weathers`
+ })
+```
+----
\ No newline at end of file
diff --git a/client/.firebaserc b/client/.firebaserc
new file mode 100644
index 0000000..9e35220
--- /dev/null
+++ b/client/.firebaserc
@@ -0,0 +1,5 @@
+{
+ "projects": {
+ "default": "fancy-to-do-5ee22"
+ }
+}
diff --git a/client/.gitignore b/client/.gitignore
new file mode 100644
index 0000000..dbb58ff
--- /dev/null
+++ b/client/.gitignore
@@ -0,0 +1,66 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+firebase-debug.log*
+firebase-debug.*.log*
+
+# Firebase cache
+.firebase/
+
+# Firebase config
+
+# Uncomment this if you'd like others to create their own Firebase project.
+# For a team working on the same Firebase project(s), it is recommended to leave
+# it commented so all members can deploy to the same project(s) in .firebaserc.
+# .firebaserc
+
+# Runtime data
+pids
+*.pid
+*.seed
+*.pid.lock
+
+# Directory for instrumented libs generated by jscoverage/JSCover
+lib-cov
+
+# Coverage directory used by tools like istanbul
+coverage
+
+# nyc test coverage
+.nyc_output
+
+# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
+.grunt
+
+# Bower dependency directory (https://bower.io/)
+bower_components
+
+# node-waf configuration
+.lock-wscript
+
+# Compiled binary addons (http://nodejs.org/api/addons.html)
+build/Release
+
+# Dependency directories
+node_modules/
+
+# Optional npm cache directory
+.npm
+
+# Optional eslint cache
+.eslintcache
+
+# Optional REPL history
+.node_repl_history
+
+# Output of 'npm pack'
+*.tgz
+
+# Yarn Integrity file
+.yarn-integrity
+
+# dotenv environment variables file
+.env
diff --git a/client/firebase.json b/client/firebase.json
new file mode 100644
index 0000000..b3a5ba8
--- /dev/null
+++ b/client/firebase.json
@@ -0,0 +1,16 @@
+{
+ "hosting": {
+ "public": ".",
+ "ignore": [
+ "firebase.json",
+ "**/.*",
+ "**/node_modules/**"
+ ],
+ "rewrites": [
+ {
+ "source": "**",
+ "destination": "/index.html"
+ }
+ ]
+ }
+}
diff --git a/client/functions.js b/client/functions.js
new file mode 100644
index 0000000..58d282d
--- /dev/null
+++ b/client/functions.js
@@ -0,0 +1,48 @@
+function formatDate(date) {
+ var d = new Date(date),
+ month = '' + (d.getMonth() + 1),
+ day = '' + d.getDate(),
+ year = d.getFullYear();
+
+ if (month.length < 2)
+ month = '0' + month;
+ if (day.length < 2)
+ day = '0' + day;
+
+ return [year, month, day].join('-');
+}
+
+function convertTemp (temp) {
+ const celcius = (+temp-273.15).toFixed(1)
+ return `${celcius}°C`
+}
+
+function source(description) {
+ description = description.toLowerCase()
+ let source
+ console.log(description)
+ switch (description) {
+ case "thunderstorm":
+ source = "https://www.flaticon.com/svg/static/icons/svg/222/222506.svg"
+ break;
+ case "mist":
+ source = "https://www.flaticon.com/svg/static/icons/svg/414/414927.svg"
+ break;
+ case "clouds":
+ source = "https://www.flaticon.com/svg/static/icons/svg/414/414927.svg"
+ break;
+ case "sunny":
+ source = "https://www.flaticon.com/svg/static/icons/svg/2917/2917242.svg"
+ break;
+ case "clear":
+ source = "https://www.flaticon.com/svg/static/icons/svg/2917/2917242.svg"
+ break;
+ case "windy":
+ source = "https://www.flaticon.com/svg/static/icons/svg/2917/2917242.svg"
+ break;
+ case "rain" :
+ source = "https://www.flaticon.com/svg/static/icons/svg/414/414974.svg"
+ break;
+ }
+ return source
+}
\ No newline at end of file
diff --git a/client/index.html b/client/index.html
index 69763a7..8330aca 100644
--- a/client/index.html
+++ b/client/index.html
@@ -1,11 +1,184 @@
+
- Document
+
+
+
+
+
+
+
+
+
+
+ Fancy ToDo
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Here Are Your Todos
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/client/index.js b/client/index.js
new file mode 100644
index 0000000..d305ce3
--- /dev/null
+++ b/client/index.js
@@ -0,0 +1,497 @@
+const SERVER = 'https://fancy-todo-tori.herokuapp.com'
+
+const Toast = Swal.mixin({
+ toast: true,
+ position: "top-end",
+ showConfirmButton: false,
+ timer: 3000,
+ timerProgressBar: true,
+ didOpen: (toast) => {
+ toast.addEventListener("mouseenter", Swal.stopTimer);
+ toast.addEventListener("mouseleave", Swal.resumeTimer);
+ },
+});
+
+function beforeLogin() {
+ $('#register').hide()
+ $('#nav-login').show()
+ $('#nav-register').show()
+ $('#nav-name').hide()
+ $('#nav-logout').hide()
+ $('#login').show()
+ $('#content').hide()
+ $('todos').hide()
+ $('#form-addToDo').hide()
+ $('#weather').hide()
+ $('#form-updateTodo').hide()
+}
+
+function afterLogin() {
+ $('#register').hide()
+ $('#login').hide()
+ $('#content').show()
+ $('#todos').show()
+ $('#nav-login').hide()
+ $('#nav-name').empty()
+ $('#nav-name').append(`${localStorage.getItem('name')}`)
+ $('#nav-name').show()
+ $('#nav-register').hide()
+ $('#nav-logout').show()
+ $('#form-addToDo').hide()
+ $('#form-updateTodo').hide()
+ $('#weather').show()
+ $('#weather').empty()
+ fetchToDos()
+ weather()
+}
+
+$(document).ready(_ => {
+ const token = localStorage.getItem('access_token')
+ if (token) {
+ afterLogin()
+ } else {
+ beforeLogin()
+ }
+})
+
+$('#nav-name').on('click', () => {
+ afterLogin()
+})
+$('.back-btn').on('click', () => {
+ afterLogin()
+})
+
+//move to login
+$('#nav-login').on('click', () => {
+ $('#register').hide()
+ $('#login').show()
+ $('#content').hide()
+})
+
+$('#login-sc').on('click', () => {
+ $('#register').hide()
+ $('#login').show()
+ $('#content').hide()
+})
+
+//move to register
+$('#nav-register').on('click', () => {
+ $('#register').show()
+ $('#login').hide()
+ $('#content').hide()
+})
+
+$('#register-sc').on('click', () => {
+ $('#register').show()
+ $('#login').hide()
+ $('#content').hide()
+})
+
+//register
+const register = e => {
+ e.preventDefault()
+ const email = $('#register-email').val()
+ const password = $('#register-pwd').val()
+
+ $.ajax({
+ method: 'POST',
+ url: `${SERVER}/users/register`,
+ data: {
+ email,
+ password
+ }
+ })
+ .done(response => {
+ $('#register').hide()
+ $('#login').show()
+ $('#todo').hide()
+
+ Swal.fire({
+ icon: 'success',
+ title: 'Sucess',
+ text: 'Your account has been registered'
+ })
+ })
+ .fail(err => {
+ console.log(err)
+ Swal.fire({
+ icon: 'error',
+ title: 'Error',
+ text: err.responseJSON.msg
+ })
+ })
+ .always(() => {
+ $('#register-pwd').val("")
+ })
+}
+
+//login
+const login = e => {
+ e.preventDefault()
+
+ const email = $('#login-email').val()
+ const password = $('#login-pwd').val()
+
+ $.ajax({
+ method: 'POST',
+ url: `${SERVER}/users/login`,
+ data: {
+ email,
+ password
+ }
+ })
+ .done(response => {
+ $('#nav-name').empty()
+ localStorage.setItem('name', email.substring(0, email.indexOf('@')))
+ const access_token = response.accessToken
+ localStorage.setItem('access_token', access_token)
+ afterLogin()
+
+ Toast.fire({
+ icon: 'success',
+ title: 'Logged in successfully'
+ })
+ })
+ .fail(err => {
+ Swal.fire({
+ icon: 'error',
+ title: 'Error',
+ text: err.responseJSON.msg
+ })
+ })
+ .always(() => {
+ $('#login-pwd').val("")
+ })
+}
+
+function onSignIn(googleUser) {
+ const profile = googleUser.getBasicProfile();
+ const name = profile.getName()
+ const google_access_token = googleUser.getAuthResponse().id_token;
+
+ $.ajax({
+ method: 'POST',
+ url: `${SERVER}/users/googleLogin`,
+ data: {
+ google_access_token
+ }
+ })
+ .done(response => {
+ localStorage.setItem('access_token', response.access_token)
+ localStorage.setItem('name',name)
+ $('#nav-name').empty()
+ afterLogin()
+
+ Toast.fire({
+ icon: 'success',
+ title: 'Logged in successfully'
+ })
+
+ })
+ .fail(err => {
+ console.log(err)
+ })
+}
+
+//logout
+$('#nav-logout').on('click', () => {
+ Swal.fire({
+ title: `Logout`,
+ text: `You're going to be logged out`,
+ icon: 'warning',
+ showCancelButton: true,
+ confirmButtonColor: '#3085d6',
+ cancelButtonColor: '#d33',
+ confirmButtonText: 'Logout'
+ })
+ .then((result) => {
+ if (result.isConfirmed) {
+ Swal.fire({
+ icon: 'info',
+ title: 'Successfully logged out'
+ })
+ beforeLogin()
+ localStorage.clear()
+ signOut()
+
+ } else {
+ Toast.fire({
+ icon: 'info',
+ title: 'cancelled'
+ })
+ afterLogin()
+ }
+ })
+
+})
+
+function signOut() {
+ const auth2 = gapi.auth2.getAuthInstance();
+ auth2.signOut().then(function () {
+ console.log('User signed out.');
+ })
+};
+
+//Weather
+const weather = () => {
+ $('#weather').empty()
+ $.ajax({
+ method: 'GET',
+ url: `${SERVER}/weathers`
+ })
+ .done(response => {
+ console.log(response)
+ $('#weather').append(`
+ Current Weather
+ Place
+
${response.name}
+
+ Description
+
${response.weather[0].main}
+
+
+ Temperature
+
Temperature: ${response.main.temp.toFixed(1)}°C
+
Feels Like: ${response.main.feels_like.toFixed(1)}°C
+
+ Humidity
+
${response.main.humidity}%
+ `)
+ })
+ .fail(err => {
+ Toast.fire({
+ icon: 'warning',
+ title: `i can't find your location`
+ })
+ $('#weather').hide()
+ })
+}
+
+//fetchTodos
+const fetchToDos = () => {
+ const access_token = localStorage.getItem('access_token')
+ $('#todos').empty()
+ $.ajax({
+ method: 'GET',
+ url: `${SERVER}/todos`,
+ headers: {
+ access_token: access_token
+ }
+ })
+ .done(response => {
+ if (response[0]) {
+ response.forEach(el => {
+ if (el.status === false) {
+ $('#todos').append(`
+
+
+
${el.title}
+
+ ${el.description}
+ Due at: ${formatDate(el.due_date)}
+
+
+
+ Finish Task
+ Update Task
+ Delete Task
+
+
+
`)
+ } else {
+ $('#todos').append(`
+
+
+
${el.title}
+
+ ${el.description}
+ Due at: ${formatDate(el.due_date)}
+
+
+
+ Task Finished
+ Delete Task
+
+
+
`)
+ }
+ })
+ } else {
+ $('#todos').append(`
+ There's nothing here
Start adding some task
`)
+ }
+ })
+ .fail(err => {
+ Swal.fire({
+ icon: 'error',
+ title: err.responseJSON.msg
+ })
+ })
+}
+
+//addTodo
+const addTodo = () => {
+ const access_token = localStorage.getItem('access_token')
+ const title = $('#add_title').val()
+ const description = $('#add_description').val()
+ const due_date = $('#add_due_date').val()
+
+ $.ajax({
+ method: 'POST',
+ url: `${SERVER}/todos`,
+ data: {
+ title,
+ description,
+ due_date
+ },
+ headers: {
+ access_token
+ }
+ })
+ .done(response => {
+ afterLogin()
+ Toast.fire({
+ icon: 'success',
+ title: 'ToDo added successfully'
+ })
+
+ })
+ .fail(err => {
+ Swal.fire({
+ icon: 'error',
+ title: 'Error',
+ text: err.responseJSON.msg
+ })
+ })
+ .always(() => {
+ $('#add_title').val("")
+ $('#add_description').val("")
+ $('#add_due_date').val("")
+ })
+}
+
+$('#addTodo').on('click', () => {
+ $('#todos').hide()
+ $('#form-addToDo').show()
+ $('#form-updateTodo').hide()
+})
+
+//updateTodo
+let idTemp;
+
+const updateToDo = _ => {
+ const access_token = localStorage.getItem('access_token')
+ const title = $('#edit_title').val()
+ const description = $('#edit_description').val()
+ const due_date = $('#edit_due_date').val()
+
+ $.ajax({
+ method: 'PUT',
+ url: `${SERVER}/todos/${idTemp}`,
+ headers: {
+ access_token: access_token
+ },
+ data: {
+ title,
+ description,
+ due_date
+ }
+ })
+ .done(response => {
+ afterLogin()
+ Toast.fire({
+ icon: 'success',
+ title: `${response.title} has been updated`
+ })
+ })
+ .fail(err => {
+ Swal.fire({
+ icon: 'error',
+ title: 'Error',
+ text: err.responseJSON.msg
+ })
+ })
+}
+
+const updateToDoForm = (id, title, description, due_date) => {
+ afterLogin()
+ $('#form-updateTodo').show()
+ $('#edit_title').val(title)
+ $('#edit_description').val(description)
+ $('#edit_due_date').val(formatDate(due_date))
+ idTemp = id
+
+}
+
+//finishTodo
+const finishToDo = id => {
+ const access_token = localStorage.getItem('access_token')
+ $.ajax({
+ method: 'PATCH',
+ url: `${SERVER}/todos/${id}`,
+ headers: {
+ access_token: access_token
+ }
+ })
+ .done(response => {
+ afterLogin()
+ Toast.fire({
+ icon: 'success',
+ title: `you have finished todo ${response.title}`
+ })
+ })
+ .fail(err => {
+ Swal.fire({
+ icon: 'error',
+ title: 'Error',
+ text: err.responseJSON.msg
+ })
+ })
+}
+
+//deleteToDo
+const deleteToDo = id => {
+ const access_token = localStorage.getItem('access_token')
+
+ Swal.fire({
+ title: `Delete Task`,
+ text: `You're going to delete this task`,
+ icon: 'warning',
+ showCancelButton: true,
+ confirmButtonColor: '#3085d6',
+ cancelButtonColor: '#d33',
+ confirmButtonText: 'Delete'
+ })
+ .then((result) => {
+ if (result.isConfirmed) {
+ $.ajax({
+ method: 'DELETE',
+ url: `${SERVER}/todos/${id}`,
+ headers: {
+ access_token: access_token
+ }
+ })
+ .done(response => {
+ afterLogin()
+ Toast.fire({
+ icon: 'success',
+ title: 'todo successfully deleted'
+ })
+ })
+ .fail(err => {
+ Swal.fire({
+ icon: 'error',
+ title: 'Error',
+ text: err.responseJSON.msg
+ })
+ })
+
+ } else {
+ Toast.fire({
+ icon: 'info',
+ title: 'cancelled'
+ })
+ afterLogin()
+ }
+ })
+}
\ No newline at end of file
diff --git a/client/style.css b/client/style.css
new file mode 100644
index 0000000..954ab4c
--- /dev/null
+++ b/client/style.css
@@ -0,0 +1,45 @@
+html,
+body {
+ height: 100%;
+}
+
+body {
+ display: -ms-flexbox;
+ display: flex;
+ -ms-flex-align: center;
+ align-items: center;
+ padding-top: 40px;
+ padding-bottom: 40px;
+ background-color: #f5f5f5;
+ font-family: 'Prompt', sans-serif;
+}
+
+.form-signin {
+ width: 100%;
+ max-width: 330px;
+ padding: 15px;
+ margin: auto;
+}
+.form-signin .checkbox {
+ font-weight: 400;
+}
+.form-signin .form-control {
+ position: relative;
+ box-sizing: border-box;
+ height: auto;
+ padding: 10px;
+ font-size: 16px;
+}
+.form-signin .form-control:focus {
+ z-index: 2;
+}
+.form-signin input[type="email"] {
+ margin-bottom: -1px;
+ border-bottom-right-radius: 0;
+ border-bottom-left-radius: 0;
+}
+.form-signin input[type="password"] {
+ margin-bottom: 10px;
+ border-top-left-radius: 0;
+ border-top-right-radius: 0;
+}
\ 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/app.js b/server/app.js
new file mode 100644
index 0000000..147ee06
--- /dev/null
+++ b/server/app.js
@@ -0,0 +1,19 @@
+if (process.env.NODE_ENV !== 'production') {
+ require('dotenv').config()
+}
+
+const express = require('express')
+const cors = require('cors')
+const router = require('./routes')
+const error_handler = require('./middlewares/error_handler')
+
+const app = express()
+const port = process.env.PORT || 3000
+
+app.use(cors())
+app.use(express.json())
+app.use(express.urlencoded({extended: true}))
+app.use(router)
+app.use(error_handler)
+
+app.listen(port, () => console.log(`listening at ${port}`))
\ No newline at end of file
diff --git a/server/config/config.json b/server/config/config.json
new file mode 100644
index 0000000..eedd1d5
--- /dev/null
+++ b/server/config/config.json
@@ -0,0 +1,20 @@
+{
+ "development": {
+ "username": "postgres",
+ "password": "torian05092002",
+ "database": "fancytodo",
+ "host": "127.0.0.1",
+ "dialect": "postgres"
+ },
+ "test": {
+ "username": "root",
+ "password": null,
+ "database": "database_test",
+ "host": "127.0.0.1",
+ "dialect": "mysql"
+ },
+ "production": {
+ "use_env_variable": "DATABASE_URL",
+ "dialect": "postgres"
+ }
+}
diff --git a/server/controllers/todoController.js b/server/controllers/todoController.js
new file mode 100644
index 0000000..7b3ca28
--- /dev/null
+++ b/server/controllers/todoController.js
@@ -0,0 +1,111 @@
+const {ToDo} = require('../models/index')
+
+class ToDoController {
+ static async create(req, res, next) {
+ try {
+ const payload = {
+ title: req.body.title,
+ description: req.body.description,
+ due_date: req.body.due_date,
+ UserId: req.user.id
+ }
+ const newToDo = await ToDo.create(payload, {
+ returning: true
+ })
+ res.status(201).json(newToDo)
+ } catch (err) {
+ next(err)
+ }
+ }
+
+ static async read(req, res, next) {
+ try {
+ const UserId = req.user.id
+ const todos = await ToDo.findAll({
+ order: [['id', 'asc']],
+ where: {
+ UserId: UserId
+ }
+ })
+ res.status(200).json(todos)
+ } catch (err) {
+ next(err)
+ }
+ }
+
+ static async findOne(req, res, next) {
+ try {
+ const id = +req.params.id
+ const todo = await ToDo.findByPk(id)
+ if (todo) {
+ res.status(200).json(todo)
+ } else {
+ throw { msg: `todo with id ${id} is not found`, status: 404 }
+ }
+ } catch (err) {
+ next(err)
+ }
+ }
+
+ static async update(req, res, next) {
+ try {
+ const payload = {
+ title: req.body.title,
+ description: req.body.description,
+ due_date: req.body.due_date,
+ }
+ const updated = await ToDo.update(payload, {
+ where: {
+ id: +req.params.id
+ },
+ returning: true
+ })
+ if (updated[0] !== 1) {
+ throw { msg: `todo with id ${+req.params.id} is not found`, status: 404 }
+ } else {
+ res.status(200).json(updated[1][0])
+ }
+
+ } catch (err) {
+ next(err)
+ }
+ }
+
+ static async finish(req, res, next) {
+ try {
+ const finished = await ToDo.update({status: true}, {
+ where: {
+ id: +req.params.id
+ },
+ returning: true
+ })
+ if (finished[0] !== 1) {
+ throw { msg: `todo with id ${+req.params.id} is not found`, status: 404 }
+ } else {
+ res.status(200).json(finished[1][0])
+ }
+ } catch (err) {
+ next(err)
+ }
+ }
+
+ static async delete(req, res, next) {
+ try {
+ const destroyed = await ToDo.destroy({
+ where: {
+ id: +req.params.id
+ }
+ })
+ if (destroyed !== 1) {
+ throw { msg: `todo with id ${+req.params.id} is not found`, status: 404 }
+ } else {
+ res.status(200).json({msg: `todo deleted successfuly`})
+ }
+ } catch (err) {
+ next(err)
+ }
+
+ }
+}
+
+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..2691cc0
--- /dev/null
+++ b/server/controllers/userController.js
@@ -0,0 +1,97 @@
+const { User } = require('../models/index')
+const { compare } = require('../helper/bcrypt')
+const { signToken } = require('../helper/jwt')
+const {OAuth2Client} = require('google-auth-library');
+
+class UserController {
+ static async register(req, res, next) {
+ try {
+ const payload = {
+ email: req.body.email,
+ password: req.body.password
+ }
+
+ const user = await User.create(payload)
+ res.status(201).json({
+ id: user.id,
+ email: user.email
+ })
+ } catch (err) {
+ next(err)
+ }
+ }
+
+ static async login(req, res, next) {
+ try {
+ const payload = {
+ email: req.body.email,
+ password: req.body.password
+ }
+
+ const user = await User.findOne({
+ where: {
+ email: payload.email
+ }
+ })
+
+ if (!user) {
+ throw { msg: 'username or password is incorrect', status: 401 }
+ } else if (!compare(payload.password, user.password)) {
+ throw { msg: 'username or password is incorrect', status: 401 }
+ } else {
+ const payload = {
+ id: user.id,
+ email:user.email
+ }
+ const token = signToken(payload)
+ res.status(200).json({accessToken: token})
+ }
+
+ } catch (err) {
+ next(err)
+ }
+ }
+
+ static googleLogin (req, res, next) {
+ const {google_access_token} = req.body
+ const client = new OAuth2Client(process.env.CLIENT_ID);
+
+ let email
+ client.verifyIdToken({
+ idToken: google_access_token,
+ audience: process.env.CLIENT_ID
+ })
+ .then(ticket => {
+ const payload = ticket.getPayload()
+ email = payload.email
+ return User.findOne({
+ where: {
+ email: payload.email
+ }
+ })
+ })
+ .then(user => {
+ if (user) {
+ return user
+ }else {
+ const obj = {
+ email: email,
+ password: 'incorrect329'
+ }
+ return User.create(obj)
+ }
+ })
+ .then(data => {
+ const access_token = signToken({
+ id: data.id,
+ email: data.email
+ })
+ return res.status(200).json({access_token})
+ })
+ .catch(err => {
+ next(err)
+ })
+ }
+}
+
+module.exports = UserController
\ No newline at end of file
diff --git a/server/controllers/weatherController.js b/server/controllers/weatherController.js
new file mode 100644
index 0000000..e5bbb46
--- /dev/null
+++ b/server/controllers/weatherController.js
@@ -0,0 +1,31 @@
+const axios = require('axios')
+
+class WeatherController {
+ static currentWeather (req, res, next) {
+ axios({
+ method: 'GET',
+ url: 'https://api.ipgeolocation.io/getip'
+ })
+ .then(response => {
+ const ip = response.data.ip
+ return axios({
+ method: 'GET',
+ url: `https://api.ipgeolocation.io/astronomy?apiKey=${process.env.GEO}&ip=${ip}`
+ })
+ })
+ .then(result => {
+ const city = result.data.location.state_prov
+ return axios({
+ url: `http://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${process.env.WEATHER}&units=metric`,
+ method: 'GET'
+ })
+ })
+ .then(data => {
+ res.status(200).json(data.data)
+ })
+ .catch(err => {
+ next(err)
+ })
+ }
+}
+module.exports = WeatherController
\ No newline at end of file
diff --git a/server/helper/bcrypt.js b/server/helper/bcrypt.js
new file mode 100644
index 0000000..0db51e1
--- /dev/null
+++ b/server/helper/bcrypt.js
@@ -0,0 +1,16 @@
+const bcrypt = require("bcryptjs")
+
+const hashPassword = password => {
+ const hashed = bcrypt.hashSync(password, +process.env.SALT)
+ return hashed
+}
+
+const compare = (password, hashed) => {
+ const result = bcrypt.compareSync(password, hashed)
+ return result
+}
+
+module.exports = {
+ hashPassword,
+ compare
+}
\ No newline at end of file
diff --git a/server/helper/jwt.js b/server/helper/jwt.js
new file mode 100644
index 0000000..e9d9253
--- /dev/null
+++ b/server/helper/jwt.js
@@ -0,0 +1,16 @@
+const jwt = require('jsonwebtoken')
+
+const signToken = payload => {
+ const token = jwt.sign(payload, process.env.SECRET)
+ return token
+}
+
+const verifyToken = token => {
+ const decoded = jwt.verify(token, process.env.SECRET)
+ return decoded
+}
+
+module.exports = {
+ signToken,
+ verifyToken
+}
\ No newline at end of file
diff --git a/server/middlewares/authentication.js b/server/middlewares/authentication.js
new file mode 100644
index 0000000..2a17710
--- /dev/null
+++ b/server/middlewares/authentication.js
@@ -0,0 +1,28 @@
+const { verifyToken } = require('../helper/jwt')
+const { User } = require('../models')
+
+const authenticate = async (req, res, next) => {
+ const {access_token} = req.headers
+ try {
+ if (!access_token) {
+ throw { msg: `Authentication failed`, status: 401 }
+ } else {
+ const decoded = verifyToken(access_token)
+ const user = await User.findOne({
+ where: {
+ email: decoded.email
+ }
+ })
+ if (!user) {
+ throw { msg: `Authentication failed`, status: 401 }
+ } else {
+ req.user = decoded
+ next()
+ }
+ }
+ } catch (err) {
+ next(err)
+ }
+}
+
+module.exports = authenticate
\ No newline at end of file
diff --git a/server/middlewares/authorization.js b/server/middlewares/authorization.js
new file mode 100644
index 0000000..293810b
--- /dev/null
+++ b/server/middlewares/authorization.js
@@ -0,0 +1,19 @@
+const { ToDo } = require('../models')
+
+const authorization = async (req, res, next) => {
+ const id = +req.params.id
+ try {
+ const todo = await ToDo.findByPk(id)
+ if (!todo) {
+ throw { msg: `Todo is not found`, status: 404 }
+ } else if (todo.UserId !== req.user.id) {
+ throw { msg: `Not Authorized`, status: 401 }
+ } else {
+ next()
+ }
+ } catch (err) {
+ next(err)
+ }
+}
+
+module.exports = authorization
\ No newline at end of file
diff --git a/server/middlewares/error_handler.js b/server/middlewares/error_handler.js
new file mode 100644
index 0000000..d55fb63
--- /dev/null
+++ b/server/middlewares/error_handler.js
@@ -0,0 +1,11 @@
+module.exports = function (err, req, res, next) {
+ let status = err.status || 500
+ let msg = err.msg || 'Internal Server Error'
+
+ if (err.name === 'SequelizeValidationError' || err.name === 'SequelizeUniqueConstraintError'){
+ status = 400
+ msg = err.errors.map(el => el.message).join(', ')
+ }
+ console.log(err)
+ res.status(status).json({msg})
+}
\ No newline at end of file
diff --git a/server/migrations/2020102605111-create-user.js b/server/migrations/2020102605111-create-user.js
new file mode 100644
index 0000000..1a1f21a
--- /dev/null
+++ b/server/migrations/2020102605111-create-user.js
@@ -0,0 +1,29 @@
+'use strict';
+module.exports = {
+ up: async (queryInterface, Sequelize) => {
+ await queryInterface.createTable('Users', {
+ id: {
+ allowNull: false,
+ autoIncrement: true,
+ primaryKey: true,
+ type: Sequelize.INTEGER
+ },
+ email: {
+ unique: true,
+ type: Sequelize.STRING
+ },
+ password: {
+ type: Sequelize.STRING
+ },
+ createdAt: {
+ type: Sequelize.DATE
+ },
+ updatedAt: {
+ type: Sequelize.DATE
+ }
+ });
+ },
+ down: async (queryInterface, Sequelize) => {
+ await queryInterface.dropTable('Users');
+ }
+};
\ No newline at end of file
diff --git a/server/migrations/20201026060655-create-to-do.js b/server/migrations/20201026060655-create-to-do.js
new file mode 100644
index 0000000..46ab270
--- /dev/null
+++ b/server/migrations/20201026060655-create-to-do.js
@@ -0,0 +1,46 @@
+'use strict';
+
+const { sequelize } = require("../models");
+
+module.exports = {
+ up: async (queryInterface, Sequelize) => {
+ await queryInterface.createTable('ToDos', {
+ id: {
+ allowNull: false,
+ autoIncrement: true,
+ primaryKey: true,
+ type: Sequelize.INTEGER
+ },
+ title: {
+ type: Sequelize.STRING
+ },
+ description: {
+ type: Sequelize.STRING
+ },
+ status: {
+ type: Sequelize.BOOLEAN
+ },
+ due_date: {
+ type: Sequelize.DATE
+ },
+ UserId : {
+ type: Sequelize.INTEGER,
+ references: {
+ model: 'Users',
+ key: 'id'
+ },
+ onDelete: 'cascade',
+ onUpdate: 'cascade'
+ },
+ createdAt: {
+ type: Sequelize.DATE
+ },
+ updatedAt: {
+ type: Sequelize.DATE
+ }
+ });
+ },
+ down: async (queryInterface, Sequelize) => {
+ await queryInterface.dropTable('ToDos');
+ }
+};
\ No newline at end of file
diff --git a/server/models/index.js b/server/models/index.js
new file mode 100644
index 0000000..33f09e7
--- /dev/null
+++ b/server/models/index.js
@@ -0,0 +1,37 @@
+'use strict';
+
+const fs = require('fs');
+const path = require('path');
+const Sequelize = require('sequelize');
+const basename = path.basename(__filename);
+const env = process.env.NODE_ENV || 'development';
+const config = require(__dirname + '/../config/config.json')[env];
+const db = {};
+
+let sequelize;
+if (config.use_env_variable) {
+ sequelize = new Sequelize(process.env[config.use_env_variable], config);
+} else {
+ sequelize = new Sequelize(config.database, config.username, config.password, config);
+}
+
+fs
+ .readdirSync(__dirname)
+ .filter(file => {
+ return (file.indexOf('.') !== 0) && (file !== basename) && (file.slice(-3) === '.js');
+ })
+ .forEach(file => {
+ const model = require(path.join(__dirname, file))(sequelize, Sequelize.DataTypes);
+ db[model.name] = model;
+ });
+
+Object.keys(db).forEach(modelName => {
+ if (db[modelName].associate) {
+ db[modelName].associate(db);
+ }
+});
+
+db.sequelize = sequelize;
+db.Sequelize = Sequelize;
+
+module.exports = db;
diff --git a/server/models/todo.js b/server/models/todo.js
new file mode 100644
index 0000000..40c1d9c
--- /dev/null
+++ b/server/models/todo.js
@@ -0,0 +1,93 @@
+'use strict';
+const {Model} = require('sequelize');
+
+module.exports = (sequelize, DataTypes) => {
+ class ToDo extends Model {
+
+ static associate(models) {
+ ToDo.belongsTo(models.User, {
+ foreignKey: 'UserId',
+ targetKey: 'id'
+ })
+ }
+ };
+ ToDo.init({
+ title: {
+ type: DataTypes.STRING,
+ allowNull: false,
+ validate: {
+ notNull: {
+ args: true,
+ msg: `title can't be empty`
+ },
+ notEmpty: {
+ args: true,
+ msg: `title can't be empty`
+ }
+ }
+ },
+ description: {
+ type: DataTypes.STRING,
+ allowNull: false,
+ validate: {
+ notEmpty: {
+ args: true,
+ msg: `description can't be empty`
+ },
+ notNull: {
+ args: true,
+ msg: `description can't be empty`
+ }
+ }
+ },
+ status: {
+ type: DataTypes.BOOLEAN,
+ allowNull: false,
+ validate: {
+ notEmpty: {
+ args: true,
+ msg: `status can't be empty`
+ },
+ notNull: {
+ args: true,
+ msg: `status can't be empty`
+ }
+ }
+ },
+ due_date: {
+ type: DataTypes.DATE,
+ allowNull: false,
+ validate: {
+ notEmpty : {
+ args: true,
+ msg: `due date can't be empty`
+ },
+ notNull: {
+ args: true,
+ msg: `due date can't be empty`
+ },
+ isDate: {
+ args: true,
+ msg: `must be in date format`
+ },
+ gtToday(value) {
+ const now = new Date()
+ if (now >= value) {
+ throw new Error('due date should be greater than today')
+ }
+ }
+ }
+ }
+ }, {
+ sequelize,
+ modelName: 'ToDo',
+ });
+
+ ToDo.addHook('beforeValidate', (instance, options) => {
+ if (!instance.status) {
+ instance.status = false
+ }
+ })
+
+ return 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..88b3d7c
--- /dev/null
+++ b/server/models/user.js
@@ -0,0 +1,63 @@
+'use strict';
+const {Model} = require('sequelize');
+const { hashPassword } = require('../helper/bcrypt')
+
+module.exports = (sequelize, DataTypes) => {
+ class User extends Model {
+
+ static associate(models) {
+ User.hasMany(models.ToDo)
+ }
+ };
+ User.init({
+ email: {
+ type: DataTypes.STRING,
+ allowNull: false,
+ validate: {
+ isEmail: {
+ args: true,
+ msg: 'please insert a valid email'
+ },
+ notEmpty: {
+ args: true,
+ msg: `email can't be empty`
+ },
+ notNull: {
+ args: true,
+ msg: `email can't be empty`
+ }
+ }
+ },
+ password: {
+ type: DataTypes.STRING,
+ allowNull: false,
+ validate: {
+ notNull: {
+ args: true,
+ msg: `password can't be empty`
+ },
+ notEmpty: {
+ args: true,
+ msg: `password can't be empty`
+ },
+ isAlphanumeric: {
+ args: true,
+ msg: `password must be alphanumeric`
+ },
+ len: {
+ args: [6,20],
+ msg: `password must be a minimal of 6 and a maximum of 20 characters`
+ }
+ }
+ }
+ }, {
+ sequelize,
+ modelName: 'User',
+ });
+
+ User.addHook('beforeCreate', (instance, options) => {
+ instance.password = hashPassword(instance.password)
+ })
+
+ return 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..bc6ecf6
--- /dev/null
+++ b/server/package-lock.json
@@ -0,0 +1,1024 @@
+{
+ "name": "fancytodo",
+ "version": "1.0.0",
+ "lockfileVersion": 1,
+ "requires": true,
+ "dependencies": {
+ "@types/node": {
+ "version": "14.14.5",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.5.tgz",
+ "integrity": "sha512-H5Wn24s/ZOukBmDn03nnGTp18A60ny9AmCwnEcgJiTgSGsCO7k+NWP7zjCCbhlcnVCoI+co52dUAt9GMhOSULw=="
+ },
+ "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": "6.0.2",
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
+ "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
+ "requires": {
+ "debug": "4"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz",
+ "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==",
+ "requires": {
+ "ms": "2.1.2"
+ }
+ }
+ }
+ },
+ "any-promise": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
+ "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8="
+ },
+ "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.21.0",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.0.tgz",
+ "integrity": "sha512-fmkJBknJKoZwem3/IKSSLpkdNXZeBu5Q7GA/aRsr2btgrptmSCxi2oFjZHqGdK9DoTil9PIHlPIZw2EcRJXRvw==",
+ "requires": {
+ "follow-redirects": "^1.10.0"
+ }
+ },
+ "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=="
+ },
+ "bcryptjs": {
+ "version": "2.4.3",
+ "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz",
+ "integrity": "sha1-mrVie5PmBiH/fNrF2pczAn3x0Ms="
+ },
+ "bignumber.js": {
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.1.tgz",
+ "integrity": "sha512-IdZR9mh6ahOBv/hYGiXyVuyCetmGJhtYkqLBpTStdhEGjegpPlUawydyaF3pbIOFynJTpllEs+NP+CS9jKFLjA=="
+ },
+ "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"
+ },
+ "dependencies": {
+ "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"
+ }
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+ }
+ }
+ },
+ "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="
+ },
+ "buffer-writer": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz",
+ "integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw=="
+ },
+ "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"
+ }
+ },
+ "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
+ },
+ "dottie": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/dottie/-/dottie-2.0.2.tgz",
+ "integrity": "sha512-fmrwR04lsniq/uSr8yikThDTrM7epXHBAAjH9TbeH3rEA8tdCO7mRzB9hdmdGyJCxF8KERo9CITcm3kGuoyMhg=="
+ },
+ "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="
+ },
+ "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"
+ },
+ "dependencies": {
+ "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"
+ }
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+ }
+ }
+ },
+ "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.3",
+ "resolved": "https://registry.npmjs.org/fast-text-encoding/-/fast-text-encoding-1.0.3.tgz",
+ "integrity": "sha512-dtm4QZH9nZtcDt8qJiOH9fcQd1NAgi+K1O2DbE6GG1PPCK/BWfOH3idCTRQ4ImXRUOyopDEgDEnVEE7Y/2Wrig=="
+ },
+ "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"
+ },
+ "dependencies": {
+ "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"
+ }
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+ }
+ }
+ },
+ "follow-redirects": {
+ "version": "1.13.0",
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.0.tgz",
+ "integrity": "sha512-aq6gF1BEKje4a9i9+5jimNFIpq4Q1WiwBToeRK5NvZBd/TRsmW8BsJfOEGkr76TbOyPVD3OVDN910EcUNtRYEA=="
+ },
+ "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": "4.0.1",
+ "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-4.0.1.tgz",
+ "integrity": "sha512-jOin8xRZ/UytQeBpSXFqIzqU7Fi5TqgPNLlUsSB8kjJ76+FiGBfImF8KJu++c6J4jOldfJUtt0YmkRj2ZpSHTQ==",
+ "requires": {
+ "abort-controller": "^3.0.0",
+ "extend": "^3.0.2",
+ "https-proxy-agent": "^5.0.0",
+ "is-stream": "^2.0.0",
+ "node-fetch": "^2.3.0"
+ }
+ },
+ "gcp-metadata": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-4.2.0.tgz",
+ "integrity": "sha512-vQZD57cQkqIA6YPGXM/zc+PIZfNRFdukWGsGZ5+LcJzesi5xp6Gn7a02wRJi4eXPyArNMIYpPET4QMxGqtlk6Q==",
+ "requires": {
+ "gaxios": "^3.0.0",
+ "json-bigint": "^1.0.0"
+ },
+ "dependencies": {
+ "gaxios": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-3.2.0.tgz",
+ "integrity": "sha512-+6WPeVzPvOshftpxJwRi2Ozez80tn/hdtOUag7+gajDHRJvAblKxTFSSMPtr2hmnLy7p0mvYz0rMXLBl8pSO7Q==",
+ "requires": {
+ "abort-controller": "^3.0.0",
+ "extend": "^3.0.2",
+ "https-proxy-agent": "^5.0.0",
+ "is-stream": "^2.0.0",
+ "node-fetch": "^2.3.0"
+ }
+ }
+ }
+ },
+ "google-auth-library": {
+ "version": "6.1.3",
+ "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-6.1.3.tgz",
+ "integrity": "sha512-m9mwvY3GWbr7ZYEbl61isWmk+fvTmOt0YNUfPOUY2VH8K5pZlAIWJjxEi0PqR3OjMretyiQLI6GURMrPSwHQ2g==",
+ "requires": {
+ "arrify": "^2.0.0",
+ "base64-js": "^1.3.0",
+ "ecdsa-sig-formatter": "^1.0.11",
+ "fast-text-encoding": "^1.0.0",
+ "gaxios": "^4.0.0",
+ "gcp-metadata": "^4.2.0",
+ "gtoken": "^5.0.4",
+ "jws": "^4.0.0",
+ "lru-cache": "^6.0.0"
+ },
+ "dependencies": {
+ "jwa": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz",
+ "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==",
+ "requires": {
+ "buffer-equal-constant-time": "1.0.1",
+ "ecdsa-sig-formatter": "1.0.11",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "jws": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz",
+ "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==",
+ "requires": {
+ "jwa": "^2.0.0",
+ "safe-buffer": "^5.0.1"
+ }
+ }
+ }
+ },
+ "google-p12-pem": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-3.0.3.tgz",
+ "integrity": "sha512-wS0ek4ZtFx/ACKYF3JhyGe5kzH7pgiQ7J5otlumqR9psmWMYc+U9cErKlCYVYHoUaidXHdZ2xbo34kB+S+24hA==",
+ "requires": {
+ "node-forge": "^0.10.0"
+ }
+ },
+ "gtoken": {
+ "version": "5.0.5",
+ "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-5.0.5.tgz",
+ "integrity": "sha512-wvjkecutFh8kVfbcdBdUWqDRrXb+WrgD79DBDEYf1Om8S1FluhylhtFjrL7Tx69vNhh259qA3Q1P4sPtb+kUYw==",
+ "requires": {
+ "gaxios": "^4.0.0",
+ "google-p12-pem": "^3.0.3",
+ "jws": "^4.0.0",
+ "mime": "^2.2.0"
+ },
+ "dependencies": {
+ "jwa": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz",
+ "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==",
+ "requires": {
+ "buffer-equal-constant-time": "1.0.1",
+ "ecdsa-sig-formatter": "1.0.11",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "jws": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz",
+ "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==",
+ "requires": {
+ "jwa": "^2.0.0",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "mime": {
+ "version": "2.4.6",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.6.tgz",
+ "integrity": "sha512-RZKhC3EmpBchfTGBVb8fb+RL2cWyw/32lshnsETttkBAyAUXSGHxbEJWWRXc751DrIxG1q04b8QwMbAwkRPpUA=="
+ }
+ }
+ },
+ "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"
+ },
+ "dependencies": {
+ "inherits": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+ "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
+ }
+ }
+ },
+ "https-proxy-agent": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz",
+ "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==",
+ "requires": {
+ "agent-base": "6",
+ "debug": "4"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz",
+ "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==",
+ "requires": {
+ "ms": "2.1.2"
+ }
+ }
+ }
+ },
+ "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"
+ }
+ },
+ "inflection": {
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/inflection/-/inflection-1.12.0.tgz",
+ "integrity": "sha1-ogCTVlbW9fa8TcdQLhrstwMihBY="
+ },
+ "inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
+ },
+ "ipaddr.js": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
+ "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="
+ },
+ "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": "1.0.0",
+ "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz",
+ "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==",
+ "requires": {
+ "bignumber.js": "^9.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"
+ }
+ },
+ "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"
+ }
+ },
+ "lodash": {
+ "version": "4.17.20",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
+ "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA=="
+ },
+ "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": "6.0.0",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+ "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+ "requires": {
+ "yallist": "^4.0.0"
+ },
+ "dependencies": {
+ "yallist": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
+ }
+ }
+ },
+ "media-typer": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
+ "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
+ },
+ "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.44.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz",
+ "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg=="
+ },
+ "mime-types": {
+ "version": "2.1.27",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz",
+ "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==",
+ "requires": {
+ "mime-db": "1.44.0"
+ }
+ },
+ "moment": {
+ "version": "2.29.1",
+ "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz",
+ "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ=="
+ },
+ "moment-timezone": {
+ "version": "0.5.31",
+ "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.31.tgz",
+ "integrity": "sha512-+GgHNg8xRhMXfEbv81iDtrVeTcWt0kWmTEY1XQK14dICTXnWJnT0dxdlPspwqF3keKMVPXwayEsk1DI0AA/jdA==",
+ "requires": {
+ "moment": ">= 2.9.0"
+ }
+ },
+ "ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
+ },
+ "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.1",
+ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz",
+ "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw=="
+ },
+ "node-forge": {
+ "version": "0.10.0",
+ "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz",
+ "integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA=="
+ },
+ "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"
+ }
+ },
+ "packet-reader": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz",
+ "integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ=="
+ },
+ "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="
+ },
+ "pg": {
+ "version": "8.4.2",
+ "resolved": "https://registry.npmjs.org/pg/-/pg-8.4.2.tgz",
+ "integrity": "sha512-E9FlUrrc7w3+sbRmL1CSw99vifACzB2TjhMM9J5w9D1LIg+6un0jKkpHS1EQf2CWhKhec2bhrBLVMmUBDbjPRQ==",
+ "requires": {
+ "buffer-writer": "2.0.0",
+ "packet-reader": "1.0.0",
+ "pg-connection-string": "^2.4.0",
+ "pg-pool": "^3.2.2",
+ "pg-protocol": "^1.3.0",
+ "pg-types": "^2.1.0",
+ "pgpass": "1.x"
+ }
+ },
+ "pg-connection-string": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.4.0.tgz",
+ "integrity": "sha512-3iBXuv7XKvxeMrIgym7njT+HlZkwZqqGX4Bu9cci8xHZNT+Um1gWKqCsAzcC0d95rcKMU5WBg6YRUcHyV0HZKQ=="
+ },
+ "pg-int8": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz",
+ "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw=="
+ },
+ "pg-pool": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.2.2.tgz",
+ "integrity": "sha512-ORJoFxAlmmros8igi608iVEbQNNZlp89diFVx6yV5v+ehmpMY9sK6QgpmgoXbmkNaBAx8cOOZh9g80kJv1ooyA=="
+ },
+ "pg-protocol": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.3.0.tgz",
+ "integrity": "sha512-64/bYByMrhWULUaCd+6/72c9PMWhiVFs3EVxl9Ct6a3v/U8+rKgqP2w+kKg/BIGgMJyB+Bk/eNivT32Al+Jghw=="
+ },
+ "pg-types": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz",
+ "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==",
+ "requires": {
+ "pg-int8": "1.0.1",
+ "postgres-array": "~2.0.0",
+ "postgres-bytea": "~1.0.0",
+ "postgres-date": "~1.0.4",
+ "postgres-interval": "^1.1.0"
+ }
+ },
+ "pgpass": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.4.tgz",
+ "integrity": "sha512-YmuA56alyBq7M59vxVBfPJrGSozru8QAdoNlWuW3cz8l+UX3cWge0vTvjKhsSHSJpo3Bom8/Mm6hf0TR5GY0+w==",
+ "requires": {
+ "split2": "^3.1.1"
+ }
+ },
+ "postgres-array": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz",
+ "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA=="
+ },
+ "postgres-bytea": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz",
+ "integrity": "sha1-AntTPAqokOJtFy1Hz5zOzFIazTU="
+ },
+ "postgres-date": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz",
+ "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q=="
+ },
+ "postgres-interval": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz",
+ "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==",
+ "requires": {
+ "xtend": "^4.0.0"
+ }
+ },
+ "proxy-addr": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz",
+ "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==",
+ "requires": {
+ "forwarded": "~0.1.2",
+ "ipaddr.js": "1.9.1"
+ }
+ },
+ "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"
+ }
+ },
+ "retry-as-promised": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/retry-as-promised/-/retry-as-promised-3.2.0.tgz",
+ "integrity": "sha512-CybGs60B7oYU/qSQ6kuaFmRd9sTZ6oXSc0toqePvV74Ac6/IFZSI1ReFQmtCN+uvW1Mtqdwpvt/LGOiCBAY2Mg==",
+ "requires": {
+ "any-promise": "^1.3.0"
+ }
+ },
+ "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=="
+ },
+ "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": {
+ "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"
+ },
+ "dependencies": {
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+ }
+ }
+ },
+ "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=="
+ }
+ }
+ },
+ "sequelize": {
+ "version": "6.3.5",
+ "resolved": "https://registry.npmjs.org/sequelize/-/sequelize-6.3.5.tgz",
+ "integrity": "sha512-MiwiPkYSA8NWttRKAXdU9h0TxP6HAc1fl7qZmMO/VQqQOND83G4nZLXd0kWILtAoT9cxtZgFqeb/MPYgEeXwsw==",
+ "requires": {
+ "debug": "^4.1.1",
+ "dottie": "^2.0.0",
+ "inflection": "1.12.0",
+ "lodash": "^4.17.15",
+ "moment": "^2.26.0",
+ "moment-timezone": "^0.5.31",
+ "retry-as-promised": "^3.2.0",
+ "semver": "^7.3.2",
+ "sequelize-pool": "^6.0.0",
+ "toposort-class": "^1.0.1",
+ "uuid": "^8.1.0",
+ "validator": "^10.11.0",
+ "wkx": "^0.5.0"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz",
+ "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==",
+ "requires": {
+ "ms": "2.1.2"
+ }
+ },
+ "semver": {
+ "version": "7.3.2",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz",
+ "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ=="
+ }
+ }
+ },
+ "sequelize-pool": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/sequelize-pool/-/sequelize-pool-6.1.0.tgz",
+ "integrity": "sha512-4YwEw3ZgK/tY/so+GfnSgXkdwIJJ1I32uZJztIEgZeAO6HMgj64OzySbWLgxj+tXhZCJnzRfkY9gINw8Ft8ZMg=="
+ },
+ "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=="
+ },
+ "split2": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz",
+ "integrity": "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==",
+ "requires": {
+ "readable-stream": "^3.0.0"
+ },
+ "dependencies": {
+ "readable-stream": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
+ "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
+ "requires": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ }
+ }
+ }
+ },
+ "statuses": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
+ "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow="
+ },
+ "string_decoder": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "requires": {
+ "safe-buffer": "~5.1.0"
+ }
+ },
+ "toidentifier": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
+ "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw=="
+ },
+ "toposort-class": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/toposort-class/-/toposort-class-1.0.1.tgz",
+ "integrity": "sha1-f/0feMi+KMO6Rc1OGj9e4ZO9mYg="
+ },
+ "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="
+ },
+ "util-deprecate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
+ },
+ "utils-merge": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
+ "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
+ },
+ "uuid": {
+ "version": "8.3.1",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.1.tgz",
+ "integrity": "sha512-FOmRr+FmWEIG8uhZv6C2bTgEVXsHk08kE7mPlrBbEe+c3r9pjceVPgupIfNIhc4yx55H69OXANrUaSuu9eInKg=="
+ },
+ "validator": {
+ "version": "10.11.0",
+ "resolved": "https://registry.npmjs.org/validator/-/validator-10.11.0.tgz",
+ "integrity": "sha512-X/p3UZerAIsbBfN/IwahhYaBbY68EN/UQBWHtsbXGT5bfrH/p4NQzUCG1kF/rtKaNpnJ7jAu6NGTdSNtyNIXMw=="
+ },
+ "vary": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
+ "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
+ },
+ "wkx": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/wkx/-/wkx-0.5.0.tgz",
+ "integrity": "sha512-Xng/d4Ichh8uN4l0FToV/258EjMGU9MGcA0HV2d9B/ZpZB3lqQm7nkOdZdm5GhKtLLhAE7PiVQwN4eN+2YJJUg==",
+ "requires": {
+ "@types/node": "*"
+ }
+ },
+ "xtend": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
+ "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="
+ }
+ }
+}
diff --git a/server/package.json b/server/package.json
new file mode 100644
index 0000000..3677c9d
--- /dev/null
+++ b/server/package.json
@@ -0,0 +1,27 @@
+{
+ "name": "fancytodo",
+ "version": "1.0.0",
+ "description": "ToDo App",
+ "main": "index.js",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1",
+ "dev": "nodemon app.js",
+ "start": "node app.js"
+ },
+ "keywords": [],
+ "author": "torianyap",
+ "license": "ISC",
+ "dependencies": {
+ "axios": "^0.21.0",
+ "bcryptjs": "^2.4.3",
+ "cors": "^2.8.5",
+ "express": "^4.17.1",
+ "google-auth-library": "^6.1.3",
+ "jsonwebtoken": "^8.5.1",
+ "pg": "^8.4.2",
+ "sequelize": "^6.3.5"
+ },
+ "devDependencies": {
+ "dotenv": "^8.2.0"
+ }
+}
diff --git a/server/readme.md b/server/readme.md
deleted file mode 100644
index 3aadcc2..0000000
--- a/server/readme.md
+++ /dev/null
@@ -1 +0,0 @@
-# FancyTodo Server
\ No newline at end of file
diff --git a/server/routes/index.js b/server/routes/index.js
new file mode 100644
index 0000000..d7737c4
--- /dev/null
+++ b/server/routes/index.js
@@ -0,0 +1,10 @@
+const route = require('express').Router()
+const todoRoute = require('./todoRoute')
+const userRoute = require('./userRoute')
+const weatherRoute = require('./weatherRoute')
+
+route.use('/todos', todoRoute)
+route.use('/users', userRoute)
+route.use('/weathers', weatherRoute)
+
+module.exports = route
\ No newline at end of file
diff --git a/server/routes/todoRoute.js b/server/routes/todoRoute.js
new file mode 100644
index 0000000..e4cb43e
--- /dev/null
+++ b/server/routes/todoRoute.js
@@ -0,0 +1,18 @@
+const route = require('express').Router()
+const ToDoController = require('../controllers/todoController')
+const authenticate = require('../middlewares/authentication')
+const authorization = require('../middlewares/authorization')
+
+route.use(authenticate)
+
+route.post('/', ToDoController.create)
+route.get('/', ToDoController.read)
+
+route.use('/:id', authorization)
+
+route.get('/:id', ToDoController.findOne)
+route.put('/:id', ToDoController.update)
+route.patch('/:id', ToDoController.finish)
+route.delete('/:id', ToDoController.delete)
+
+module.exports = route
\ No newline at end of file
diff --git a/server/routes/userRoute.js b/server/routes/userRoute.js
new file mode 100644
index 0000000..3ad946c
--- /dev/null
+++ b/server/routes/userRoute.js
@@ -0,0 +1,8 @@
+const route = require('express').Router()
+const UserController = require('../controllers/userController')
+
+route.post('/register', UserController.register)
+route.post('/login', UserController.login)
+route.post('/googleLogin', UserController.googleLogin)
+
+module.exports = route
\ No newline at end of file
diff --git a/server/routes/weatherRoute.js b/server/routes/weatherRoute.js
new file mode 100644
index 0000000..2877a81
--- /dev/null
+++ b/server/routes/weatherRoute.js
@@ -0,0 +1,6 @@
+const route = require('express').Router()
+const WeatherController = require('../controllers/weatherController')
+
+route.get('/', WeatherController.currentWeather)
+
+module.exports = route
\ No newline at end of file