From 1dc6cd838ddc5b1b2b6fa76eadd503032a56bdc1 Mon Sep 17 00:00:00 2001 From: "Glitch (fp-start)" Date: Wed, 9 Oct 2019 03:26:02 +0000 Subject: [PATCH 1/2] =?UTF-8?q?=F0=9F=91=B9=F0=9F=8D=B5=20Updated=20with?= =?UTF-8?q?=20Glitch?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 3 + .glitch-assets | 11 + README.md | 51 +- ...37e22a2%2FfaviconCheck.ico?v=1570204892951 | Bin 0 -> 318 bytes package.json | 23 + proposal.md | 8 + public/css/index.css | 21 + public/index.html | 60 + public/js/calendarScripts.js | 123 ++ public/js/loginScripts.js | 67 + public/js/mainScripts.js | 222 +++ public/js/meetingScripts.js | 130 ++ public/js/messageScripts.js | 132 ++ public/mainview.html | 104 ++ public/meetingmakerview.html | 103 ++ public/meetingtasksview.html | 118 ++ public/messagesview.html | 100 ++ server.improved.js | 357 ++++ shrinkwrap.yaml | 1431 +++++++++++++++++ student.db | Bin 0 -> 49152 bytes 20 files changed, 3020 insertions(+), 44 deletions(-) create mode 100755 .gitignore create mode 100644 .glitch-assets mode change 100644 => 100755 README.md create mode 100644 assets/3a7a7745-805f-4bc9-9091-f891637e22a2%2FfaviconCheck.ico?v=1570204892951 create mode 100755 package.json create mode 100644 proposal.md create mode 100644 public/css/index.css create mode 100755 public/index.html create mode 100644 public/js/calendarScripts.js create mode 100644 public/js/loginScripts.js create mode 100644 public/js/mainScripts.js create mode 100644 public/js/meetingScripts.js create mode 100644 public/js/messageScripts.js create mode 100644 public/mainview.html create mode 100644 public/meetingmakerview.html create mode 100644 public/meetingtasksview.html create mode 100644 public/messagesview.html create mode 100644 server.improved.js create mode 100644 shrinkwrap.yaml create mode 100644 student.db diff --git a/.gitignore b/.gitignore new file mode 100755 index 00000000..57195033 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +*.DS_Store +node_modules/ +package-lock.json diff --git a/.glitch-assets b/.glitch-assets new file mode 100644 index 00000000..2b950f60 --- /dev/null +++ b/.glitch-assets @@ -0,0 +1,11 @@ +{"name":"image.png","date":"2019-09-09T03:21:27.676Z","url":"https://cdn.glitch.com/e0bc9286-a871-443e-a57f-2e27d07bd89e%2Fimage.png","type":"image/png","size":54851,"imageWidth":558,"imageHeight":601,"thumbnail":"https://cdn.glitch.com/e0bc9286-a871-443e-a57f-2e27d07bd89e%2Fthumbnails%2Fimage.png","thumbnailWidth":307,"thumbnailHeight":330,"uuid":"gyBKnc2D2mLyQ8up"} +{"name":"favicon (2).ico","date":"2019-09-14T16:22:13.186Z","url":"https://cdn.glitch.com/5cd46ecf-8f21-44d2-941d-1799ff06883e%2Ffavicon%20(2).ico","type":"image/x-icon","size":318,"imageWidth":16,"imageHeight":16,"thumbnail":"https://cdn.glitch.com/5cd46ecf-8f21-44d2-941d-1799ff06883e%2Ffavicon%20(2).ico","thumbnailWidth":16,"thumbnailHeight":16,"uuid":"s1BVKqfyil2B9f2m"} +{"uuid":"s1BVKqfyil2B9f2m","deleted":true} +{"name":"favicon-a3.ico","date":"2019-09-14T16:26:08.998Z","url":"https://cdn.glitch.com/5cd46ecf-8f21-44d2-941d-1799ff06883e%2Ffavicon-a3.ico","type":"image/x-icon","size":318,"imageWidth":16,"imageHeight":16,"thumbnail":"https://cdn.glitch.com/5cd46ecf-8f21-44d2-941d-1799ff06883e%2Ffavicon-a3.ico","thumbnailWidth":16,"thumbnailHeight":16,"uuid":"2XFISO0ezBjOTcjZ"} +{"uuid":"gyBKnc2D2mLyQ8up","deleted":true} +{"name":"GradeTable.PNG","date":"2019-09-15T22:37:10.243Z","url":"https://cdn.glitch.com/5cd46ecf-8f21-44d2-941d-1799ff06883e%2FGradeTable.PNG","type":"image/png","size":9468,"imageWidth":1670,"imageHeight":221,"thumbnail":"https://cdn.glitch.com/5cd46ecf-8f21-44d2-941d-1799ff06883e%2Fthumbnails%2FGradeTable.PNG","thumbnailWidth":330,"thumbnailHeight":44,"uuid":"ilUkSUhHfqYIw109"} +{"name":"favicon (2).ico","date":"2019-10-04T15:59:56.794Z","url":"https://cdn.glitch.com/3a7a7745-805f-4bc9-9091-f891637e22a2%2Ffavicon%20(2).ico","type":"image/x-icon","size":318,"imageWidth":16,"imageHeight":16,"thumbnail":"https://cdn.glitch.com/3a7a7745-805f-4bc9-9091-f891637e22a2%2Ffavicon%20(2).ico","thumbnailWidth":16,"thumbnailHeight":16,"uuid":"4riQPjUDuKjm5fQ4"} +{"uuid":"ilUkSUhHfqYIw109","deleted":true} +{"uuid":"2XFISO0ezBjOTcjZ","deleted":true} +{"uuid":"4riQPjUDuKjm5fQ4","deleted":true} +{"name":"faviconCheck.ico","date":"2019-10-04T16:01:32.951Z","url":"https://cdn.glitch.com/3a7a7745-805f-4bc9-9091-f891637e22a2%2FfaviconCheck.ico","type":"image/x-icon","size":318,"imageWidth":16,"imageHeight":16,"thumbnail":"https://cdn.glitch.com/3a7a7745-805f-4bc9-9091-f891637e22a2%2FfaviconCheck.ico","thumbnailWidth":16,"thumbnailHeight":16,"uuid":"wE3HPcJdaSgbPE5C"} diff --git a/README.md b/README.md old mode 100644 new mode 100755 index 39e2788d..b63613b3 --- a/README.md +++ b/README.md @@ -1,51 +1,14 @@ -# cs4241-FinalProject +## Scheduler -For your final project, you'll implement a course project that exhibits your mastery of the course materials. -Similar to A4, this project gives you an opportunity to be creative and to pursue individual research and learning. +https://fp-start.glitch.me -## General description +This is an application that allows for users to schedule meetings, and assign tasks to users for each meeting that they have created. -Your project should consist of a complete Web application, exhibiting facets of the three main sections of the course material: +Features of the application include: -- Static Web page content and design. You should have a project that is accessible, easily navigable, and features significant content. -- Dynamic behavior implemented with JavaScript. -- Server-side programming *using Node.js*. Typically this will take the form of some sort of persistent data, authentication, and possibly server-side computation. +- Passport authentication for users +- Seperate pages for allowing users to view meetings, make meetings, assign tasks, and view their own messages and tasks. +- Messages that are automatically stored for users to notify them when they are assigned or unassigned from a task. -Additionally, you should incorporate features that you independently research, design, and implement for your project. -## Project ideation -Excellent projects serve someone/some group; for this assignment you need to define your users and stakeholders. I encourage you to identify projects that will have impact, either artistically, politically, or in terms of productivity. Consider creating something useful for a cause or hobby you care about. - -## Logistics - -### Team size -Students are encouraged to work in teams of 2-5 students for the project. This will allow you to build a good project without expending an excessive amount of effort. While I would expect a team of four or five students to produce a project with more features, I expect a every team's work to exhibit all of the required facets described above. - -### Deliverables - -__Proposal:__ -Provide an outline of your project direction and the names of the team members. -The outline should have enough detail so that staff can determine if it meets the minimum expectations, or if it goes too far to be reasonable by the deadline. -This file must be named proposal.md so we can find it. -Submit a PR to turn it in by Monday, September 30th, before class - -There are no other scheduled checkpoints for your project. -You must be done in time to present before the final project demo day (October 10th). - -#### Turning in Your Outline / Project - -**NOTE: code is due before the project presentation day due to the end of term / grading schedule constraints** -Submit a second PR on the final project repo to turn in your app and code. - -Deploy your app, in the form of a webpage, to Glitch/Heroku/Digital Ocean or some other service. -Folks on the same team do not need to post the same webpage, but must instead clearly state who is on the team in their proposal. -(Staff will use the proposal to build the grading sheet.) - -## Final Presentation - -Presentations will occur during the final day of class. - -## FAQs - -- **Can I use XYZ framework?** You can use any web-based frameworks or tools available, but for your server programming you need to use node.js. diff --git a/assets/3a7a7745-805f-4bc9-9091-f891637e22a2%2FfaviconCheck.ico?v=1570204892951 b/assets/3a7a7745-805f-4bc9-9091-f891637e22a2%2FfaviconCheck.ico?v=1570204892951 new file mode 100644 index 0000000000000000000000000000000000000000..998d1b29d1a4f4a162a0973d916efa0acd0368ab GIT binary patch literal 318 zcmcJJI}XA?3`CzLB-*8+MyYZ`+T4nxq@_!N2xSV2&5W~=fF7@&#~)cc5PGE) + + + CS4241 Final Project + + + + + + + + + + + +
+
+

Scheduler App

+

Login Page

+
+
+ +
+ +
+ + + + + + +
+ Sign in above if you already have registered as a user, or register as a new user below. +
+
+ + + + +
+ Your username must be unique from any other user. +
+ + + + + +
+
+ +
+
+
+

+ Created by Tom Graham, Peter Jankowski, and Rosana Pochat +

+
+
+ + diff --git a/public/js/calendarScripts.js b/public/js/calendarScripts.js new file mode 100644 index 00000000..9c95c0e4 --- /dev/null +++ b/public/js/calendarScripts.js @@ -0,0 +1,123 @@ +// Here is where the js will go for allowing users to add, remove, and display meetings by date + +let isHidden = true; + +const hide = function( e ) { + e.preventDefault(); + document.getElementById("tablePrint").innerHTML = '
'; + isHidden = true; +} + +// Get all meetings from the database for this date +const view = function( e ) { + e.preventDefault(); + + const dateInput = document.querySelector( '#enteredDate' ); + if (dateInput.value !== "") { + let meetingsArray; + const json = { date: dateInput.value, }, + body = JSON.stringify( json ); + + fetch( '/viewMyMeetings', { + method:'POST', + credentials: 'include', + headers: { 'Content-Type': 'application/json' }, + body: body + }) + .then( function( response ) { + + // Fetch all tasks for this meeting in the database to add to a table + response.json().then((data) => { + meetingsArray = data.meetingsArray; + let numTasks = meetingsArray.length; + let myTable = ''; + myTable += ""; + myTable += ""; + myTable += ""; + for (let i = 0; i < numTasks; i++) { // Make the table with one row per task + myTable += ""; + myTable += ""; + myTable += ""; + myTable += ""; + } + myTable += "
Meeting Name:Meeting Date:Meeting Creator:Meeting Details:
" + meetingsArray[i].name + "" + meetingsArray[i].date + "" + meetingsArray[i].username + "" + meetingsArray[i].details + "
"; + document.getElementById("tablePrint").innerHTML = myTable; + isHidden = false; + }); + }); + return false; + } else { + alert("Enter a full date to see meetings on that date"); + } +} + +const submit = function( e ) { // Submit request for a new meeting + e.preventDefault() + + const nameInput = document.querySelector( '#meetingName' ), + detailsInput = document.querySelector( '#meetingDetails' ), + dateInput = document.querySelector('#enteredDate'), + json = { name: nameInput.value, date: dateInput.value, details: detailsInput.value }, + body = JSON.stringify( json ); + + fetch( '/submitMeeting', { //This meeting will be made originally with no tasks in it, just details + method:'POST', + body: JSON.stringify( json ), + headers: { 'Content-Type': 'application/json' }, + credentials: 'include' + }) + .then( function( response ) { + // Update the task list for the user + response.json().then((data) => { + //act now that the new meeting has been created + if (data.meetingAdded) { + alert("Meeting created"); + } else { + alert("You already have a meeting of that name"); + } + if (!isHidden) { + view(e); + } + }); + }); + return false; +} + +const remove = function( e ) { // Delete a task with a specified id number + // prevent default form action from being carried out + e.preventDefault(); + + const nameInput = document.querySelector( '#meetingName' ), + dateInput = document.querySelector('#enteredDate'), + json = { name: nameInput.value, date: dateInput.value }, + body = JSON.stringify( json ); + + fetch( '/removeMeeting', { + method:'DELETE', + body: JSON.stringify( json ), + headers: { 'Content-Type': 'application/json' }, + credentials: 'include' + }) + .then( function( response ) { + // Simply redisplay the table on the response after the deletion occurs + // if it is not currently hidden + response.json().then((data) => { + document.getElementById("tablePrint").innerHTML = '
'; + if (!isHidden) { + view(e); + } + }); + }); + return false; +} + +window.onload = function() { // Link each button to its respective function + const viButton = document.querySelector( '#viewButton' ), + siButton = document.querySelector( '#submitButton' ), + hiButton = document.querySelector( '#hideButton' ), + reButton = document.querySelector( '#removeButton' ); + viButton.onclick = view; + siButton.onclick = submit; + hiButton.onclick = hide; + reButton.onclick = remove; +} diff --git a/public/js/loginScripts.js b/public/js/loginScripts.js new file mode 100644 index 00000000..6dfa64e9 --- /dev/null +++ b/public/js/loginScripts.js @@ -0,0 +1,67 @@ +// Some Javascript code here, to run on the front end on the login page + +window.onload = function() { // Link each button to its respective function + const newButton = document.querySelector( '#signupButton' ); + newButton.onclick = signUpFunc; + const logButton = document.querySelector( '#loginButton' ); + logButton.onclick = loginFunc; +} + +const signUpFunc = function( e ) { + e.preventDefault(); + // Handles a new user signing up + + const nameInput = document.querySelector( '#newname' ), + passInput = document.querySelector( '#newpass' ), + json = { username: nameInput.value, password: passInput.value}, + body = JSON.stringify( json ); + console.log(body); + + fetch( '/signUp', { + method:'POST', + body + }) + .then( function( response ) { + // Inform the user of what the letter grade of the student is, and + // refresh the table view with the new student in it + response.json().then((data) => { + console.log(data); + if (data.userAdded === true) { + alert("New user added: " + nameInput.value); + } else { + alert("That username has already been taken!"); + } + }) + return false; + }) +} + +const loginFunc = function( e ) { // Handles logins + e.preventDefault(); + const nameInput = document.querySelector( '#loginName' ), + passInput = document.querySelector( '#pass' ), + json = { username: nameInput.value, password: passInput.value}, + body = JSON.stringify( json ); + console.log(body); + + fetch( '/login', { + method:'POST', + body: body, + headers: { 'Content-Type': 'application/json' } + }) + .then( function( response ) { + // Inform the user of what the letter grade of the student is, and + // refresh the table view with the new student in it + console.log( response.status ); + if (response.status === 200 || response.status === 304) { + loginRedir(); + } else { + alert("Login failed!"); + } + return false; + }) +} + +const loginRedir = function( e ) { + window.location.replace('https://fp-start.glitch.me/mainview.html'); +} diff --git a/public/js/mainScripts.js b/public/js/mainScripts.js new file mode 100644 index 00000000..5ef29d7c --- /dev/null +++ b/public/js/mainScripts.js @@ -0,0 +1,222 @@ +// Here is where the js will go for allowing users to add, remove, and display meetings by date + +let isHidden = true; +let isHiddenTasks = true; + +const drawCal = function (date) { + let dateInput = document.querySelector( '#enteredDate' ); + if (date) { + dateInput.value = date; + } + const calendarHeader = document.querySelector( '#calendarHeader' ); + const month = dateInput.value.substring(5, 7); + const yearStr = dateInput.value.substring(0, 4); + let monthStr; + let dayLimit; + if (month === "01") { + monthStr = "January"; + dayLimit = 31; + } else if (month === "02") { + monthStr = "February"; + dayLimit = 28; + // Take leap years into account + if (Number(yearStr) % 4 === 0 && (Number(yearStr) % 100 !== 0 || Number(yearStr) % 400 === 0)) { + dayLimit++; + } + } else if (month === "03") { + monthStr = "March"; + dayLimit = 31; + } else if (month === "04") { + monthStr = "April"; + dayLimit = 30; + } else if (month === "05") { + monthStr = "May"; + dayLimit = 31; + } else if (month === "06") { + monthStr = "June"; + dayLimit = 30; + } else if (month === "07") { + monthStr = "July"; + dayLimit = 31; + } else if (month === "08") { + monthStr = "August"; + dayLimit = 31; + } else if (month === "09") { + monthStr = "September"; + dayLimit = 30; + } else if (month === "10") { + monthStr = "October"; + dayLimit = 31; + } else if (month === "11") { + monthStr = "November"; + dayLimit = 30; + } else if (month === "12") { + monthStr = "December"; + dayLimit = 31; + } + calendarHeader.innerHTML = "Calendar: " + monthStr + " " + yearStr; + const canvas = document.getElementById('myCanvas'); + // Get our 2D drawing context + const ctx = canvas.getContext('2d'); + const draw = function () { + ctx.fillStyle = '#212529'; + ctx.fillRect(0, 0, canvas.width, canvas.height); + ctx.fillStyle = '#ffffff'; + // Draw the lines on the calendar + for (let i = 100; i < 700; i+=100) { + ctx.fillRect(i, 0, 1, canvas.height); + ctx.fillRect(0, i, canvas.width, 1); + } + ctx.font = "30px Times New Roman"; + // Fill the top row with day names + ctx.fillText("Sun", 25, 75); + ctx.fillText("Mon", 125, 75); + ctx.fillText("Tue", 225, 75); + ctx.fillText("Wed", 325, 75); + ctx.fillText("Thu", 425, 75); + ctx.fillText("Fri", 525, 75); + ctx.fillText("Sat", 625, 75); + + // Now start filling in the days of the month + let date = new Date(dateInput.value + "T00:00:00"); + // Get the weekday of the first day of the month + let firstDay = date.getDate(); + let weekday = date.getDay(); + while (firstDay % 7 != 1) { + firstDay--; + weekday--; + if (weekday === -1) { + weekday = 6; + } + } + // weekday now corresponds to the day in the week for the first day of the month + let currentHeight = 175; + let currentX = 25 + (100 * weekday); // Set the x position based on the starting weekday + for (let i = 1; i < dayLimit+1; i++) { + if (i === date.getDate()) { + ctx.fillStyle = '#ff4000'; + ctx.fillText(i, currentX, currentHeight); + ctx.fillStyle = '#ffffff'; + } else { + ctx.fillText(i, currentX, currentHeight); + } + currentX += 100; + if (currentX > 625) { + currentX = 25; + currentHeight += 100; + } + } + } + draw(); +} + +// Hide the table of tasks +const hide = function( e ) { + e.preventDefault(); + document.getElementById("tablePrintM").innerHTML = '
'; + isHiddenTasks = true; +} + +// Get all meetings from the database for this date and this user +const view = function(e) { + e.preventDefault(); + + const dateInput = document.querySelector( '#enteredDate' ); + if (dateInput.value !== "") { + let meetingsArray; + const json = { date: dateInput.value, }, + body = JSON.stringify( json ); + + fetch( '/viewMeetings', { + method:'POST', + credentials: 'include', + headers: { 'Content-Type': 'application/json' }, + body: body + }) + .then( function( response ) { + + // Fetch all tasks for this meeting in the database to add to a table + console.log( response ); + response.json().then((data) => { + meetingsArray = data.meetingsArray; + let numTasks = meetingsArray.length; + let myTable = ''; + myTable += ""; + myTable += ""; + myTable += ""; + for (let i = 0; i < numTasks; i++) { // Make the table with one row per task + myTable += ""; + myTable += ""; + myTable += ""; + myTable += ""; + } + myTable += "
Meeting Name:Meeting Date:Meeting Creator:Meeting Details:
" + meetingsArray[i].name + "" + meetingsArray[i].date + "" + meetingsArray[i].username + "" + meetingsArray[i].details + "
"; + document.getElementById("tablePrint").innerHTML = myTable; + isHidden = false; + drawCal(); + }); + }); + return false; + } else { + alert("Enter a full date to see meetings on that date that you have made"); + } +} + +const viewTasks = function(e) { + e.preventDefault(); + let tasksArray; + + const nameInput = document.querySelector( '#meetingname' ), + json = { meeting: nameInput.value, }, + body = JSON.stringify( json ); + + fetch( '/viewTasks', { + method:'POST', + credentials: 'include', + headers: { 'Content-Type': 'application/json' }, + body: body + }) + .then( function( response ) { + + // Fetch all tasks for this meeting in the database to add to a table + console.log( response ); + response.json().then((data) => { + console.log( data ); + if (data.tasksArray) { + tasksArray = data.tasksArray; + let numTasks = tasksArray.length; + let myTable = ''; + myTable += ""; + myTable += ""; + myTable += ""; + for (let i = 0; i < numTasks; i++) { // Make the table with one row per task + myTable += ""; + myTable += ""; + myTable += ""; + myTable += ""; + } + myTable += "
' + nameInput.value + '
Task Name:Assigned to:Date:Details:
" + tasksArray[i].taskName + "" + tasksArray[i].assigneeName + "" + tasksArray[i].date + "" + tasksArray[i].details + "
"; + document.getElementById("tablePrintM").innerHTML = myTable; + isHidden = false; + } else { + alert("You have not made a meeting with the name that you entered.") + } + }); + }); + return false; +} + +window.onload = function() { // Link each button to its respective function + const viButton = document.querySelector( '#viewButton' ), + viTaButton = document.querySelector(' #viewButtonM '), + hiButton = document.querySelector( '#hideButtonM' ); + viButton.onclick = view; + hiButton.onclick = hide; + viTaButton.onclick = viewTasks; + let today = new Date(); + let day = String(today.getDate()).padStart(2, '0'); + let month = String(today.getMonth() + 1).padStart(2, '0'); //January is 0! + let year = today.getFullYear(); + today = year + '-' + month + '-' + day; + drawCal(today); +} diff --git a/public/js/meetingScripts.js b/public/js/meetingScripts.js new file mode 100644 index 00000000..f5de7c74 --- /dev/null +++ b/public/js/meetingScripts.js @@ -0,0 +1,130 @@ +// Code specifically for viewing and managing a task list for each meeting + +let isHidden = true; + +// Each task has one meeting and one assigned user that it relates to, although names can be shared +// Each meeting has a unique ID, and is findable as a unique combination for the meeting maker's username and the MeetingName + +const submitTask = function( e ) { // Submit request for a new task for a user + // prevent default form action from being carried out + e.preventDefault() + + const nameInput = document.querySelector( '#meetingname' ), + userInput = document.querySelector( '#assigneename' ), + taskInput = document.querySelector( '#taskname' ), + detailsInput = document.querySelector( '#details' ), + json = { meeting: nameInput.value, task: taskInput.value, name: userInput.value, details: detailsInput.value }, + body = JSON.stringify( json ); + + fetch( '/submitTask', { //Note that on the server side, this will also give the assigned user a new message about their task + method:'POST', + body: JSON.stringify( json ), + headers: { 'Content-Type': 'application/json' }, + credentials: 'include' + }) + .then( function( response ) { + // Update the task list for the user + console.log( response ); + response.json().then((data) => { + document.getElementById("tablePrint").innerHTML = '
'; + if (!isHidden) { + viewMeetingTasks(e); + } + if (data.taskAdded === false) { + alert("Cannot add task: Meeting not found") + } + }); + }); + return false; +} + +const viewMeetingTasks = function(e) { + e.preventDefault(); + let tasksArray; + + const nameInput = document.querySelector( '#meetingname' ), + json = { meeting: nameInput.value, }, + body = JSON.stringify( json ); + + fetch( '/viewTasks', { + method:'POST', + credentials: 'include', + headers: { 'Content-Type': 'application/json' }, + body: body + }) + .then( function( response ) { + + // Fetch all tasks for this meeting in the database to add to a table + console.log( response ); + response.json().then((data) => { + console.log(data); + if (data.tasksArray) { + tasksArray = data.tasksArray; + let numTasks = tasksArray.length; + let myTable = ''; + myTable += ""; + myTable += ""; + myTable += ""; + for (let i = 0; i < numTasks; i++) { // Make the table with one row per task + myTable += ""; + myTable += ""; + myTable += ""; + myTable += ""; + } + myTable += "
' + nameInput.value + '
Task Name:Assigned to:Date:Details:
" + tasksArray[i].taskName + "" + tasksArray[i].assigneeName + "" + tasksArray[i].date + "" + tasksArray[i].details + "
"; + document.getElementById("tablePrint").innerHTML = myTable; + isHidden = false; + } else { + alert("You have not made a meeting with the name that you entered.") + } + }); + }); + return false; +} + +const hide = function( e ) { + e.preventDefault(); + document.getElementById("tablePrint").innerHTML = '
'; + isHidden = true; +} + +const deleteTask = function( e ) { // Delete a task with a specified id number + // prevent default form action from being carried out + e.preventDefault(); + + const userInput = document.querySelector( '#assigneename' ), + taskInput = document.querySelector( '#taskname' ), + nameInput = document.querySelector( '#meetingname' ), + json = { name: userInput.value, meeting: nameInput.value, task: taskInput.value, }; + + fetch( '/removeTask', { + method:'DELETE', + body: JSON.stringify( json ), + headers: { 'Content-Type': 'application/json' }, + credentials: 'include' + }) + .then( function( response ) { + // Simply redisplay the table on the response after the deletion occurs + // if it is not hidden + console.log( response ); + response.json().then((data) => { + console.log(data); + document.getElementById("tablePrint").innerHTML = '
'; + if (!isHidden) { + viewMeetingTasks(e); + } + }); + }); + return false; +} + +window.onload = function() { // Link each button to its respective function + const inButton = document.querySelector( '#inputButton' ); + const viButton = document.querySelector( '#viewButton' ); + const deButton = document.querySelector( '#removeButton' ); + const hiButton = document.querySelector( '#hideButton' ); + hiButton.onclick = hide; + viButton.onclick = viewMeetingTasks; + inButton.onclick = submitTask; + deButton.onclick = deleteTask; +} diff --git a/public/js/messageScripts.js b/public/js/messageScripts.js new file mode 100644 index 00000000..9a3f2dcd --- /dev/null +++ b/public/js/messageScripts.js @@ -0,0 +1,132 @@ +// Code specifically for messages and tasks that the user has received + +// Note that we don't allow for users to send messages themselves, but messages will be automatically +// generated for them whenever they are assigned a task in another meeting +let isHidden = true; +let isHiddenTasks = true; + +const submit = function( e ) { // Submit request for a new or updated student's grades + // prevent default form action from being carried out + e.preventDefault() + + const nameInput = document.querySelector( '#yourname' ), + gradeInput = document.querySelector( '#yourgrade' ), + json = { yourname: nameInput.value, yourgrade: gradeInput.value }, + body = JSON.stringify( json ); + + fetch( '/submit', { + method:'POST', + body: JSON.stringify( json ), + headers: { 'Content-Type': 'application/json' }, + credentials: 'include' + }) + .then( function( response ) { + // Inform the user of what the letter grade of the student is, and + // refresh the table view with the new student in it + console.log( response ); + response.json().then((data) => { + document.getElementById("tablePrint").innerHTML = '
'; + if (!isHidden) { + view(e); + } + alert("The grade of this student is : " + + data.numericGrade + " (" + data.letterGrade + ")"); + }); + }); + return false; +} + +// Get all messages from the db for this user +const view = function(e) { + e.preventDefault(); + + fetch( '/viewMessages', { + method:'POST', + credentials: 'include', + headers: { 'Content-Type': 'application/json' } + }) + .then( function( response ) { + + // Fetch all students in the database to add to a table + console.log( response ); + response.json().then((data) => { + console.log(data); + let messagesArray = data.messagesArray; + let num = messagesArray.length; + let myTable = ''; + myTable += ""; + for (let i = 0; i < num; i++) { // Make the table with one row per student + myTable += ""; + myTable += ""; + } + myTable += "
From:Contents:
" + messagesArray[i].sender + "" + messagesArray[i].contents + "
"; + document.getElementById("tablePrint").innerHTML = myTable; + isHidden = false; + }); + }); + return false; +} + +const hide = function( e ) { + e.preventDefault(); + document.getElementById("tablePrint").innerHTML = '
'; + isHidden = true; +} + +// Get all tasks from the db from this user +const viewMeetingTasks = function(e) { + e.preventDefault(); + let tasksArray; + + //const //nameInput = document.querySelector( '#meetingname' ), + const json = { }, + body = JSON.stringify( json ); + + fetch( '/viewMyTasks', { + method:'POST', + credentials: 'include', + headers: { 'Content-Type': 'application/json' }, + body: body + }) + .then( function( response ) { + + // Fetch all tasks for this meeting in the database to add to a table + console.log( response ); + response.json().then((data) => { + console.log(data); + tasksArray = data.tasksArray; + let numTasks = tasksArray.length; + let myTable = ''; + myTable += "" + myTable += "" + myTable += ""; + for (let i = 0; i < numTasks; i++) { // Make the table with one row per task + myTable += ""; + myTable += ""; + myTable += ""; + myTable += ""; + } + myTable += "
Task Name:Meeting:Date:Details:
" + tasksArray[i].taskName + "" + tasksArray[i].meetingName + "" + tasksArray[i].date + "" + tasksArray[i].details + "
"; + document.getElementById("tableTasksPrint").innerHTML = myTable; + isHiddenTasks = false; + }); + }); + return false; +} + +const hideTasks = function( e ) { + e.preventDefault(); + document.getElementById("tableTasksPrint").innerHTML = '
'; + isHiddenTasks = true; +} + +window.onload = function() { // Link each button to its respective function + const viButton = document.querySelector( '#viewButton' ); + const hiButton = document.querySelector( '#hideButton' ); + hiButton.onclick = hide; + viButton.onclick = view; + const viTaButton = document.querySelector( '#viewTasksButton' ); + const hiTaButton = document.querySelector( '#hideTasksButton' ); + hiTaButton.onclick = hideTasks; + viTaButton.onclick = viewMeetingTasks; +} diff --git a/public/mainview.html b/public/mainview.html new file mode 100644 index 00000000..66ae3dcb --- /dev/null +++ b/public/mainview.html @@ -0,0 +1,104 @@ + + + + CS4241 Final Project + + + + + + + + +
+ +
+ +
+

Home

+
+
+

+ Welcome to your Home Screen. Browser diferent views through the Navigation Bar on top, or look at meetings and tasks you have scheduled down below. +

+ + +
+
+ +

Meeting Information

+ +
+ Show all meetings on the date entered. +
+ +
+ + +
+

Your Tasks

+
+ + +
+ Displays the tasks that you have assigned for the meeting entered. +
+ + +
+
+
+ + +
+

Calendar

+ +
+
+
+
+ +
+
+
+

+ Created by Tom Graham, Peter Jankowski, and Rosana Pochat +

+
+
+ + diff --git a/public/meetingmakerview.html b/public/meetingmakerview.html new file mode 100644 index 00000000..102f5074 --- /dev/null +++ b/public/meetingmakerview.html @@ -0,0 +1,103 @@ + + + + CS4241 Final Project + + + + + + + + +
+ +
+ +
+

Meeting Creator

+
+ +
+ +
+

View Your Meetings

+
+ +
+ Shows all meetings that you have made on a given date. +
+
+

+
+ + +
+
+ + +
+

Create a Meeting

+
+ + +
+ Create a meeting with the entered date, name, and details. +
+
+

+
+ + +
+
+
+ +
+
+ +
+
+
+
  • + Back to main view +
  • +

    + Created by Tom Graham, Peter Jankowski, and Rosana Pochat +

    +
    +
    + + + diff --git a/public/meetingtasksview.html b/public/meetingtasksview.html new file mode 100644 index 00000000..0a0464ec --- /dev/null +++ b/public/meetingtasksview.html @@ -0,0 +1,118 @@ + + + + + + CS4241 Final Project + + + + + + + + + +
    + +
    + +
    +

    Meeting Task Manager

    +
    +
    + You can only view, assign or unassign tasks to meetings that you have created. You can't view or assign tasks to meetings that you did not create. +
    + +
    + +
    + +
    + + +
    + Enter the name of a meeting you have made to see all tasks that you have assigned for it. +
    + + +
    +
    + +
    +
    + + + + + + + + +
    +
    + Add and remove tasks for your meeting with the entered name. Note that to remove a task, + you must specify the user for which the task has been assigned, since multiple users can + be assigned tasks with the same name. +
    +
    + + +
    +
    +
    + + +
    +
    + +
    +
    +
    +
  • + Back to main view +
  • +

    + Created by Tom Graham, Peter Jankowski, and Rosana Pochat +

    +
    +
    + + diff --git a/public/messagesview.html b/public/messagesview.html new file mode 100644 index 00000000..16b75a96 --- /dev/null +++ b/public/messagesview.html @@ -0,0 +1,100 @@ + + + + CS4241 Final Project + + + + + + + + +
    + +
    + + +
    +
    +

    Messages

    +
    +
    + Here you can see messages sent to you as you are assigned and unassigned from tasks. +
    + + +
      +
      +
    +
    + +
    +

    Your Tasks

    +
    +
    + Here you can see all tasks currently assigned to you. +
    + + +
    +
    +
    + + + + +
    +
    +
    +
  • + Back to main view +
  • +

    + Created by Tom Graham, Peter Jankowski, and Rosana Pochat +

    +
    +
    + + diff --git a/server.improved.js b/server.improved.js new file mode 100644 index 00000000..a98066bf --- /dev/null +++ b/server.improved.js @@ -0,0 +1,357 @@ +const express = require('express'), + app = express(), + bodyparser = require( 'body-parser' ), + passport = require('passport'), + LocalStrategy = require('passport-local').Strategy, + sqlite3 = require('sqlite3').verbose(), + session = require('express-session'), + responseTime = require('response-time'), + favicon = require('serve-favicon'), + path = require('path'), + helmet = require('helmet'), + slash = require('express-slash'), + morgan = require('morgan'), + compression = require('compression'); + +// Display all requests made in the server console +app.use(morgan('combined')); +// Compression allows for responses to be sent more quickly +app.use(compression()); +//Adds in the favicon from a local file, rather than a URL +app.use(favicon(path.join(__dirname, 'assets', '3a7a7745-805f-4bc9-9091-f891637e22a2%2FfaviconCheck.ico?v=1570204892951'))); +//Adds in helmet security headers automatically +app.use(helmet()); +// +// Show response time as a response header x-response-time +app.use(responseTime()); +app.use( bodyparser.json() ); +// Set up a user session, authenticated with passport +app.use( session({ secret:'secretSession', resave:false, saveUninitialized:false }) ) +app.use(passport.initialize()); +app.use(passport.session()); + +// Open database in memory +let db = new sqlite3.Database('./student.db', (err) => { + if (err) { + return console.error(err.message); + } + console.log('Connected to the SQlite database.'); +}); + +db.serialize(function(){ + db.run('CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT, password TEXT);'); + db.run('CREATE TABLE IF NOT EXISTS messages (id INTEGER PRIMARY KEY AUTOINCREMENT, sender TEXT, receiver TEXT, contents TEXT);'); + db.run('CREATE TABLE IF NOT EXISTS meetings (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, username TEXT, date TEXT, details TEXT);'); + db.run('CREATE TABLE IF NOT EXISTS tasks (id INTEGER PRIMARY KEY AUTOINCREMENT, taskName TEXT, assigneeName TEXT, username TEXT, meetingName TEXT, details TEXT, date TEXT);'); + //db.run('DROP TABLE tasks'); + // Default Users: + // Username: User1 + // Password: Password1 + // + // Username: charlie + // Password: charliee + + db.each('SELECT * from users', function(err, row) { + if ( row ) { + console.log('Initial Users:', row); + } + }); + db.each('SELECT * from messages', function(err, row) { + if ( row ) { + console.log('Initial Messages:', row); + } + }); + db.each('SELECT * from tasks', function(err, row) { + if ( row ) { + console.log('Initial tasks:', row); + } + }); +}); + +// Set up the local strategy for logins using the database +passport.use(new LocalStrategy(function(username, password, done) { + db.get('SELECT password FROM users WHERE username = ?', username, function(err, row) { + if (!row) { + console.log("not found:"); + return done(null, false); + } else { + db.get('SELECT username, id FROM users WHERE username = ? AND password = ?', username, password, function(err, row) { + if (!row) { + return done(null, false); + } + return done(null, row); + }); + } + }); +})); +passport.initialize(); + +passport.serializeUser(function(user, done) { + return done(null, user.id); +}); + +passport.deserializeUser(function(id, done) { + db.get('SELECT id, username FROM users WHERE id = ?', id, function(err, row) { + if (!row) { + return done(null, false); + } + return done(null, row); + }); +}); + +// Enforce strict routing for express-slash +app.enable('strict routing'); + +// Creating a router using express-slash middleware to handle extra or omitted +// slashes, (/) in the url +const router = express.Router({ + caseSensitive: app.get('case sensitive routing'), + strict : app.get('strict routing') +}); + +// Enabling strict router for the trailing slash to be handled +app.use(router); +app.use(slash()); + +// Serve up the different views in the app other than the login only if the user is authenticated +router.get('/mainview.html', function(request, response) { + if (!request.user) { + response.sendFile( __dirname + '/public/index.html' ); + } else { + response.sendFile( __dirname + '/public/mainview.html' ); + } +}) + +router.get('/meetingview.html', function(request, response) { + if (!request.user) { + response.sendFile( __dirname + '/public/index.html' ); + } else { + response.sendFile( __dirname + '/public/meetingview.html' ); + } +}) + +router.get('/calendarview.html', function(request, response) { + if (!request.user) { + response.sendFile( __dirname + '/public/index.html' ); + } else { + response.sendFile( __dirname + '/public/calendarview.html' ); + } +}) + +router.get('/messagesview.html', function(request, response) { + if (!request.user) { + response.sendFile( __dirname + '/public/index.html' ); + } else { + response.sendFile( __dirname + '/public/messagesview.html' ); + } +}) + +// Explicitly handle the index file +router.get('/', function(request, response) { + response.sendFile( __dirname + '/public/index.html' ); +}) + +router.get('/index.html/', function(request, response) { + response.sendFile( __dirname + '/public/index.html' ); +}) + +// View all of the messages that are intended for this user +app.post('/viewMessages', function(request, response) { + let resp; + response.writeHead( 200, "OK", {'Content-Type': 'application/json' }); + db.all('SELECT * from messages WHERE receiver = ? ', request.user.username, function(err, rows) { + if (rows === undefined) { + rows = []; + } + console.log(rows); + console.log(request.user.username); + resp = '{ "messagesArray": '+ JSON.stringify(rows) + ' }'; + console.log(resp); + response.end(resp, 'utf-8'); + }); +}) + +// View all of the tasks associated with a given meeting +app.post('/viewTasks', function(request, response) { + let resp; + response.writeHead( 200, "OK", {'Content-Type': 'application/json' }); + //first ensure that the meeting exists + db.get('SELECT * FROM meetings WHERE username=? AND name=?', request.user.username, request.body.meeting, function(err, row) { + if (row === undefined) { + resp = '{ "noTasksArray": ""}'; + console.log(resp); + response.end(resp, 'utf-8'); + } else { + db.all('SELECT * from tasks WHERE username = ? AND meetingName = ?', request.user.username, request.body.meeting, function(err, rows) { + if (rows === undefined) { + rows = []; + } + console.log(rows); + console.log(request.user.username); + resp = '{ "tasksArray": '+ JSON.stringify(rows) + ' }'; + console.log(resp); + response.end(resp, 'utf-8'); + })} + }); +}) + +// View all of the tasks associated with this user +app.post('/viewMyTasks', function(request, response) { + let resp; + response.writeHead( 200, "OK", {'Content-Type': 'application/json' }); + db.all('SELECT * from tasks WHERE assigneeName=?', request.user.username, function(err, rows) { + if (rows === undefined) { + rows = []; + } + console.log(rows); + console.log(request.user.username); + resp = '{ "tasksArray": '+ JSON.stringify(rows) + ' }'; + console.log(resp); + response.end(resp, 'utf-8'); + }); +}) + +// View all of the meetings associated with a given date, regardless of whether or not they belong to the current user +app.post('/viewMeetings', function(request, response) { + let resp; + response.writeHead( 200, "OK", {'Content-Type': 'application/json' }); + db.all('SELECT * from meetings WHERE date=?', request.body.date, function(err, rows) { + if (rows === undefined) { + rows = []; + } + console.log(rows); + resp = '{ "meetingsArray": '+ JSON.stringify(rows) + ' }'; + console.log(resp); + response.end(resp, 'utf-8'); + }); +}) + +// View all of the meetings associated with a given date that belong to the current user +app.post('/viewMyMeetings', function(request, response) { + let resp; + response.writeHead( 200, "OK", {'Content-Type': 'application/json' }); + db.all('SELECT * from meetings WHERE date=? AND username=?', request.body.date, request.user.username, function(err, rows) { + if (rows === undefined) { + rows = []; + } + console.log(rows); + resp = '{ "meetingsArray": '+ JSON.stringify(rows) + ' }'; + console.log(resp); + response.end(resp, 'utf-8'); + }); +}) + +// Add a new user account +app.post( '/signup', function( request, response ) { + let dataString = ''; + + request.on( 'data', function( data ) { + dataString += data; + }) + request.on( 'end', function() { + let resp; + let data = JSON.parse( dataString ); + let inserted; + let val; + // See if the user is in the database first + db.get('SELECT password FROM users WHERE username = ?', data.username, function(err, row) { + if (row) { + val = 1; + resp = '{"userAdded":' + false + '}'; + response.writeHead( 200, "OK", {'Content-Type': 'text/plain' }); + response.end(resp, 'utf-8'); + } else { + // If the user is not in the database, make a new user + db.run('INSERT INTO users (username, password) VALUES ("' + data.username + '","' + data.password + '")'); + console.log("new user entered"); + resp = '{"userAdded":' + true + '}'; + response.writeHead( 200, "OK", {'Content-Type': 'text/plain' }); + response.end(resp, 'utf-8'); + } + console.log(val); + return val; + }) + }) +}) + +// Add a new task for a user for a given meeting, and send along a message to the assignee +// Note that tasks can be assigned to users that don't exist yet +app.post( '/submitTask', function( request, response ) { + let resp; + db.get('SELECT * FROM meetings WHERE username=? AND name=?', request.user.username, request.body.meeting, function(err, row) { + if (row) { // Only add this task if the meeting actually exists + let newTaskMSG = "You have been assigned a task " + request.body.task + ", for the meeting " + request.body.meeting + " on " + row.date; + // Add the message and the task to the db for the other user + db.run('INSERT INTO messages (sender, receiver, contents) VALUES ("' + request.user.username + '","' + request.body.name + '","' + newTaskMSG + '")'); + db.run('INSERT INTO tasks (taskName, assigneeName, username, meetingName, details, date) VALUES ("' + request.body.task + '","' + request.body.name + '","' + request.user.username + '","' + request.body.meeting + '","' + request.body.details + '","' + row.date + '")'); + response.writeHead( 200, "OK", {'Content-Type': 'text/plain' }); + resp = '{"taskAdded":' + true + '}'; + response.end(resp, 'utf-8'); + } else { + response.writeHead( 200, "OK", {'Content-Type': 'text/plain' }); + resp = '{"taskAdded":' + false + '}'; + response.end(resp, 'utf-8'); + } + }) +}) + +// Submit a meeting, but only make it if its name is unique for this user +app.post( '/submitMeeting', function( request, response ) { + let resp; + db.get('SELECT * FROM meetings WHERE username=? AND name=?', request.user.username, request.body.name, function(err, row) { + if (row) { + resp = '{"meetingAdded":' + false + '}'; + response.writeHead( 200, "OK", {'Content-Type': 'text/plain' }); + response.end(resp, 'utf-8'); + } else { + db.run('INSERT INTO meetings (username, name, date, details) VALUES ("' + request.user.username + '","' + request.body.name + '","' + request.body.date + '","' + request.body.details + '")'); + resp = '{"meetingAdded":' + true + '}'; + response.writeHead( 200, "OK", {'Content-Type': 'text/plain' }); + response.end(resp, 'utf-8'); + } + }) +}) + +// Remove a task that belongs to the given user and meeting +app.delete('/removeTask', function( request, response ) { +console.log(request.body.name); + db.run('DELETE FROM tasks WHERE assigneeName=? AND taskName=? AND meetingName=? AND username=?', request.body.name, request.body.task, request.body.meeting, request.user.username, function(err) { + if (err) { + return console.error(err.message); + } + console.log(`Row(s) deleted ${this.changes}`); + let deleteMSG = "The task " + request.body.task + " that you were assigned for the meeting " + request.body.meeting + " has been removed."; + db.run('INSERT INTO messages (sender, receiver, contents) VALUES ("' + request.user.username + '","' + request.body.name + '","' + deleteMSG + '")'); + response.writeHead( 200, "OK", {'Content-Type': 'text/plain' }); + response.end('{"removed": "done"}', 'utf-8'); + }); +}) + +// Remove a meeting, and all associated tasks and messages +app.delete('/removeMeeting', function( request, response ) { +console.log(request.body.name); + db.run('DELETE FROM meetings WHERE name=? AND date=? AND username=?', request.body.name, request.body.date, request.user.username, function(err) { + if (err) { + return console.error(err.message); + } + console.log(`Row(s) deleted ${this.changes}`); + // Delete all tasks associated with the meeting automatically + db.run('DELETE FROM tasks WHERE meetingName=? AND username=?', request.body.name, request.user.username, function(err) { + if (err) { + return console.error(err.message); + } + response.writeHead( 200, "OK", {'Content-Type': 'text/plain' }); + response.end('{"removed": "done"}', 'utf-8'); + }); + }); +}) + +// Authenticate the user so they can be sent to the login page +app.post('/login', passport.authenticate( 'local' ), function( req, res ) { + console.log( 'user:', req.user ); + res.json({ status:true }); +}) + +app.use( express.static( 'public' ) ); + +app.listen( process.env.PORT ); diff --git a/shrinkwrap.yaml b/shrinkwrap.yaml new file mode 100644 index 00000000..dfc25b12 --- /dev/null +++ b/shrinkwrap.yaml @@ -0,0 +1,1431 @@ +dependencies: + body-parser: 1.19.0 + compression: 1.7.4 + express: 4.17.1 + express-session: 1.16.2 + express-slash: 2.0.1 + helmet: 3.21.1 + morgan: 1.9.1 + passport: 0.4.0 + passport-local: 1.0.0 + response-time: 2.3.2 + serve-favicon: 2.5.0 + sqlite3: 4.1.0 +packages: + /abbrev/1.1.1: + dev: false + resolution: + integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== + /accepts/1.3.7: + dependencies: + mime-types: 2.1.24 + negotiator: 0.6.2 + dev: false + engines: + node: '>= 0.6' + resolution: + integrity: sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA== + /ajv/6.10.2: + dependencies: + fast-deep-equal: 2.0.1 + fast-json-stable-stringify: 2.0.0 + json-schema-traverse: 0.4.1 + uri-js: 4.2.2 + dev: false + resolution: + integrity: sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw== + /ansi-regex/2.1.1: + dev: false + engines: + node: '>=0.10.0' + resolution: + integrity: sha1-w7M6te42DYbg5ijwRorn7yfWVN8= + /ansi-regex/3.0.0: + dev: false + engines: + node: '>=4' + resolution: + integrity: sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= + /aproba/1.2.0: + dev: false + resolution: + integrity: sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw== + /are-we-there-yet/1.1.5: + dependencies: + delegates: 1.0.0 + readable-stream: 2.3.6 + dev: false + resolution: + integrity: sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w== + /array-flatten/1.1.1: + dev: false + resolution: + integrity: sha1-ml9pkFGx5wczKPKgCJaLZOopVdI= + /asn1/0.2.4: + dependencies: + safer-buffer: 2.1.2 + dev: false + resolution: + integrity: sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg== + /assert-plus/1.0.0: + dev: false + engines: + node: '>=0.8' + resolution: + integrity: sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= + /asynckit/0.4.0: + dev: false + resolution: + integrity: sha1-x57Zf380y48robyXkLzDZkdLS3k= + /aws-sign2/0.7.0: + dev: false + resolution: + integrity: sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg= + /aws4/1.8.0: + dev: false + resolution: + integrity: sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ== + /balanced-match/1.0.0: + dev: false + resolution: + integrity: sha1-ibTRmasr7kneFk6gK4nORi1xt2c= + /basic-auth/2.0.1: + dependencies: + safe-buffer: 5.1.2 + dev: false + engines: + node: '>= 0.8' + resolution: + integrity: sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg== + /bcrypt-pbkdf/1.0.2: + dependencies: + tweetnacl: 0.14.5 + dev: false + resolution: + integrity: sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4= + /body-parser/1.19.0: + dependencies: + 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.18 + dev: false + engines: + node: '>= 0.8' + resolution: + integrity: sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw== + /bowser/2.7.0: + dev: false + resolution: + integrity: sha512-aIlMvstvu8x+34KEiOHD3AsBgdrzg6sxALYiukOWhFvGMbQI6TRP/iY0LMhUrHs56aD6P1G0Z7h45PUJaa5m9w== + /brace-expansion/1.1.11: + dependencies: + balanced-match: 1.0.0 + concat-map: 0.0.1 + dev: false + resolution: + integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + /bytes/3.0.0: + dev: false + engines: + node: '>= 0.8' + resolution: + integrity: sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg= + /bytes/3.1.0: + dev: false + engines: + node: '>= 0.8' + resolution: + integrity: sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== + /camelize/1.0.0: + dev: false + resolution: + integrity: sha1-FkpUg+Yw+kMh5a8HAg5TGDGyYJs= + /caseless/0.12.0: + dev: false + resolution: + integrity: sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= + /chownr/1.1.3: + dev: false + resolution: + integrity: sha512-i70fVHhmV3DtTl6nqvZOnIjbY0Pe4kAUjwHj8z0zAdgBtYrJyYwLKCCuRBQ5ppkyL0AkN7HKRnETdmdp1zqNXw== + /code-point-at/1.1.0: + dev: false + engines: + node: '>=0.10.0' + resolution: + integrity: sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= + /combined-stream/1.0.8: + dependencies: + delayed-stream: 1.0.0 + dev: false + engines: + node: '>= 0.8' + resolution: + integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + /compressible/2.0.17: + dependencies: + mime-db: 1.42.0 + dev: false + engines: + node: '>= 0.6' + resolution: + integrity: sha512-BGHeLCK1GV7j1bSmQQAi26X+GgWcTjLr/0tzSvMCl3LH1w1IJ4PFSPoV5316b30cneTziC+B1a+3OjoSUcQYmw== + /compression/1.7.4: + dependencies: + accepts: 1.3.7 + bytes: 3.0.0 + compressible: 2.0.17 + debug: 2.6.9 + on-headers: 1.0.2 + safe-buffer: 5.1.2 + vary: 1.1.2 + dev: false + engines: + node: '>= 0.8.0' + resolution: + integrity: sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ== + /concat-map/0.0.1: + dev: false + resolution: + integrity: sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= + /console-control-strings/1.1.0: + dev: false + resolution: + integrity: sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4= + /content-disposition/0.5.3: + dependencies: + safe-buffer: 5.1.2 + dev: false + engines: + node: '>= 0.6' + resolution: + integrity: sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g== + /content-security-policy-builder/2.1.0: + dev: false + engines: + node: '>=4.0.0' + resolution: + integrity: sha512-/MtLWhJVvJNkA9dVLAp6fg9LxD2gfI6R2Fi1hPmfjYXSahJJzcfvoeDOxSyp4NvxMuwWv3WMssE9o31DoULHrQ== + /content-type/1.0.4: + dev: false + engines: + node: '>= 0.6' + resolution: + integrity: sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== + /cookie-signature/1.0.6: + dev: false + resolution: + integrity: sha1-4wOogrNCzD7oylE6eZmXNNqzriw= + /cookie/0.3.1: + dev: false + engines: + node: '>= 0.6' + resolution: + integrity: sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s= + /cookie/0.4.0: + dev: false + engines: + node: '>= 0.6' + resolution: + integrity: sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg== + /core-util-is/1.0.2: + dev: false + resolution: + integrity: sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= + /dashdash/1.14.1: + dependencies: + assert-plus: 1.0.0 + dev: false + engines: + node: '>=0.10' + resolution: + integrity: sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA= + /dasherize/2.0.0: + dev: false + resolution: + integrity: sha1-bYCcnNDPe7iVLYD8hPoT1H3bEwg= + /debug/2.6.9: + dependencies: + ms: 2.0.0 + dev: false + resolution: + integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + /debug/3.2.6: + dependencies: + ms: 2.1.2 + dev: false + resolution: + integrity: sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== + /deep-extend/0.6.0: + dev: false + engines: + node: '>=4.0.0' + resolution: + integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== + /delayed-stream/1.0.0: + dev: false + engines: + node: '>=0.4.0' + resolution: + integrity: sha1-3zrhmayt+31ECqrgsp4icrJOxhk= + /delegates/1.0.0: + dev: false + resolution: + integrity: sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= + /depd/1.1.2: + dev: false + engines: + node: '>= 0.6' + resolution: + integrity: sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= + /depd/2.0.0: + dev: false + engines: + node: '>= 0.8' + resolution: + integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== + /destroy/1.0.4: + dev: false + resolution: + integrity: sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= + /detect-libc/1.0.3: + dev: false + engines: + node: '>=0.10' + hasBin: true + resolution: + integrity: sha1-+hN8S9aY7fVc1c0CrFWfkaTEups= + /dns-prefetch-control/0.2.0: + dev: false + engines: + node: '>=4.0.0' + resolution: + integrity: sha512-hvSnros73+qyZXhHFjx2CMLwoj3Fe7eR9EJsFsqmcI1bB2OBWL/+0YzaEaKssCHnj/6crawNnUyw74Gm2EKe+Q== + /dont-sniff-mimetype/1.1.0: + dev: false + engines: + node: '>=4.0.0' + resolution: + integrity: sha512-ZjI4zqTaxveH2/tTlzS1wFp+7ncxNZaIEWYg3lzZRHkKf5zPT/MnEG6WL0BhHMJUabkh8GeU5NL5j+rEUCb7Ug== + /ecc-jsbn/0.1.2: + dependencies: + jsbn: 0.1.1 + safer-buffer: 2.1.2 + dev: false + resolution: + integrity: sha1-OoOpBOVDUyh4dMVkt1SThoSamMk= + /ee-first/1.1.1: + dev: false + resolution: + integrity: sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= + /encodeurl/1.0.2: + dev: false + engines: + node: '>= 0.8' + resolution: + integrity: sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= + /escape-html/1.0.3: + dev: false + resolution: + integrity: sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= + /etag/1.8.1: + dev: false + engines: + node: '>= 0.6' + resolution: + integrity: sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= + /expect-ct/0.2.0: + dev: false + engines: + node: '>=4.0.0' + resolution: + integrity: sha512-6SK3MG/Bbhm8MsgyJAylg+ucIOU71/FzyFalcfu5nY19dH8y/z0tBJU0wrNBXD4B27EoQtqPF/9wqH0iYAd04g== + /express-session/1.16.2: + dependencies: + cookie: 0.3.1 + cookie-signature: 1.0.6 + debug: 2.6.9 + depd: 2.0.0 + on-headers: 1.0.2 + parseurl: 1.3.3 + safe-buffer: 5.1.2 + uid-safe: 2.1.5 + dev: false + engines: + node: '>= 0.8.0' + resolution: + integrity: sha512-oy0sRsdw6n93E9wpCNWKRnSsxYnSDX9Dnr9mhZgqUEEorzcq5nshGYSZ4ZReHFhKQ80WI5iVUUSPW7u3GaKauw== + /express-slash/2.0.1: + dev: false + resolution: + integrity: sha1-duuGn+2Pca5PmHhcvgC/G7RU1ic= + /express/4.17.1: + dependencies: + 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 + dev: false + engines: + node: '>= 0.10.0' + resolution: + integrity: sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g== + /extend/3.0.2: + dev: false + resolution: + integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== + /extsprintf/1.3.0: + dev: false + engines: + '0': node >=0.6.0 + resolution: + integrity: sha1-lpGEQOMEGnpBT4xS48V06zw+HgU= + /extsprintf/1.4.0: + dev: false + engines: + '0': node >=0.6.0 + resolution: + integrity: sha1-4mifjzVvrWLMplo6kcXfX5VRaS8= + /fast-deep-equal/2.0.1: + dev: false + resolution: + integrity: sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk= + /fast-json-stable-stringify/2.0.0: + dev: false + resolution: + integrity: sha1-1RQsDK7msRifh9OnYREGT4bIu/I= + /feature-policy/0.3.0: + dev: false + engines: + node: '>=4.0.0' + resolution: + integrity: sha512-ZtijOTFN7TzCujt1fnNhfWPFPSHeZkesff9AXZj+UEjYBynWNUIYpC87Ve4wHzyexQsImicLu7WsC2LHq7/xrQ== + /finalhandler/1.1.2: + dependencies: + 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 + dev: false + engines: + node: '>= 0.8' + resolution: + integrity: sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA== + /forever-agent/0.6.1: + dev: false + resolution: + integrity: sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= + /form-data/2.3.3: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + mime-types: 2.1.24 + dev: false + engines: + node: '>= 0.12' + resolution: + integrity: sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== + /forwarded/0.1.2: + dev: false + engines: + node: '>= 0.6' + resolution: + integrity: sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ= + /frameguard/3.1.0: + dev: false + engines: + node: '>=4.0.0' + resolution: + integrity: sha512-TxgSKM+7LTA6sidjOiSZK9wxY0ffMPY3Wta//MqwmX0nZuEHc8QrkV8Fh3ZhMJeiH+Uyh/tcaarImRy8u77O7g== + /fresh/0.5.2: + dev: false + engines: + node: '>= 0.6' + resolution: + integrity: sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= + /fs-minipass/1.2.7: + dependencies: + minipass: 2.9.0 + dev: false + resolution: + integrity: sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA== + /fs.realpath/1.0.0: + dev: false + resolution: + integrity: sha1-FQStJSMVjKpA20onh8sBQRmU6k8= + /gauge/2.7.4: + dependencies: + aproba: 1.2.0 + console-control-strings: 1.1.0 + has-unicode: 2.0.1 + object-assign: 4.1.1 + signal-exit: 3.0.2 + string-width: 1.0.2 + strip-ansi: 3.0.1 + wide-align: 1.1.3 + dev: false + resolution: + integrity: sha1-LANAXHU4w51+s3sxcCLjJfsBi/c= + /getpass/0.1.7: + dependencies: + assert-plus: 1.0.0 + dev: false + resolution: + integrity: sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo= + /glob/7.1.4: + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.0.4 + once: 1.4.0 + path-is-absolute: 1.0.1 + dev: false + resolution: + integrity: sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A== + /har-schema/2.0.0: + dev: false + engines: + node: '>=4' + resolution: + integrity: sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI= + /har-validator/5.1.3: + dependencies: + ajv: 6.10.2 + har-schema: 2.0.0 + dev: false + engines: + node: '>=6' + resolution: + integrity: sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g== + /has-unicode/2.0.1: + dev: false + resolution: + integrity: sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk= + /helmet-crossdomain/0.4.0: + dev: false + engines: + node: '>=4.0.0' + resolution: + integrity: sha512-AB4DTykRw3HCOxovD1nPR16hllrVImeFp5VBV9/twj66lJ2nU75DP8FPL0/Jp4jj79JhTfG+pFI2MD02kWJ+fA== + /helmet-csp/2.9.2: + dependencies: + bowser: 2.7.0 + camelize: 1.0.0 + content-security-policy-builder: 2.1.0 + dasherize: 2.0.0 + dev: false + engines: + node: '>=4.0.0' + resolution: + integrity: sha512-Lt5WqNfbNjEJ6ysD4UNpVktSyjEKfU9LVJ1LaFmPfYseg/xPealPfgHhtqdAdjPDopp5zbg/VWCyp4cluMIckw== + /helmet/3.21.1: + dependencies: + depd: 2.0.0 + dns-prefetch-control: 0.2.0 + dont-sniff-mimetype: 1.1.0 + expect-ct: 0.2.0 + feature-policy: 0.3.0 + frameguard: 3.1.0 + helmet-crossdomain: 0.4.0 + helmet-csp: 2.9.2 + hide-powered-by: 1.1.0 + hpkp: 2.0.0 + hsts: 2.2.0 + ienoopen: 1.1.0 + nocache: 2.1.0 + referrer-policy: 1.2.0 + x-xss-protection: 1.3.0 + dev: false + engines: + node: '>=4.0.0' + resolution: + integrity: sha512-IC/54Lxvvad2YiUdgLmPlNFKLhNuG++waTF5KPYq/Feo3NNhqMFbcLAlbVkai+9q0+4uxjxGPJ9bNykG+3zZNg== + /hide-powered-by/1.1.0: + dev: false + engines: + node: '>=4.0.0' + resolution: + integrity: sha512-Io1zA2yOA1YJslkr+AJlWSf2yWFkKjvkcL9Ni1XSUqnGLr/qRQe2UI3Cn/J9MsJht7yEVCe0SscY1HgVMujbgg== + /hpkp/2.0.0: + dev: false + resolution: + integrity: sha1-EOFCJk52IVpdMMROxD3mTe5tFnI= + /hsts/2.2.0: + dependencies: + depd: 2.0.0 + dev: false + engines: + node: '>=4.0.0' + resolution: + integrity: sha512-ToaTnQ2TbJkochoVcdXYm4HOCliNozlviNsg+X2XQLQvZNI/kCHR9rZxVYpJB3UPcHz80PgxRyWQ7PdU1r+VBQ== + /http-errors/1.7.2: + dependencies: + depd: 1.1.2 + inherits: 2.0.3 + setprototypeof: 1.1.1 + statuses: 1.5.0 + toidentifier: 1.0.0 + dev: false + engines: + node: '>= 0.6' + resolution: + integrity: sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg== + /http-errors/1.7.3: + dependencies: + depd: 1.1.2 + inherits: 2.0.4 + setprototypeof: 1.1.1 + statuses: 1.5.0 + toidentifier: 1.0.0 + dev: false + engines: + node: '>= 0.6' + resolution: + integrity: sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw== + /http-signature/1.2.0: + dependencies: + assert-plus: 1.0.0 + jsprim: 1.4.1 + sshpk: 1.16.1 + dev: false + engines: + node: '>=0.8' + npm: '>=1.3.7' + resolution: + integrity: sha1-muzZJRFHcvPZW2WmCruPfBj7rOE= + /iconv-lite/0.4.24: + dependencies: + safer-buffer: 2.1.2 + dev: false + engines: + node: '>=0.10.0' + resolution: + integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + /ienoopen/1.1.0: + dev: false + engines: + node: '>=4.0.0' + resolution: + integrity: sha512-MFs36e/ca6ohEKtinTJ5VvAJ6oDRAYFdYXweUnGY9L9vcoqFOU4n2ZhmJ0C4z/cwGZ3YIQRSB3XZ1+ghZkY5NQ== + /ignore-walk/3.0.2: + dependencies: + minimatch: 3.0.4 + dev: false + resolution: + integrity: sha512-EXyErtpHbn75ZTsOADsfx6J/FPo6/5cjev46PXrcTpd8z3BoRkXgYu9/JVqrI7tusjmwCZutGeRJeU0Wo1e4Cw== + /inflight/1.0.6: + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + dev: false + resolution: + integrity: sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= + /inherits/2.0.3: + dev: false + resolution: + integrity: sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= + /inherits/2.0.4: + dev: false + resolution: + integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + /ini/1.3.5: + dev: false + resolution: + integrity: sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw== + /ipaddr.js/1.9.0: + dev: false + engines: + node: '>= 0.10' + resolution: + integrity: sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA== + /is-fullwidth-code-point/1.0.0: + dependencies: + number-is-nan: 1.0.1 + dev: false + engines: + node: '>=0.10.0' + resolution: + integrity: sha1-754xOG8DGn8NZDr4L95QxFfvAMs= + /is-fullwidth-code-point/2.0.0: + dev: false + engines: + node: '>=4' + resolution: + integrity: sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= + /is-typedarray/1.0.0: + dev: false + resolution: + integrity: sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= + /isarray/1.0.0: + dev: false + resolution: + integrity: sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= + /isstream/0.1.2: + dev: false + resolution: + integrity: sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= + /jsbn/0.1.1: + dev: false + resolution: + integrity: sha1-peZUwuWi3rXyAdls77yoDA7y9RM= + /json-schema-traverse/0.4.1: + dev: false + resolution: + integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + /json-schema/0.2.3: + dev: false + resolution: + integrity: sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= + /json-stringify-safe/5.0.1: + dev: false + resolution: + integrity: sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= + /jsprim/1.4.1: + dependencies: + assert-plus: 1.0.0 + extsprintf: 1.3.0 + json-schema: 0.2.3 + verror: 1.10.0 + dev: false + engines: + '0': node >=0.6.0 + resolution: + integrity: sha1-MT5mvB5cwG5Di8G3SZwuXFastqI= + /media-typer/0.3.0: + dev: false + engines: + node: '>= 0.6' + resolution: + integrity: sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= + /merge-descriptors/1.0.1: + dev: false + resolution: + integrity: sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E= + /methods/1.1.2: + dev: false + engines: + node: '>= 0.6' + resolution: + integrity: sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= + /mime-db/1.40.0: + dev: false + engines: + node: '>= 0.6' + resolution: + integrity: sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA== + /mime-db/1.42.0: + dev: false + engines: + node: '>= 0.6' + resolution: + integrity: sha512-UbfJCR4UAVRNgMpfImz05smAXK7+c+ZntjaA26ANtkXLlOe947Aag5zdIcKQULAiF9Cq4WxBi9jUs5zkA84bYQ== + /mime-types/2.1.24: + dependencies: + mime-db: 1.40.0 + dev: false + engines: + node: '>= 0.6' + resolution: + integrity: sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ== + /mime/1.6.0: + dev: false + engines: + node: '>=4' + hasBin: true + resolution: + integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== + /minimatch/3.0.4: + dependencies: + brace-expansion: 1.1.11 + dev: false + resolution: + integrity: sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== + /minimist/0.0.8: + dev: false + resolution: + integrity: sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0= + /minimist/1.2.0: + dev: false + resolution: + integrity: sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ= + /minipass/2.9.0: + dependencies: + safe-buffer: 5.2.0 + yallist: 3.1.1 + dev: false + resolution: + integrity: sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg== + /minizlib/1.3.3: + dependencies: + minipass: 2.9.0 + dev: false + resolution: + integrity: sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q== + /mkdirp/0.5.1: + dependencies: + minimist: 0.0.8 + dev: false + hasBin: true + resolution: + integrity: sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM= + /morgan/1.9.1: + dependencies: + basic-auth: 2.0.1 + debug: 2.6.9 + depd: 1.1.2 + on-finished: 2.3.0 + on-headers: 1.0.2 + dev: false + engines: + node: '>= 0.8.0' + resolution: + integrity: sha512-HQStPIV4y3afTiCYVxirakhlCfGkI161c76kKFca7Fk1JusM//Qeo1ej2XaMniiNeaZklMVrh3vTtIzpzwbpmA== + /ms/2.0.0: + dev: false + resolution: + integrity: sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= + /ms/2.1.1: + dev: false + resolution: + integrity: sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== + /ms/2.1.2: + dev: false + resolution: + integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + /nan/2.14.0: + dev: false + resolution: + integrity: sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg== + /needle/2.4.0: + dependencies: + debug: 3.2.6 + iconv-lite: 0.4.24 + sax: 1.2.4 + dev: false + engines: + node: '>= 4.4.x' + hasBin: true + resolution: + integrity: sha512-4Hnwzr3mi5L97hMYeNl8wRW/Onhy4nUKR/lVemJ8gJedxxUyBLm9kkrDColJvoSfwi0jCNhD+xCdOtiGDQiRZg== + /negotiator/0.6.2: + dev: false + engines: + node: '>= 0.6' + resolution: + integrity: sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== + /nocache/2.1.0: + dev: false + engines: + node: '>=4.0.0' + resolution: + integrity: sha512-0L9FvHG3nfnnmaEQPjT9xhfN4ISk0A8/2j4M37Np4mcDesJjHgEUfgPhdCyZuFI954tjokaIj/A3NdpFNdEh4Q== + /node-pre-gyp/0.11.0: + dependencies: + detect-libc: 1.0.3 + mkdirp: 0.5.1 + needle: 2.4.0 + nopt: 4.0.1 + npm-packlist: 1.4.4 + npmlog: 4.1.2 + rc: 1.2.8 + rimraf: 2.7.1 + semver: 5.7.1 + tar: 4.4.13 + dev: false + hasBin: true + resolution: + integrity: sha512-TwWAOZb0j7e9eGaf9esRx3ZcLaE5tQ2lvYy1pb5IAaG1a2e2Kv5Lms1Y4hpj+ciXJRofIxxlt5haeQ/2ANeE0Q== + /nopt/4.0.1: + dependencies: + abbrev: 1.1.1 + osenv: 0.1.5 + dev: false + hasBin: true + resolution: + integrity: sha1-0NRoWv1UFRk8jHUFYC0NF81kR00= + /npm-bundled/1.0.6: + dev: false + resolution: + integrity: sha512-8/JCaftHwbd//k6y2rEWp6k1wxVfpFzB6t1p825+cUb7Ym2XQfhwIC5KwhrvzZRJu+LtDE585zVaS32+CGtf0g== + /npm-packlist/1.4.4: + dependencies: + ignore-walk: 3.0.2 + npm-bundled: 1.0.6 + dev: false + resolution: + integrity: sha512-zTLo8UcVYtDU3gdeaFu2Xu0n0EvelfHDGuqtNIn5RO7yQj4H1TqNdBc/yZjxnWA0PVB8D3Woyp0i5B43JwQ6Vw== + /npmlog/4.1.2: + dependencies: + are-we-there-yet: 1.1.5 + console-control-strings: 1.1.0 + gauge: 2.7.4 + set-blocking: 2.0.0 + dev: false + resolution: + integrity: sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg== + /number-is-nan/1.0.1: + dev: false + engines: + node: '>=0.10.0' + resolution: + integrity: sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= + /oauth-sign/0.9.0: + dev: false + resolution: + integrity: sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== + /object-assign/4.1.1: + dev: false + engines: + node: '>=0.10.0' + resolution: + integrity: sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= + /on-finished/2.3.0: + dependencies: + ee-first: 1.1.1 + dev: false + engines: + node: '>= 0.8' + resolution: + integrity: sha1-IPEzZIGwg811M3mSoWlxqi2QaUc= + /on-headers/1.0.2: + dev: false + engines: + node: '>= 0.8' + resolution: + integrity: sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA== + /once/1.4.0: + dependencies: + wrappy: 1.0.2 + dev: false + resolution: + integrity: sha1-WDsap3WWHUsROsF9nFC6753Xa9E= + /os-homedir/1.0.2: + dev: false + engines: + node: '>=0.10.0' + resolution: + integrity: sha1-/7xJiDNuDoM94MFox+8VISGqf7M= + /os-tmpdir/1.0.2: + dev: false + engines: + node: '>=0.10.0' + resolution: + integrity: sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= + /osenv/0.1.5: + dependencies: + os-homedir: 1.0.2 + os-tmpdir: 1.0.2 + dev: false + resolution: + integrity: sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g== + /parseurl/1.3.3: + dev: false + engines: + node: '>= 0.8' + resolution: + integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== + /passport-local/1.0.0: + dependencies: + passport-strategy: 1.0.0 + dev: false + engines: + node: '>= 0.4.0' + resolution: + integrity: sha1-H+YyaMkudWBmJkN+O5BmYsFbpu4= + /passport-strategy/1.0.0: + dev: false + engines: + node: '>= 0.4.0' + resolution: + integrity: sha1-tVOaqPwiWj0a0XlHbd8ja0QPUuQ= + /passport/0.4.0: + dependencies: + passport-strategy: 1.0.0 + pause: 0.0.1 + dev: false + engines: + node: '>= 0.4.0' + resolution: + integrity: sha1-xQlWkTR71a07XhgCOMORTRbwWBE= + /path-is-absolute/1.0.1: + dev: false + engines: + node: '>=0.10.0' + resolution: + integrity: sha1-F0uSaHNVNP+8es5r9TpanhtcX18= + /path-to-regexp/0.1.7: + dev: false + resolution: + integrity: sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= + /pause/0.0.1: + dev: false + resolution: + integrity: sha1-HUCLP9t2kjuVQ9lvtMnf1TXZy10= + /performance-now/2.1.0: + dev: false + resolution: + integrity: sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= + /process-nextick-args/2.0.1: + dev: false + resolution: + integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== + /proxy-addr/2.0.5: + dependencies: + forwarded: 0.1.2 + ipaddr.js: 1.9.0 + dev: false + engines: + node: '>= 0.10' + resolution: + integrity: sha512-t/7RxHXPH6cJtP0pRG6smSr9QJidhB+3kXu0KgXnbGYMgzEnUxRQ4/LDdfOwZEMyIh3/xHb8PX3t+lfL9z+YVQ== + /psl/1.4.0: + dev: false + resolution: + integrity: sha512-HZzqCGPecFLyoRj5HLfuDSKYTJkAfB5thKBIkRHtGjWwY7p1dAyveIbXIq4tO0KYfDF2tHqPUgY9SDnGm00uFw== + /punycode/1.4.1: + dev: false + resolution: + integrity: sha1-wNWmOycYgArY4esPpSachN1BhF4= + /punycode/2.1.1: + dev: false + engines: + node: '>=6' + resolution: + integrity: sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== + /qs/6.5.2: + dev: false + engines: + node: '>=0.6' + resolution: + integrity: sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== + /qs/6.7.0: + dev: false + engines: + node: '>=0.6' + resolution: + integrity: sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ== + /random-bytes/1.0.0: + dev: false + engines: + node: '>= 0.8' + resolution: + integrity: sha1-T2ih3Arli9P7lYSMMDJNt11kNgs= + /range-parser/1.2.1: + dev: false + engines: + node: '>= 0.6' + resolution: + integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== + /raw-body/2.4.0: + dependencies: + bytes: 3.1.0 + http-errors: 1.7.2 + iconv-lite: 0.4.24 + unpipe: 1.0.0 + dev: false + engines: + node: '>= 0.8' + resolution: + integrity: sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q== + /rc/1.2.8: + dependencies: + deep-extend: 0.6.0 + ini: 1.3.5 + minimist: 1.2.0 + strip-json-comments: 2.0.1 + dev: false + hasBin: true + resolution: + integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== + /readable-stream/2.3.6: + dependencies: + core-util-is: 1.0.2 + inherits: 2.0.4 + isarray: 1.0.0 + process-nextick-args: 2.0.1 + safe-buffer: 5.1.2 + string_decoder: 1.1.1 + util-deprecate: 1.0.2 + dev: false + resolution: + integrity: sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw== + /referrer-policy/1.2.0: + dev: false + engines: + node: '>=4.0.0' + resolution: + integrity: sha512-LgQJIuS6nAy1Jd88DCQRemyE3mS+ispwlqMk3b0yjZ257fI1v9c+/p6SD5gP5FGyXUIgrNOAfmyioHwZtYv2VA== + /request/2.88.0: + dependencies: + aws-sign2: 0.7.0 + aws4: 1.8.0 + caseless: 0.12.0 + combined-stream: 1.0.8 + extend: 3.0.2 + forever-agent: 0.6.1 + form-data: 2.3.3 + har-validator: 5.1.3 + http-signature: 1.2.0 + is-typedarray: 1.0.0 + isstream: 0.1.2 + json-stringify-safe: 5.0.1 + mime-types: 2.1.24 + oauth-sign: 0.9.0 + performance-now: 2.1.0 + qs: 6.5.2 + safe-buffer: 5.2.0 + tough-cookie: 2.4.3 + tunnel-agent: 0.6.0 + uuid: 3.3.3 + dev: false + engines: + node: '>= 4' + resolution: + integrity: sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg== + /response-time/2.3.2: + dependencies: + depd: 1.1.2 + on-headers: 1.0.2 + dev: false + engines: + node: '>= 0.8.0' + resolution: + integrity: sha1-/6cbq5UtYvfB1Jt0NDVfvGjf/Fo= + /rimraf/2.7.1: + dependencies: + glob: 7.1.4 + dev: false + hasBin: true + resolution: + integrity: sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== + /safe-buffer/5.1.1: + dev: false + resolution: + integrity: sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg== + /safe-buffer/5.1.2: + dev: false + resolution: + integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + /safe-buffer/5.2.0: + dev: false + resolution: + integrity: sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg== + /safer-buffer/2.1.2: + dev: false + resolution: + integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + /sax/1.2.4: + dev: false + resolution: + integrity: sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== + /semver/5.7.1: + dev: false + hasBin: true + resolution: + integrity: sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + /send/0.17.1: + dependencies: + 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.3 + mime: 1.6.0 + ms: 2.1.1 + on-finished: 2.3.0 + range-parser: 1.2.1 + statuses: 1.5.0 + dev: false + engines: + node: '>= 0.8.0' + resolution: + integrity: sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg== + /serve-favicon/2.5.0: + dependencies: + etag: 1.8.1 + fresh: 0.5.2 + ms: 2.1.1 + parseurl: 1.3.3 + safe-buffer: 5.1.1 + dev: false + engines: + node: '>= 0.8.0' + resolution: + integrity: sha1-k10kDN/g9YBTB/3+ln2IlCosvPA= + /serve-static/1.14.1: + dependencies: + encodeurl: 1.0.2 + escape-html: 1.0.3 + parseurl: 1.3.3 + send: 0.17.1 + dev: false + engines: + node: '>= 0.8.0' + resolution: + integrity: sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg== + /set-blocking/2.0.0: + dev: false + resolution: + integrity: sha1-BF+XgtARrppoA93TgrJDkrPYkPc= + /setprototypeof/1.1.1: + dev: false + resolution: + integrity: sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw== + /signal-exit/3.0.2: + dev: false + resolution: + integrity: sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0= + /sqlite3/4.1.0: + dependencies: + nan: 2.14.0 + node-pre-gyp: 0.11.0 + request: 2.88.0 + dev: false + requiresBuild: true + resolution: + integrity: sha512-RvqoKxq+8pDHsJo7aXxsFR18i+dU2Wp5o12qAJOV5LNcDt+fgJsc2QKKg3sIRfXrN9ZjzY1T7SNe/DFVqAXjaw== + /sshpk/1.16.1: + dependencies: + asn1: 0.2.4 + assert-plus: 1.0.0 + bcrypt-pbkdf: 1.0.2 + dashdash: 1.14.1 + ecc-jsbn: 0.1.2 + getpass: 0.1.7 + jsbn: 0.1.1 + safer-buffer: 2.1.2 + tweetnacl: 0.14.5 + dev: false + engines: + node: '>=0.10.0' + hasBin: true + resolution: + integrity: sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg== + /statuses/1.5.0: + dev: false + engines: + node: '>= 0.6' + resolution: + integrity: sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= + /string-width/1.0.2: + dependencies: + code-point-at: 1.1.0 + is-fullwidth-code-point: 1.0.0 + strip-ansi: 3.0.1 + dev: false + engines: + node: '>=0.10.0' + resolution: + integrity: sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M= + /string-width/2.1.1: + dependencies: + is-fullwidth-code-point: 2.0.0 + strip-ansi: 4.0.0 + dev: false + engines: + node: '>=4' + resolution: + integrity: sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== + /string_decoder/1.1.1: + dependencies: + safe-buffer: 5.1.2 + dev: false + resolution: + integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + /strip-ansi/3.0.1: + dependencies: + ansi-regex: 2.1.1 + dev: false + engines: + node: '>=0.10.0' + resolution: + integrity: sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= + /strip-ansi/4.0.0: + dependencies: + ansi-regex: 3.0.0 + dev: false + engines: + node: '>=4' + resolution: + integrity: sha1-qEeQIusaw2iocTibY1JixQXuNo8= + /strip-json-comments/2.0.1: + dev: false + engines: + node: '>=0.10.0' + resolution: + integrity: sha1-PFMZQukIwml8DsNEhYwobHygpgo= + /tar/4.4.13: + dependencies: + chownr: 1.1.3 + fs-minipass: 1.2.7 + minipass: 2.9.0 + minizlib: 1.3.3 + mkdirp: 0.5.1 + safe-buffer: 5.2.0 + yallist: 3.1.1 + dev: false + engines: + node: '>=4.5' + resolution: + integrity: sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA== + /toidentifier/1.0.0: + dev: false + engines: + node: '>=0.6' + resolution: + integrity: sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== + /tough-cookie/2.4.3: + dependencies: + psl: 1.4.0 + punycode: 1.4.1 + dev: false + engines: + node: '>=0.8' + resolution: + integrity: sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ== + /tunnel-agent/0.6.0: + dependencies: + safe-buffer: 5.2.0 + dev: false + resolution: + integrity: sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0= + /tweetnacl/0.14.5: + dev: false + resolution: + integrity: sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= + /type-is/1.6.18: + dependencies: + media-typer: 0.3.0 + mime-types: 2.1.24 + dev: false + engines: + node: '>= 0.6' + resolution: + integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== + /uid-safe/2.1.5: + dependencies: + random-bytes: 1.0.0 + dev: false + engines: + node: '>= 0.8' + resolution: + integrity: sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA== + /unpipe/1.0.0: + dev: false + engines: + node: '>= 0.8' + resolution: + integrity: sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= + /uri-js/4.2.2: + dependencies: + punycode: 2.1.1 + dev: false + resolution: + integrity: sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ== + /util-deprecate/1.0.2: + dev: false + resolution: + integrity: sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= + /utils-merge/1.0.1: + dev: false + engines: + node: '>= 0.4.0' + resolution: + integrity: sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= + /uuid/3.3.3: + dev: false + hasBin: true + resolution: + integrity: sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ== + /vary/1.1.2: + dev: false + engines: + node: '>= 0.8' + resolution: + integrity: sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= + /verror/1.10.0: + dependencies: + assert-plus: 1.0.0 + core-util-is: 1.0.2 + extsprintf: 1.4.0 + dev: false + engines: + '0': node >=0.6.0 + resolution: + integrity: sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA= + /wide-align/1.1.3: + dependencies: + string-width: 2.1.1 + dev: false + resolution: + integrity: sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA== + /wrappy/1.0.2: + dev: false + resolution: + integrity: sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= + /x-xss-protection/1.3.0: + dev: false + engines: + node: '>=4.0.0' + resolution: + integrity: sha512-kpyBI9TlVipZO4diReZMAHWtS0MMa/7Kgx8hwG/EuZLiA6sg4Ah/4TRdASHhRRN3boobzcYgFRUFSgHRge6Qhg== + /yallist/3.1.1: + dev: false + resolution: + integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== +registry: 'https://registry.npmjs.org/' +shrinkwrapMinorVersion: 9 +shrinkwrapVersion: 3 +specifiers: + body-parser: ^1.19.0 + compression: ^1.7.4 + express: ^4.17.1 + express-session: ^1.16.2 + express-slash: ^2.0.1 + helmet: ^3.21.0 + morgan: 1.9.1 + passport: ^0.4.0 + passport-local: ^1.0.0 + response-time: ^2.3.2 + serve-favicon: ^2.5.0 + sqlite3: ^4.1.0 diff --git a/student.db b/student.db new file mode 100644 index 0000000000000000000000000000000000000000..cf3c8fa1ef9163e208e58b6a873f5bbf4cc99997 GIT binary patch literal 49152 zcmeI5U2NOd6~{^I`-8f6RhLmzTV#{eacn21l638g0Zj*|&MG?}mfO1Rn$RX~skJ0W zq>={h!&IOz#Re369g02lq1)5C7RbXM8mxQSfUX0Yq5}r>VNV;-w*u=+QDEm@Q6weB zG>uS2upRnCCVB7wocp_nANNw$S-DuMn!2>vXx3CyI?0W4JkLELNgT)Ta2%J0pYC!N z{s?zh;FWj%z004Var~4dq@U*!soSB%w1Bx5?G?pLeN-n~?w#Kmter z2_OL^fCP{L67Wl)9nQqWi3z^_f~l@=>owgl)GgiUzJ`8$Y2i$HK`Nhlva}#|w@OE= znsjctyztb*CF#PYb4zC~tw~QWtVw59%jeH6L!G6CPDk(>UGm#d#A~ZTwN4J{tcPAVXDR}2K$xlae!^|Yn>KeR%^OtUp0*CR$bR&Q+swa z#;pC|`>@t^pk?UIy5mnx*Uf5u%jKb_n`(8tLu6LcR2XVebki5&QE_I5Ut`W1SGQpd zcG=Lcw)Faj-nW|Q!PU3(D78KdHK*%I*8cY6Op-cguwU_>>THu=+I_9t0o>OcO|8>{ zTBKv|G0R?42Yc`<+Gh_oNz6-D%^g~^QoU7Mx1vW{K3|rWS4*W?X{!qey>^()Rx&`>zA43%L;pmsiBxi2;WvZaE)+}M}c@Fhoj#+2x&0A_fHImJzq8!P~S*5wtfO4iS^k}(xV{(M; z%(eOzPPSl?S||7@cPL+&E`v?0sa8p4{=_3EsFrE7rH;{g3Z8sR`!h|x&3ux%nwb>d z7gmHs`h#>OJ(>DE^;YVM-T_i)fhPKU6=$5+4eYCfNyUX9i2f}oqbCa%x9_?gxoU^GPx-?f*=~c zm}lM3TfwzQ_j`p^=^zW!t`dtB@0NS1UCM!4zMZiAQZ++URn1#Z)igsL?31PLz1_+I z<{|NaAkjw99}m%0kvZBGwY0q*wF>E8vJZf)>}o&UJ!mR?dn@mCd@hZH?3g2amvW{1 zL5?_bcjsIw3u`*nvATdZ*`gMO++ZZgv~J<&AL5BV-m~ zECZbpgc1y70#c%*pJ04S2c`t08kbLt-ozI@h_AxP3<5U0-=HW%P^KACcdZ zcge5F&tVfTNB{{S0VIF~kN^@u0!RP}AOR$R1du>L1d@p`dCRw2-MC`_9%GX{0D`GP3Z-t2D@-L~#D3+m3&!s9aCai_vgS^EC} zIqoBld`aFQQ<+aPS2L5s`@)KlNPm#7q$g9Kr`}3Ek^Cn4PV#(`Pkflr5<>iDT#XB{ zTd`JbBKm3c_2|LKXOU}>1L2$Da_H;O+o5A4w?ga_H!@pr31Zk1ASOQtWh_?2v_QvlZ2pZa_@K+cI=3o^_4o^E4~E zWCeiam>5X2sm-uvxXlc~O63?>DcFs%PdLH1r!xJ0S*jjomQGp$uuGeY#UMIqikX`3 zH{~5~%%ft_cf?(J7_8*lQ?7`X_DDS62}A!Djtt$x;h|eNG;9kmJ_HuTw(RPMJ-Pud zQoj4(c^TA6yL;Z+BLV#W&RJ(ANMxKhtP7cSS@RB3tug4ia-|UEvO@cDxX#&U2g}SVGw7@^Jz5QoED`wY-yA9`3Cm$+b60RnrWwjPNT5n1{swfkYcYe+D~xsdMus zDXSuLv@2?9dpl|s(!FFK09o19ez+-v8#7JyR^IFQTp9=2F-P_;Xhy^jd;>h02fLHhRW81qfsU+lmg8O^7zcy#pQD08!W*lc+t-11T)fhlDIQxXDG zN(ZKt>QP$sCcfxFd^HJ5VmrIvpeRF7c*rg$;Ib*c=3f%6`lLieA_3>7|WE-PG%*LC61bVU#1kCO^!4k$FAy zknks=1siZd0!RP}AOR$R1dsp{Kmter3H*-}*f`DRr0J};d)C*PV)i?(cGVadR+mn( zd1d-<-}czuukPR={ii2k0GYKP8Nm#MEvouewP#ib<)14u`R+fCgS__)fY Date: Wed, 9 Oct 2019 03:29:31 +0000 Subject: [PATCH 2/2] =?UTF-8?q?=F0=9F=A6=82=F0=9F=92=81=20Updated=20with?= =?UTF-8?q?=20Glitch?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit