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 00000000..998d1b29 Binary files /dev/null and b/assets/3a7a7745-805f-4bc9-9091-f891637e22a2%2FfaviconCheck.ico?v=1570204892951 differ diff --git a/package.json b/package.json new file mode 100755 index 00000000..be01b08c --- /dev/null +++ b/package.json @@ -0,0 +1,23 @@ +{ + "name": "fp-start", + "version": "0.1.0", + "description": "A nice description", + "author": "Many ", + "scripts": { + "start": "node server.improved.js" + }, + "dependencies": { + "body-parser": "^1.19.0", + "express": "^4.17.1", + "express-session": "^1.16.2", + "passport": "^0.4.0", + "passport-local": "^1.0.0", + "sqlite3": "^4.1.0", + "response-time": "^2.3.2", + "serve-favicon": "^2.5.0", + "helmet": "^3.21.0", + "express-slash": "^2.0.1", + "morgan": "1.9.1", + "compression": "^1.7.4" + } +} diff --git a/proposal.md b/proposal.md new file mode 100644 index 00000000..6053f7b2 --- /dev/null +++ b/proposal.md @@ -0,0 +1,8 @@ +# Proposal + +Team members: Peter Jankowski, Tom Graham, Rosana Pochat + +Project details: + +We want to make a calendar application that allows for people to sign up, and set up meetings with other registered users. Our website will allow for people to set up task lists in advance of a meeting, assign tasks to users from a meeting’s task list, and notify users as the meeting deadline approaches. We will also allow for users to adjust the look of the site when they are logged in. + diff --git a/public/css/index.css b/public/css/index.css new file mode 100644 index 00000000..3eb89a06 --- /dev/null +++ b/public/css/index.css @@ -0,0 +1,21 @@ +.form-control { + margin-left:25%; + width:50%; +} + +label { + margin-left:25%; +} + +details { + margin-left:25%; + margin-right:25%; +} + +#myCanvas{ + background-color: black; +} + +.btn { + margin-left:25%; +} \ No newline at end of file diff --git a/public/index.html b/public/index.html new file mode 100755 index 00000000..e06990bf --- /dev/null +++ b/public/index.html @@ -0,0 +1,60 @@ + + + + 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. +
+ + + + + +
+
+ + + + 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

+ +
+
+
+
+ + + + 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. +
+
+

+
+ + +
+
+
+ +
+
+ + + + + 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. +
+
+ + +
+
+
+ + +
+
+ + + + 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. +
+ + +
+
+
+ + + + + + + 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 00000000..cf3c8fa1 Binary files /dev/null and b/student.db differ