From 2f2ec89521d605ff78c1bce7309fd3465a24d185 Mon Sep 17 00:00:00 2001 From: Raymond Oung Date: Sun, 26 Apr 2020 13:38:59 +0900 Subject: [PATCH 01/30] Switches from pug to ejs --- webapp/views/app.html | 620 ----------------------------------- webapp/views/error.ejs | 15 + webapp/views/error.pug | 6 - webapp/views/header.ejs | 32 ++ webapp/views/index.ejs | 71 ++++ webapp/views/index.pug | 45 --- webapp/views/layout.pug | 50 --- webapp/views/mobile.ejs | 45 +++ webapp/views/mobile.pug | 23 -- webapp/views/test.html | 49 --- webapp/views/test_jitsi.html | 28 -- 11 files changed, 163 insertions(+), 821 deletions(-) delete mode 100644 webapp/views/app.html create mode 100644 webapp/views/error.ejs delete mode 100644 webapp/views/error.pug create mode 100644 webapp/views/header.ejs create mode 100644 webapp/views/index.ejs delete mode 100644 webapp/views/index.pug delete mode 100644 webapp/views/layout.pug create mode 100644 webapp/views/mobile.ejs delete mode 100644 webapp/views/mobile.pug delete mode 100644 webapp/views/test.html delete mode 100644 webapp/views/test_jitsi.html diff --git a/webapp/views/app.html b/webapp/views/app.html deleted file mode 100644 index f56ad8f..0000000 --- a/webapp/views/app.html +++ /dev/null @@ -1,620 +0,0 @@ - - - - - Connect - - - - - - - - - - - - - - - - - - - - -
- - -
- -
- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/webapp/views/error.ejs b/webapp/views/error.ejs new file mode 100644 index 0000000..18c77ed --- /dev/null +++ b/webapp/views/error.ejs @@ -0,0 +1,15 @@ +<%- include('header') -%> + + +
+

Error 404

+

The requested URL was not found on this server.

+ + + + + + + + + diff --git a/webapp/views/error.pug b/webapp/views/error.pug deleted file mode 100644 index 27159b8..0000000 --- a/webapp/views/error.pug +++ /dev/null @@ -1,6 +0,0 @@ -extends layout - -block content - .p-3.mb-2.bg-light.text-dark - p Error 404 - p The requested URL was not found on this server. diff --git a/webapp/views/header.ejs b/webapp/views/header.ejs new file mode 100644 index 0000000..af5323f --- /dev/null +++ b/webapp/views/header.ejs @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/webapp/views/index.ejs b/webapp/views/index.ejs new file mode 100644 index 0000000..59350dd --- /dev/null +++ b/webapp/views/index.ejs @@ -0,0 +1,71 @@ +<%- include('header') -%> + + +
+ +
+
+ + +
+
+
+ + +
+ + + + + + + + location_on + + + + +
+ + +
+
+
+ + +
+
+ + +
+
+ + +
+

© 2020 hapi-robo st, Inc. — All Rights Reserved.

+
+ + + + + + + + + + + + + + + + + + + + + + + diff --git a/webapp/views/index.pug b/webapp/views/index.pug deleted file mode 100644 index aa6b531..0000000 --- a/webapp/views/index.pug +++ /dev/null @@ -1,45 +0,0 @@ -extends layout - -block content - - div(style='height:100vh;') - - // robot selection menu - div#robot-menu.row.valign-wrapper - div.input-field.col.l2.offset-l5.m4.offset-m4.s6.offset-s3 - input#temi-id.validate.white-text(type='text') - label(for='temi-id') temi ID - div#robot-collection - - // control panel - div#robot-ctrl-panel.container.center-align - a#home-btn.btn-flat.white-text - i.fas.fa-home - a#video-btn.btn-flat.white-text - i.fas.fa-video - a.btn-flat.white-text.modal-trigger(data-target='waypoint-menu') - i.material-icons location_on - a.btn-flat.white-text - i#battery-state - - // video-call div - div.row.valign-wrapper - div#video-container.col.l6.offset-l3.s12.center-align - - // text input - div#text-input.row - div.input-field.col.l2.offset-l5.m4.offset-m4.s6.offset-s3 - input#utterance.validate.white-text(type='text') - label(for='utterance') Type to speak - - // footer - div.white-text(style='position:relative') - p(style='position:fixed; bottom:0; width:100%; text-align:center') © 2020 hapi-robo st, Inc. — All Rights Reserved. - - - // locations modal - div#waypoint-menu.modal(style='width:30%;padding:0') - .modal-content - ul#waypoint-modal.collection - - diff --git a/webapp/views/layout.pug b/webapp/views/layout.pug deleted file mode 100644 index 5ac870b..0000000 --- a/webapp/views/layout.pug +++ /dev/null @@ -1,50 +0,0 @@ -doctype html -html(lang='en', dir='ltr') - head - title #{title} - meta(charset='utf-8') - - // Let browser know website is optimized for mobile - meta(name='viewport', content='width=device-width, initial-scale=1.0') - - // Favicon - // https://realfavicongenerator.net/ - link(rel="apple-touch-icon", sizes="180x180", href="/icons/apple-touch-icon.png") - link(rel="icon", type="image/png", sizes="32x32", href="/icons/favicon-32x32.png") - link(rel="icon", type="image/png", sizes="16x16", href="/icons/favicon-16x16.png") - link(rel="manifest", href="/icons/site.webmanifest") - link(rel="mask-icon", href="/icons/safari-pinned-tab.svg", color="#00aba9") - link(rel="shortcut icon", href="/icons/favicon.ico") - meta(name="apple-mobile-web-app-title", content="Connect") - meta(name="application-name", content="Connect") - meta(name="msapplication-TileColor", content="#00aba9") - meta(name="msapplication-config", content="/icons/browserconfig.xml") - meta(name="theme-color", content="#ffffff") - - // FontAwesome - link(rel='stylesheet', href='https://use.fontawesome.com/releases/v5.7.2/css/all.css', integrity='sha384-fnmOCqbTlWIlj8LyTjo7mOUStjsKC4pOpQbqyi7RrhN7udi9RwhKkMHpvLbHG9Sr', crossorigin='anonymous') - - // Import Google Icon Font - link(href='https://fonts.googleapis.com/icon?family=Material+Icons', rel='stylesheet') - - // Materialize CSS - link(rel='stylesheet', href='https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css') - - body() - - block content - - // JavaScript at end of body for optimized loading - // Materialize JavaScript - script(type='text/javascript', src='https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/js/materialize.min.js') - - // Jitsi - // https://github.com/jitsi/jitsi-meet/blob/master/doc/api.md - script(type='text/javascript', src='https://meet.jit.si/external_api.js') - - // Paho MQTT - // https://github.com/eclipse/paho.mqtt.javascript - script(type='text/javascript', src='https://cdnjs.cloudflare.com/ajax/libs/paho-mqtt/1.1.0/paho-mqtt.min.js') - - // Custom Events - script(type='module', src='/js/main.js') diff --git a/webapp/views/mobile.ejs b/webapp/views/mobile.ejs new file mode 100644 index 0000000..7913307 --- /dev/null +++ b/webapp/views/mobile.ejs @@ -0,0 +1,45 @@ +<%- include('header') -%> + + + + + + + + + +
+

© 2020 hapi-robo st, Inc. — All Rights Reserved.

+
+ + + + + + + + + + + + + + + + + + + diff --git a/webapp/views/mobile.pug b/webapp/views/mobile.pug deleted file mode 100644 index bdc5d89..0000000 --- a/webapp/views/mobile.pug +++ /dev/null @@ -1,23 +0,0 @@ -extends layout - -block content - - // robot selection menu - div#robot-menu(style='display:none') - div.row.valign-wrapper(style='height:80vh') - div.input-field.col.l2.offset-l5.m4.offset-m4.s6.offset-s3 - input#temi_id.validate.white-text(type='text') - label(for='temi-id') temi ID - div#robot-collection - - // waypoint selection menu - div#waypoint-menu(style='display:none') - div.row.valign-wrapper(style='height:80vh') - div.input-field.col.l2.offset-l5.m4.offset-m4.s6.offset-s3 - div.center-align.white-text - //- i#battery-state - div#waypoint-collection - - // footer - div.white-text(style='position:relative') - p(style='position:fixed; bottom:0; width:100%; text-align:center') © 2020 hapi-robo st, Inc. — All Rights Reserved. diff --git a/webapp/views/test.html b/webapp/views/test.html deleted file mode 100644 index b264af3..0000000 --- a/webapp/views/test.html +++ /dev/null @@ -1,49 +0,0 @@ - - - - Test - - - - - - - - - -
- - - - -
-

Footer Text

-
- - - - - - - - \ No newline at end of file From d9bfcbbe6dcbfb9b7a18507f76e5015117b1b104 Mon Sep 17 00:00:00 2001 From: Raymond Oung Date: Mon, 27 Apr 2020 18:36:28 +0900 Subject: [PATCH 02/30] Adds Oauth2.0 login --- .gitignore | 1 + webapp/app.js | 97 ++-- webapp/config/passport-setup.js | 79 +++ webapp/models/user-model.js | 12 + webapp/package-lock.json | 891 +++++++++++++++++--------------- webapp/package.json | 11 +- webapp/routes/auth-routes.js | 37 ++ webapp/routes/profile-routes.js | 16 + webapp/views/header.ejs | 9 +- webapp/views/home.ejs | 14 + webapp/views/index.ejs | 77 +-- webapp/views/login.ejs | 15 + webapp/views/profile.ejs | 83 +++ 13 files changed, 783 insertions(+), 559 deletions(-) create mode 100644 webapp/config/passport-setup.js create mode 100644 webapp/models/user-model.js create mode 100644 webapp/routes/auth-routes.js create mode 100644 webapp/routes/profile-routes.js create mode 100644 webapp/views/home.ejs create mode 100644 webapp/views/login.ejs create mode 100644 webapp/views/profile.ejs diff --git a/.gitignore b/.gitignore index 983688e..4511204 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,4 @@ android/build android/captures android/.externalNativeBuild android/.cxx +webapp/config/keys.js \ No newline at end of file diff --git a/webapp/app.js b/webapp/app.js index 7b48aaa..b8b42e8 100644 --- a/webapp/app.js +++ b/webapp/app.js @@ -1,94 +1,57 @@ /** * Main backend NodeJS file. * - * Starts an HTTP server. - * - * Reference: - * https://developer.mozilla.org/en-US/docs/Learn/Server-side/Express_Nodejs/skeleton_website - * */ - -// required libraries const express = require("express"); const path = require("path"); -const https = require("https"); -const fs = require("fs"); +const cookieSession = require('cookie-session'); +const passport = require('passport'); + +const authRoutes = require('./routes/auth-routes'); +const profileRoutes = require('./routes/profile-routes'); +const passportSetup = require('./config/passport-setup'); +const mongoose = require('mongoose'); +const keys = require('./config/keys'); + +// constants +const port = 8080; // instantiate webapp const app = express(); -const port = 8080; -/* - * Setup webapp - */ // setup template engine -app.set("views", path.join(__dirname, "views")); -app.set("view engine", "pug"); +app.set("view engine", "ejs"); // serve static files app.use(express.static(path.join(__dirname, "public"))); -/* - * Setup routes - */ -// mobile route -app.get("/mobile", function(req, res) { - res.render("mobile", {}); -}); +// set up session cookies +app.use(cookieSession({ + maxAge: 24 * 60 * 60 * 1000, + keys: [keys.session.cookieKey] +})); -// desktop route -app.get("/", function(req, res) { - res.render("index", { - title: " Connect" - }); -}); +// initialize passport and cookie session +app.use(passport.initialize()); +app.use(passport.session()); -/* - * Catch errors - */ -// catch 404 and forward to the error handler -app.use((req, res, next) => { - var err = new Error("Not Found"); - err.status = 404; - next(err); -}); +// connect to mongodb +mongoose.connect(keys.mongodb.dbURI, {useNewUrlParser: true, useUnifiedTopology: true}); -// catch errors handler and render error page -app.use((err, req, res, next) => { - // set locals, only providing error in development - res.locals.message = err.message; - res.locals.error = req.app.get("env") == "development" ? err : {}; +// set up routes +app.use('/auth', authRoutes); +app.use('/profile', profileRoutes); - // render the error page - res.status(err.status || 500); - res.render("error"); +// create home route +app.get("/", (req, res) => { + res.render('home', { user: req.user }); }); -/* - * Start listening on the port - */ -// HTTP +// start listening on the port app.listen(port, err => { if (err) { return console.log(`Something bad happened ${err}`); } else { - console.log(`Server is listening on localhost:${port}`); + console.log(`Server is listening on ${port}`); } }); - -// HTTPS -// https -// .createServer( -// { -// key: fs.readFileSync("./security/server.key"), -// cert: fs.readFileSync("./security/server.cert") -// }, -// app -// ) -// .listen(port, err => { -// if (err) { -// return console.log(`Something bad happened ${err}`); -// } else { -// console.log(`Server is listening on localhost:${port}`); -// } -// }); diff --git a/webapp/config/passport-setup.js b/webapp/config/passport-setup.js new file mode 100644 index 0000000..0419137 --- /dev/null +++ b/webapp/config/passport-setup.js @@ -0,0 +1,79 @@ +// reference: https://gist.github.com/joshbirk/1732068 +const passport = require('passport'); +const jwt = require('jsonwebtoken'); + +const keys = require('./keys'); +const User = require('../models/user-model'); + +const GitHubStrategy = require('passport-github').Strategy; +const AzureStrategy = require('passport-azure-ad-oauth2').Strategy; + +passport.serializeUser((user, cb) => { + cb(null, user.id); +}); + +passport.deserializeUser((id, cb) => { + User.findById(id).then((user) => { + cb(null, user); + }); +}); + +passport.use( + new GitHubStrategy({ + clientID: keys.github.clientID, + clientSecret: keys.github.clientSecret, + callbackURL: '/auth/github/redirect' + }, + (accessToken, refreshToken, profile, cb) => { + console.log(profile); + + // check if user already exists in our db + User.findOne({ githubId: profile.id }).then((currentUser) => { + if (currentUser) { + // user already exists + console.log('User Exists: ', currentUser); + cb(null, currentUser); + } else { + // if not, create user in our db + new User({ + username: profile.username, + githubId: profile.id + }).save().then((newUser) => { + console.log(`New user created: ${newUser}`); + cb(null, newUser); + }); + } + }); + }) +); + +passport.use( + new AzureStrategy({ + clientID: keys.azure.clientID, + clientSecret: keys.azure.clientSecret, + callbackURL: '/auth/azure/redirect' + }, + (accessToken, refreshToken, params, profile, cb) => { + const waadProfile = jwt.decode(params.id_token); + console.log(waadProfile); + + // check if user already exists in our db + User.findOne({ azureId: waadProfile.sub }).then((currentUser) => { + if (currentUser) { + // user already exists + console.log('User Exists: ', currentUser); + cb(null, currentUser); + } else { + // if not, create user in our db + // reference: https://docs.microsoft.com/en-us/azure/active-directory/develop/id-tokens + new User({ + username: waadProfile.name, + azureId: waadProfile.sub + }).save().then((newUser) => { + console.log(`New user created: ${newUser}`); + cb(null, newUser); + }); + } + }); + }) +); \ No newline at end of file diff --git a/webapp/models/user-model.js b/webapp/models/user-model.js new file mode 100644 index 0000000..3bb33ef --- /dev/null +++ b/webapp/models/user-model.js @@ -0,0 +1,12 @@ +const mongoose = require('mongoose'); +const Schema = mongoose.Schema; + +const userSchema = new Schema({ + username: String, + githubId: String, + azureId: String, +}); + +const User = mongoose.model('user', userSchema); + +module.exports = User; \ No newline at end of file diff --git a/webapp/package-lock.json b/webapp/package-lock.json index 2cb9e84..cdf45f8 100644 --- a/webapp/package-lock.json +++ b/webapp/package-lock.json @@ -1,6 +1,6 @@ { "name": "Connect", - "version": "0.0.1", + "version": "0.1.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -24,19 +24,6 @@ "js-tokens": "^4.0.0" } }, - "@types/babel-types": { - "version": "7.0.7", - "resolved": "https://registry.npmjs.org/@types/babel-types/-/babel-types-7.0.7.tgz", - "integrity": "sha512-dBtBbrc+qTHy1WdfHYjBwRln4+LWqASWakLHsWHR2NWHIFkv4W3O070IGoGLEBrJBvct3r0L1BUPuvURi7kYUQ==" - }, - "@types/babylon": { - "version": "6.16.5", - "resolved": "https://registry.npmjs.org/@types/babylon/-/babylon-6.16.5.tgz", - "integrity": "sha512-xH2e58elpj1X4ynnKp9qSnWlsRTIs6n3tgLGNfwAGHwePw0mulHQllV34n0T25uYSu1k0hRKkWXF890B1yS47w==", - "requires": { - "@types/babel-types": "*" - } - }, "@types/color-name": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", @@ -58,21 +45,6 @@ "integrity": "sha512-add7dgA5ppRPxCFJoAGfMDi7PIBXq1RtGo7BhbLaxwrXPOmw8gq48Y9ozT01hUKy9byMjlR20EJhu5zlkErEkg==", "dev": true }, - "acorn-globals": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-3.1.0.tgz", - "integrity": "sha1-/YJw9x+7SZawBPqIDuXUZXOnMb8=", - "requires": { - "acorn": "^4.0.4" - }, - "dependencies": { - "acorn": { - "version": "4.0.13", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz", - "integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c=" - } - } - }, "acorn-jsx": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.2.0.tgz", @@ -91,16 +63,6 @@ "uri-js": "^4.2.2" } }, - "align-text": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", - "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", - "requires": { - "kind-of": "^3.0.2", - "longest": "^1.0.1", - "repeat-string": "^1.5.2" - } - }, "ansi-escapes": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz", @@ -128,7 +90,6 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, "requires": { "color-convert": "^1.9.0" } @@ -168,47 +129,40 @@ "es-abstract": "^1.17.0-next.1" } }, - "asap": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" - }, "astral-regex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", "dev": true }, - "babel-runtime": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", - "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", - "requires": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" - } - }, - "babel-types": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", - "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", - "requires": { - "babel-runtime": "^6.26.0", - "esutils": "^2.0.2", - "lodash": "^4.17.4", - "to-fast-properties": "^1.0.3" - } - }, - "babylon": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", - "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==" + "async": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz", + "integrity": "sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0=" }, "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "base64url": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/base64url/-/base64url-3.0.1.tgz", + "integrity": "sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A==" + }, + "bl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-2.2.0.tgz", + "integrity": "sha512-wbgvOpqopSr7uq6fJrLH8EsvYMJf9gzfo2jCsL2eTy75qXPukA4pCgHamOQkZtY5vmfVtjB+P3LNlMHW5CEZXA==", + "requires": { + "readable-stream": "^2.3.5", + "safe-buffer": "^5.1.1" + } + }, + "bluebird": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz", + "integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA==" }, "body-parser": { "version": "1.19.0", @@ -246,12 +200,21 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, + "bson": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/bson/-/bson-1.1.4.tgz", + "integrity": "sha512-S/yKGU1syOMzO86+dGpg2qGoDL0zvzcb262G+gqEy6TgP6rt6z6qxSFX/8X6vLC91P7G7C3nLs0+bvDzmvBA3Q==" + }, + "buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=" + }, "bytes": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", @@ -263,53 +226,22 @@ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true }, - "camelcase": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", - "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=" - }, - "center-align": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", - "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", - "requires": { - "align-text": "^0.1.3", - "lazy-cache": "^1.0.3" - } - }, "chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, "requires": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", "supports-color": "^5.3.0" } }, - "character-parser": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/character-parser/-/character-parser-2.2.0.tgz", - "integrity": "sha1-x84o821LzZdE5f/CxfzeHHMmH8A=", - "requires": { - "is-regex": "^1.0.3" - } - }, "chardet": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", "dev": true }, - "clean-css": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.3.tgz", - "integrity": "sha512-VcMWDN54ZN/DS+g58HYL5/n4Zrqe8vHJpGA8KdgUXFU4fuP/aHNw8eld9SyEIyabIMJX/0RaY/fplOo5hYLSFA==", - "requires": { - "source-map": "~0.6.0" - } - }, "cli-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", @@ -325,21 +257,10 @@ "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", "dev": true }, - "cliui": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", - "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", - "requires": { - "center-align": "^0.1.1", - "right-align": "^0.1.1", - "wordwrap": "0.0.2" - } - }, "color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, "requires": { "color-name": "1.1.3" } @@ -347,14 +268,12 @@ "color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, "confusing-browser-globals": { "version": "1.0.9", @@ -362,17 +281,6 @@ "integrity": "sha512-KbS1Y0jMtyPgIxjO7ZzMAuUpAKMt1SzCL9fsrKsX6b0zJPTaT0SiSPmewwVZg9UAO83HVIlEhZF84LIjZ0lmAw==", "dev": true }, - "constantinople": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/constantinople/-/constantinople-3.1.2.tgz", - "integrity": "sha512-yePcBqEFhLOqSBtwYOGGS1exHo/s1xjekXiinh4itpNQGCu4KA1euPh1fg07N2wMITZXQkBz75Ntdt1ctGZouw==", - "requires": { - "@types/babel-types": "^7.0.0", - "@types/babylon": "^6.16.2", - "babel-types": "^6.26.0", - "babylon": "^6.18.0" - } - }, "contains-path": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz", @@ -397,15 +305,56 @@ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" }, + "cookie-session": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/cookie-session/-/cookie-session-1.4.0.tgz", + "integrity": "sha512-0hhwD+BUIwMXQraiZP/J7VP2YFzqo6g4WqZlWHtEHQ22t0MeZZrNBSCxC1zcaLAs8ApT3BzAKizx9gW/AP9vNA==", + "requires": { + "cookies": "0.8.0", + "debug": "2.6.9", + "on-headers": "~1.0.2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, "cookie-signature": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" }, - "core-js": { - "version": "2.6.11", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.11.tgz", - "integrity": "sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg==" + "cookies": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/cookies/-/cookies-0.8.0.tgz", + "integrity": "sha512-8aPsApQfebXnuI+537McwYsDtjVxGm8gTIzQI3FDW6t5t/DAhERxtnbEPN/8RX+uZthoz4eCOgloXaE5cYyNow==", + "requires": { + "depd": "~2.0.0", + "keygrip": "~1.1.0" + }, + "dependencies": { + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" + } + } + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" }, "cross-spawn": { "version": "6.0.5", @@ -437,11 +386,6 @@ "ms": "^2.1.1" } }, - "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" - }, "deep-is": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", @@ -457,6 +401,11 @@ "object-keys": "^1.0.12" } }, + "denque": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/denque/-/denque-1.4.1.tgz", + "integrity": "sha512-OfzPuSZKGcgr96rf1oODnfjqBFmr1DVoc/TrItj3Ohe0Ah1C5WX5Baquw/9U9KovnQ88EqmJbD66rKYUQYN1tQ==" + }, "depd": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", @@ -476,16 +425,27 @@ "esutils": "^2.0.2" } }, - "doctypes": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/doctypes/-/doctypes-1.1.0.tgz", - "integrity": "sha1-6oCxBqh1OHdOijpKWv4pPeSJ4Kk=" + "ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "requires": { + "safe-buffer": "^5.0.1" + } }, "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" }, + "ejs": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.2.tgz", + "integrity": "sha512-zFuywxrAWtX5Mk2KAuoJNkXXbfezpNA0v7i+YC971QORguPekpjpAgeOv99YWSdKXwj7JxI2QAWDeDkE8fWtXw==", + "requires": { + "jake": "^10.6.1" + } + }, "emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -544,8 +504,7 @@ "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" }, "eslint": { "version": "6.8.0", @@ -773,7 +732,8 @@ "esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true }, "etag": { "version": "1.8.1", @@ -879,6 +839,14 @@ "flat-cache": "^2.0.1" } }, + "filelist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.1.tgz", + "integrity": "sha512-8zSK6Nu0DQIC08mUC46sWGXi+q3GGpKydAG36k+JDba6VRpkevvOWUW5a/PhShij4+vHT9M+ghgG7eM+a9JDUQ==", + "requires": { + "minimatch": "^3.0.4" + } + }, "finalhandler": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", @@ -953,7 +921,8 @@ "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true }, "functional-red-black-tree": { "version": "1.0.1", @@ -1003,6 +972,7 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, "requires": { "function-bind": "^1.1.1" } @@ -1010,8 +980,7 @@ "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" }, "has-symbols": { "version": "1.0.1", @@ -1087,8 +1056,7 @@ "inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "inquirer": { "version": "7.1.0", @@ -1183,11 +1151,6 @@ "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", "dev": true }, - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" - }, "is-callable": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz", @@ -1200,22 +1163,6 @@ "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==", "dev": true }, - "is-expression": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-expression/-/is-expression-3.0.0.tgz", - "integrity": "sha1-Oayqa+f9HzRx3ELHQW5hwkMXrJ8=", - "requires": { - "acorn": "~4.0.2", - "object-assign": "^4.0.1" - }, - "dependencies": { - "acorn": { - "version": "4.0.13", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz", - "integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c=" - } - } - }, "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -1240,12 +1187,14 @@ "is-promise": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", - "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=" + "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", + "dev": true }, "is-regex": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz", "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==", + "dev": true, "requires": { "has": "^1.0.3" } @@ -1268,8 +1217,7 @@ "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" }, "isexe": { "version": "2.0.0", @@ -1277,10 +1225,16 @@ "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", "dev": true }, - "js-stringify": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/js-stringify/-/js-stringify-1.0.2.tgz", - "integrity": "sha1-Fzb939lyTyijaCrcYjCufk6Weds=" + "jake": { + "version": "10.6.1", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.6.1.tgz", + "integrity": "sha512-pHUK3+V0BjOb1XSi95rbBksrMdIqLVC9bJqDnshVyleYsET3H0XAq+3VB2E3notcYvv4wRdRHn13p7vobG+wfQ==", + "requires": { + "async": "0.9.x", + "chalk": "^2.4.2", + "filelist": "^1.0.1", + "minimatch": "^3.0.4" + } }, "js-tokens": { "version": "4.0.0", @@ -1310,27 +1264,61 @@ "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", "dev": true }, - "jstransformer": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/jstransformer/-/jstransformer-1.0.0.tgz", - "integrity": "sha1-7Yvwkh4vPx7U1cGkT2hwntJHIsM=", + "jsonwebtoken": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz", + "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==", + "requires": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^5.6.0" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + } + } + }, + "jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", "requires": { - "is-promise": "^2.0.0", - "promise": "^7.0.1" + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" } }, - "kind-of": { + "jws": { "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", "requires": { - "is-buffer": "^1.1.5" + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" } }, - "lazy-cache": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", - "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=" + "kareem": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.3.1.tgz", + "integrity": "sha512-l3hLhffs9zqoDe8zjmb/mAN4B8VT3L56EUvKNqLFVs9YlFA+zx7ke1DO8STAdDyYNkeSo1nKmjuvQeI12So8Xw==" + }, + "keygrip": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/keygrip/-/keygrip-1.1.0.tgz", + "integrity": "sha512-iYSchDJ+liQ8iwbSI2QqsQOvqv58eJCEanyJPJi+Khyu8smkcKSFUCbPwzFcL7YVtZ6eONjqRX/38caJ7QjRAQ==", + "requires": { + "tsscmp": "1.0.6" + } }, "levn": { "version": "0.3.0", @@ -1367,18 +1355,55 @@ "lodash": { "version": "4.17.15", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", + "dev": true }, - "longest": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", - "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=" + "lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8=" + }, + "lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY=" + }, + "lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha1-YZwK89A/iwTDH1iChAt3sRzWg0M=" + }, + "lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w=" + }, + "lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=" + }, + "lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=" + }, + "lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=" }, "media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" }, + "memory-pager": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", + "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==", + "optional": true + }, "merge-descriptors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", @@ -1417,7 +1442,6 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, "requires": { "brace-expansion": "^1.1.7" } @@ -1437,11 +1461,78 @@ "minimist": "0.0.8" } }, + "mongodb": { + "version": "3.5.6", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.5.6.tgz", + "integrity": "sha512-sh3q3GLDLT4QmoDLamxtAECwC3RGjq+oNuK1ENV8+tnipIavss6sMYt77hpygqlMOCt0Sla5cl7H4SKCVBCGEg==", + "requires": { + "bl": "^2.2.0", + "bson": "^1.1.4", + "denque": "^1.4.1", + "require_optional": "^1.0.1", + "safe-buffer": "^5.1.2", + "saslprep": "^1.0.0" + } + }, + "mongoose": { + "version": "5.9.10", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-5.9.10.tgz", + "integrity": "sha512-w1HNukfJzzDLfcI1f79h2Wj4ogVbf+X8hRkyFgqlcjK7OnDlAgahjDMIsT+mCS9jKojrMhjSsZIs9FiRPkLqMg==", + "requires": { + "bson": "^1.1.4", + "kareem": "2.3.1", + "mongodb": "3.5.6", + "mongoose-legacy-pluralize": "1.0.2", + "mpath": "0.7.0", + "mquery": "3.2.2", + "ms": "2.1.2", + "regexp-clone": "1.0.0", + "safe-buffer": "5.1.2", + "sift": "7.0.1", + "sliced": "1.0.1" + } + }, + "mongoose-legacy-pluralize": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/mongoose-legacy-pluralize/-/mongoose-legacy-pluralize-1.0.2.tgz", + "integrity": "sha512-Yo/7qQU4/EyIS8YDFSeenIvXxZN+ld7YdV9LqFVQJzTLye8unujAWPZ4NWKfFA+RNjh+wvTWKY9Z3E5XM6ZZiQ==" + }, + "mpath": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.7.0.tgz", + "integrity": "sha512-Aiq04hILxhz1L+f7sjGyn7IxYzWm1zLNNXcfhDtx04kZ2Gk7uvFdgZ8ts1cWa/6d0TQmag2yR8zSGZUmp0tFNg==" + }, + "mquery": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/mquery/-/mquery-3.2.2.tgz", + "integrity": "sha512-XB52992COp0KP230I3qloVUbkLUxJIu328HBP2t2EsxSFtf4W1HPSOBWOXf1bqxK4Xbb66lfMJ+Bpfd9/yZE1Q==", + "requires": { + "bluebird": "3.5.1", + "debug": "3.1.0", + "regexp-clone": "^1.0.0", + "safe-buffer": "5.1.2", + "sliced": "1.0.1" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "mute-stream": { "version": "0.0.8", @@ -1486,10 +1577,10 @@ } } }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + "oauth": { + "version": "0.9.15", + "resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz", + "integrity": "sha1-vR/vr2hslrdUda7VGWQS/2DPucE=" }, "object-inspect": { "version": "1.7.0", @@ -1547,6 +1638,11 @@ "ee-first": "1.1.1" } }, + "on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==" + }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -1632,6 +1728,75 @@ "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" }, + "passport": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/passport/-/passport-0.4.1.tgz", + "integrity": "sha512-IxXgZZs8d7uFSt3eqNjM9NQ3g3uQCW5avD8mRNoXV99Yig50vjuaez6dQK2qC0kVWPRTujxY0dWgGfT09adjYg==", + "requires": { + "passport-strategy": "1.x.x", + "pause": "0.0.1" + } + }, + "passport-azure-ad-oauth2": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/passport-azure-ad-oauth2/-/passport-azure-ad-oauth2-0.0.4.tgz", + "integrity": "sha1-GgnYhp79Sv0JURbiKJQZYbOlwSA=", + "requires": { + "passport-oauth": "1.0.x" + } + }, + "passport-github": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/passport-github/-/passport-github-1.1.0.tgz", + "integrity": "sha1-jOHj/NYa11eOsd9ZWDnkrqEjVdQ=", + "requires": { + "passport-oauth2": "1.x.x" + } + }, + "passport-google-oauth20": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/passport-google-oauth20/-/passport-google-oauth20-2.0.0.tgz", + "integrity": "sha512-KSk6IJ15RoxuGq7D1UKK/8qKhNfzbLeLrG3gkLZ7p4A6DBCcv7xpyQwuXtWdpyR0+E0mwkpjY1VfPOhxQrKzdQ==", + "requires": { + "passport-oauth2": "1.x.x" + } + }, + "passport-oauth": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/passport-oauth/-/passport-oauth-1.0.0.tgz", + "integrity": "sha1-kK/2M4dUDwIImvKM2tOep/gNd98=", + "requires": { + "passport-oauth1": "1.x.x", + "passport-oauth2": "1.x.x" + } + }, + "passport-oauth1": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/passport-oauth1/-/passport-oauth1-1.1.0.tgz", + "integrity": "sha1-p96YiiEfnPRoc3cTDqdN8ycwyRg=", + "requires": { + "oauth": "0.9.x", + "passport-strategy": "1.x.x", + "utils-merge": "1.x.x" + } + }, + "passport-oauth2": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/passport-oauth2/-/passport-oauth2-1.5.0.tgz", + "integrity": "sha512-kqBt6vR/5VlCK8iCx1/KpY42kQ+NEHZwsSyt4Y6STiNjU+wWICG1i8ucc1FapXDGO15C5O5VZz7+7vRzrDPXXQ==", + "requires": { + "base64url": "3.x.x", + "oauth": "0.9.x", + "passport-strategy": "1.x.x", + "uid2": "0.0.x", + "utils-merge": "1.x.x" + } + }, + "passport-strategy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz", + "integrity": "sha1-tVOaqPwiWj0a0XlHbd8ja0QPUuQ=" + }, "path-exists": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", @@ -1653,7 +1818,8 @@ "path-parse": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", - "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==" + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "dev": true }, "path-to-regexp": { "version": "0.1.7", @@ -1669,6 +1835,11 @@ "pify": "^2.0.0" } }, + "pause": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", + "integrity": "sha1-HUCLP9t2kjuVQ9lvtMnf1TXZy10=" + }, "pify": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", @@ -1696,20 +1867,17 @@ "integrity": "sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==", "dev": true }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, "progress": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", "dev": true }, - "promise": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", - "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", - "requires": { - "asap": "~2.0.3" - } - }, "proxy-addr": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", @@ -1719,120 +1887,6 @@ "ipaddr.js": "1.9.1" } }, - "pug": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/pug/-/pug-2.0.4.tgz", - "integrity": "sha512-XhoaDlvi6NIzL49nu094R2NA6P37ijtgMDuWE+ofekDChvfKnzFal60bhSdiy8y2PBO6fmz3oMEIcfpBVRUdvw==", - "requires": { - "pug-code-gen": "^2.0.2", - "pug-filters": "^3.1.1", - "pug-lexer": "^4.1.0", - "pug-linker": "^3.0.6", - "pug-load": "^2.0.12", - "pug-parser": "^5.0.1", - "pug-runtime": "^2.0.5", - "pug-strip-comments": "^1.0.4" - } - }, - "pug-attrs": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/pug-attrs/-/pug-attrs-2.0.4.tgz", - "integrity": "sha512-TaZ4Z2TWUPDJcV3wjU3RtUXMrd3kM4Wzjbe3EWnSsZPsJ3LDI0F3yCnf2/W7PPFF+edUFQ0HgDL1IoxSz5K8EQ==", - "requires": { - "constantinople": "^3.0.1", - "js-stringify": "^1.0.1", - "pug-runtime": "^2.0.5" - } - }, - "pug-code-gen": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/pug-code-gen/-/pug-code-gen-2.0.2.tgz", - "integrity": "sha512-kROFWv/AHx/9CRgoGJeRSm+4mLWchbgpRzTEn8XCiwwOy6Vh0gAClS8Vh5TEJ9DBjaP8wCjS3J6HKsEsYdvaCw==", - "requires": { - "constantinople": "^3.1.2", - "doctypes": "^1.1.0", - "js-stringify": "^1.0.1", - "pug-attrs": "^2.0.4", - "pug-error": "^1.3.3", - "pug-runtime": "^2.0.5", - "void-elements": "^2.0.1", - "with": "^5.0.0" - } - }, - "pug-error": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/pug-error/-/pug-error-1.3.3.tgz", - "integrity": "sha512-qE3YhESP2mRAWMFJgKdtT5D7ckThRScXRwkfo+Erqga7dyJdY3ZquspprMCj/9sJ2ijm5hXFWQE/A3l4poMWiQ==" - }, - "pug-filters": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/pug-filters/-/pug-filters-3.1.1.tgz", - "integrity": "sha512-lFfjNyGEyVWC4BwX0WyvkoWLapI5xHSM3xZJFUhx4JM4XyyRdO8Aucc6pCygnqV2uSgJFaJWW3Ft1wCWSoQkQg==", - "requires": { - "clean-css": "^4.1.11", - "constantinople": "^3.0.1", - "jstransformer": "1.0.0", - "pug-error": "^1.3.3", - "pug-walk": "^1.1.8", - "resolve": "^1.1.6", - "uglify-js": "^2.6.1" - } - }, - "pug-lexer": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/pug-lexer/-/pug-lexer-4.1.0.tgz", - "integrity": "sha512-i55yzEBtjm0mlplW4LoANq7k3S8gDdfC6+LThGEvsK4FuobcKfDAwt6V4jKPH9RtiE3a2Akfg5UpafZ1OksaPA==", - "requires": { - "character-parser": "^2.1.1", - "is-expression": "^3.0.0", - "pug-error": "^1.3.3" - } - }, - "pug-linker": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/pug-linker/-/pug-linker-3.0.6.tgz", - "integrity": "sha512-bagfuHttfQOpANGy1Y6NJ+0mNb7dD2MswFG2ZKj22s8g0wVsojpRlqveEQHmgXXcfROB2RT6oqbPYr9EN2ZWzg==", - "requires": { - "pug-error": "^1.3.3", - "pug-walk": "^1.1.8" - } - }, - "pug-load": { - "version": "2.0.12", - "resolved": "https://registry.npmjs.org/pug-load/-/pug-load-2.0.12.tgz", - "integrity": "sha512-UqpgGpyyXRYgJs/X60sE6SIf8UBsmcHYKNaOccyVLEuT6OPBIMo6xMPhoJnqtB3Q3BbO4Z3Bjz5qDsUWh4rXsg==", - "requires": { - "object-assign": "^4.1.0", - "pug-walk": "^1.1.8" - } - }, - "pug-parser": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/pug-parser/-/pug-parser-5.0.1.tgz", - "integrity": "sha512-nGHqK+w07p5/PsPIyzkTQfzlYfuqoiGjaoqHv1LjOv2ZLXmGX1O+4Vcvps+P4LhxZ3drYSljjq4b+Naid126wA==", - "requires": { - "pug-error": "^1.3.3", - "token-stream": "0.0.1" - } - }, - "pug-runtime": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/pug-runtime/-/pug-runtime-2.0.5.tgz", - "integrity": "sha512-P+rXKn9un4fQY77wtpcuFyvFaBww7/91f3jHa154qU26qFAnOe6SW1CbIDcxiG5lLK9HazYrMCCuDvNgDQNptw==" - }, - "pug-strip-comments": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/pug-strip-comments/-/pug-strip-comments-1.0.4.tgz", - "integrity": "sha512-i5j/9CS4yFhSxHp5iKPHwigaig/VV9g+FgReLJWWHEHbvKsbqL0oP/K5ubuLco6Wu3Kan5p7u7qk8A4oLLh6vw==", - "requires": { - "pug-error": "^1.3.3" - } - }, - "pug-walk": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/pug-walk/-/pug-walk-1.1.8.tgz", - "integrity": "sha512-GMu3M5nUL3fju4/egXwZO0XLi6fW/K3T3VTgFQ14GxNi8btlxgT5qZL//JwZFm/2Fa64J/PNS8AZeys3wiMkVA==" - }, "punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", @@ -1881,10 +1935,24 @@ "read-pkg": "^2.0.0" } }, - "regenerator-runtime": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", - "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==" + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "regexp-clone": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/regexp-clone/-/regexp-clone-1.0.0.tgz", + "integrity": "sha512-TuAasHQNamyyJ2hb97IuBEif4qBHGjPHBS64sZwytpLEqtBQ1gPJTnOaQ6qmpET16cK14kkjbazl6+p0RRv0yw==" }, "regexpp": { "version": "2.0.1", @@ -1892,15 +1960,32 @@ "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==", "dev": true }, - "repeat-string": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=" + "require_optional": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require_optional/-/require_optional-1.0.1.tgz", + "integrity": "sha512-qhM/y57enGWHAe3v/NcwML6a3/vfESLe/sGM2dII+gEO0BpKRUkWZow/tyloNqJyN6kXSl3RyyM8Ll5D/sJP8g==", + "requires": { + "resolve-from": "^2.0.0", + "semver": "^5.1.0" + }, + "dependencies": { + "resolve-from": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz", + "integrity": "sha1-lICrIOlP+h2egKgEx+oUdhGWa1c=" + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + } + } }, "resolve": { "version": "1.15.1", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.15.1.tgz", "integrity": "sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w==", + "dev": true, "requires": { "path-parse": "^1.0.6" } @@ -1921,14 +2006,6 @@ "signal-exit": "^3.0.2" } }, - "right-align": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", - "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", - "requires": { - "align-text": "^0.1.1" - } - }, "rimraf": { "version": "2.6.3", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", @@ -1966,6 +2043,15 @@ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, + "saslprep": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/saslprep/-/saslprep-1.0.3.tgz", + "integrity": "sha512-/MY/PEMbk2SuY5sScONwhUDsV2p77Znkb/q3nSVstq/yQzYJOH/Azh29p9oJLsl3LnQwSvZDKagDGBsBwSooag==", + "optional": true, + "requires": { + "sparse-bitfield": "^3.0.3" + } + }, "semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", @@ -2045,6 +2131,11 @@ "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", "dev": true }, + "sift": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/sift/-/sift-7.0.1.tgz", + "integrity": "sha512-oqD7PMJ+uO6jV9EQCl0LrRw1OwsiPsiFQR5AR30heR+4Dl7jBBbDLnNvWiak20tzZlSE1H7RB30SX/1j/YYT7g==" + }, "signal-exit": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", @@ -2070,10 +2161,19 @@ } } }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + "sliced": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sliced/-/sliced-1.0.1.tgz", + "integrity": "sha1-CzpmK10Ewxd7GSa+qCsD+Dei70E=" + }, + "sparse-bitfield": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", + "integrity": "sha1-/0rm5oZWBWuks+eSqzM004JzyhE=", + "optional": true, + "requires": { + "memory-pager": "^1.0.2" + } }, "spdx-correct": { "version": "3.1.0", @@ -2160,6 +2260,14 @@ "function-bind": "^1.1.1" } }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + }, "strip-ansi": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", @@ -2193,7 +2301,6 @@ "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, "requires": { "has-flag": "^3.0.0" } @@ -2256,27 +2363,22 @@ "os-tmpdir": "~1.0.2" } }, - "to-fast-properties": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", - "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=" - }, "toidentifier": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" }, - "token-stream": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/token-stream/-/token-stream-0.0.1.tgz", - "integrity": "sha1-zu78cXp2xDFvEm0LnbqlXX598Bo=" - }, "tslib": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.11.1.tgz", "integrity": "sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA==", "dev": true }, + "tsscmp": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/tsscmp/-/tsscmp-1.0.6.tgz", + "integrity": "sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==" + }, "type-check": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", @@ -2301,28 +2403,10 @@ "mime-types": "~2.1.24" } }, - "uglify-js": { - "version": "2.8.29", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", - "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", - "requires": { - "source-map": "~0.5.1", - "uglify-to-browserify": "~1.0.0", - "yargs": "~3.10.0" - }, - "dependencies": { - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" - } - } - }, - "uglify-to-browserify": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", - "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=", - "optional": true + "uid2": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/uid2/-/uid2-0.0.3.tgz", + "integrity": "sha1-SDEm4Rd03y9xuLY53NeZw3YWK4I=" }, "unpipe": { "version": "1.0.0", @@ -2338,6 +2422,11 @@ "punycode": "^2.1.0" } }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, "utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", @@ -2364,11 +2453,6 @@ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" }, - "void-elements": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", - "integrity": "sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=" - }, "which": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", @@ -2378,38 +2462,12 @@ "isexe": "^2.0.0" } }, - "window-size": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", - "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=" - }, - "with": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/with/-/with-5.1.1.tgz", - "integrity": "sha1-+k2qktrzLE6pTtRTyB8EaGtXXf4=", - "requires": { - "acorn": "^3.1.0", - "acorn-globals": "^3.0.0" - }, - "dependencies": { - "acorn": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", - "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=" - } - } - }, "word-wrap": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", "dev": true }, - "wordwrap": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", - "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=" - }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -2424,17 +2482,6 @@ "requires": { "mkdirp": "^0.5.1" } - }, - "yargs": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", - "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", - "requires": { - "camelcase": "^1.0.2", - "cliui": "^2.1.0", - "decamelize": "^1.0.0", - "window-size": "0.1.0" - } } } } diff --git a/webapp/package.json b/webapp/package.json index 140a9e1..68a8f9c 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -1,6 +1,6 @@ { "name": "Connect", - "version": "0.0.1", + "version": "0.1.0", "description": "Jitsi-temi webapp", "private": true, "main": "app.js", @@ -10,8 +10,15 @@ "author": "R. Oung", "license": "MIT", "dependencies": { + "cookie-session": "^1.4.0", + "ejs": "^3.1.2", "express": "*", - "pug": "*" + "jsonwebtoken": "^8.5.1", + "mongoose": "^5.9.10", + "passport": "^0.4.1", + "passport-azure-ad-oauth2": "0.0.4", + "passport-github": "^1.1.0", + "passport-google-oauth20": "^2.0.0" }, "devDependencies": { "eslint": "^6.8.0", diff --git a/webapp/routes/auth-routes.js b/webapp/routes/auth-routes.js new file mode 100644 index 0000000..67d1d8f --- /dev/null +++ b/webapp/routes/auth-routes.js @@ -0,0 +1,37 @@ +const router = require('express').Router(); +const passport = require('passport'); + +// auth login +router.get('/login', (req, res) => { + res.render('login', { user: req.user }); +}); + +// auth logout +router.get('/logout', (req, res) => { + req.logout(); + res.redirect('/'); +}); + +// auth with github +router.get('/github', passport.authenticate('github', { + scope: ['profile'] +})); + +// callback route for github-oauth +// hand control to passport to use code to grab profile info +router.get('/github/redirect', + passport.authenticate('github', { failureRedirect: '/login' }), + (req, res) => { res.redirect('/profile'); } + ); + +// auth with azure +router.get('/azure', passport.authenticate('azure_ad_oauth2')); + +// callback route for azure-oauth +// hand control to passport to use code to grab profile info +router.get('/azure/redirect', + passport.authenticate('azure_ad_oauth2', { failureRedirect: '/login' }), + (req, res) => { res.redirect('/profile'); } + ); + +module.exports = router; diff --git a/webapp/routes/profile-routes.js b/webapp/routes/profile-routes.js new file mode 100644 index 0000000..3c9d875 --- /dev/null +++ b/webapp/routes/profile-routes.js @@ -0,0 +1,16 @@ +const router = require('express').Router(); + +// middleware to check if user is already logged in +const authCheck = (req, res, next) => { + if(!req.user){ + res.redirect('/auth/login'); + } else { + next(); + } +}; + +router.get('/', authCheck, (req, res) => { + res.render('profile', { user: req.user }); +}); + +module.exports = router; \ No newline at end of file diff --git a/webapp/views/header.ejs b/webapp/views/header.ejs index af5323f..3c13b40 100644 --- a/webapp/views/header.ejs +++ b/webapp/views/header.ejs @@ -8,7 +8,7 @@ - + @@ -24,9 +24,6 @@ - - - - - + + diff --git a/webapp/views/home.ejs b/webapp/views/home.ejs new file mode 100644 index 0000000..9b17835 --- /dev/null +++ b/webapp/views/home.ejs @@ -0,0 +1,14 @@ +<%- include('header') -%> + + + + +

Homepage

+ + + + diff --git a/webapp/views/index.ejs b/webapp/views/index.ejs index 59350dd..792acdf 100644 --- a/webapp/views/index.ejs +++ b/webapp/views/index.ejs @@ -1,71 +1,24 @@ <%- include('header') -%> -
- -
-
- - -
-
-
- - - - - -
-
-
- - -
-
- - -
-
- - -
-

© 2020 hapi-robo st, Inc. — All Rights Reserved.

-
- - - - + - - - - - - - - - - - - - + + diff --git a/webapp/views/login.ejs b/webapp/views/login.ejs new file mode 100644 index 0000000..d641614 --- /dev/null +++ b/webapp/views/login.ejs @@ -0,0 +1,15 @@ +<%- include('header') -%> + + + + + Github + Azure + + + + diff --git a/webapp/views/profile.ejs b/webapp/views/profile.ejs new file mode 100644 index 0000000..57652c8 --- /dev/null +++ b/webapp/views/profile.ejs @@ -0,0 +1,83 @@ +<%- include('header') -%> + + + + +
+ +
+
+ + +
+
+
+ + + + + +
+
+
+ + +
+
+ + +
+
+ + +
+

© 2020 hapi-robo st, Inc. — All Rights Reserved.

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + From bd36bbe32f5a905dc6fc1e376f70d4bfba042b9c Mon Sep 17 00:00:00 2001 From: Raymond Oung Date: Mon, 27 Apr 2020 23:09:26 +0900 Subject: [PATCH 03/30] Adds Microsoft logo to sign-in button and updates navigation bar --- webapp/app.js | 9 +- .../img/ms-symbollockup_mssymbol_19.svg | 1 + .../img/ms-symbollockup_signin_dark.svg | 1 + .../img/ms-symbollockup_signin_light.svg | 1 + webapp/public/js/main.js | 2 +- webapp/public/logo.svg | 1 + webapp/routes/auth-routes.js | 4 +- webapp/routes/console-routes.js | 16 ++++ webapp/views/console.ejs | 93 +++++++++++++++++++ webapp/views/home.ejs | 20 ++-- webapp/views/login.ejs | 40 ++++++-- 11 files changed, 166 insertions(+), 22 deletions(-) create mode 100644 webapp/public/img/ms-symbollockup_mssymbol_19.svg create mode 100644 webapp/public/img/ms-symbollockup_signin_dark.svg create mode 100644 webapp/public/img/ms-symbollockup_signin_light.svg create mode 100644 webapp/public/logo.svg create mode 100644 webapp/routes/console-routes.js create mode 100644 webapp/views/console.ejs diff --git a/webapp/app.js b/webapp/app.js index b8b42e8..1f8478f 100644 --- a/webapp/app.js +++ b/webapp/app.js @@ -6,13 +6,14 @@ const express = require("express"); const path = require("path"); const cookieSession = require('cookie-session'); const passport = require('passport'); +const mongoose = require('mongoose'); -const authRoutes = require('./routes/auth-routes'); -const profileRoutes = require('./routes/profile-routes'); const passportSetup = require('./config/passport-setup'); -const mongoose = require('mongoose'); const keys = require('./config/keys'); +const authRoutes = require('./routes/auth-routes'); +const consoleRoutes = require('./routes/console-routes'); + // constants const port = 8080; @@ -40,7 +41,7 @@ mongoose.connect(keys.mongodb.dbURI, {useNewUrlParser: true, useUnifiedTopology: // set up routes app.use('/auth', authRoutes); -app.use('/profile', profileRoutes); +app.use('/console', consoleRoutes); // create home route app.get("/", (req, res) => { diff --git a/webapp/public/img/ms-symbollockup_mssymbol_19.svg b/webapp/public/img/ms-symbollockup_mssymbol_19.svg new file mode 100644 index 0000000..1f73976 --- /dev/null +++ b/webapp/public/img/ms-symbollockup_mssymbol_19.svg @@ -0,0 +1 @@ +MS-SymbolLockup \ No newline at end of file diff --git a/webapp/public/img/ms-symbollockup_signin_dark.svg b/webapp/public/img/ms-symbollockup_signin_dark.svg new file mode 100644 index 0000000..cc8ba38 --- /dev/null +++ b/webapp/public/img/ms-symbollockup_signin_dark.svg @@ -0,0 +1 @@ +MS-SymbolLockup \ No newline at end of file diff --git a/webapp/public/img/ms-symbollockup_signin_light.svg b/webapp/public/img/ms-symbollockup_signin_light.svg new file mode 100644 index 0000000..a5fa6ea --- /dev/null +++ b/webapp/public/img/ms-symbollockup_signin_light.svg @@ -0,0 +1 @@ +MS-SymbolLockup \ No newline at end of file diff --git a/webapp/public/js/main.js b/webapp/public/js/main.js index febc67d..a4bbfa8 100644 --- a/webapp/public/js/main.js +++ b/webapp/public/js/main.js @@ -395,7 +395,7 @@ function connectMQTT(host, port) { client.connect(options); } -document.body.style.backgroundColor = 'black'; +// document.body.style.backgroundColor = 'black'; // @TODO Make this configurable // window.onload = connectMQTT('localhost', 9001); diff --git a/webapp/public/logo.svg b/webapp/public/logo.svg new file mode 100644 index 0000000..ad8e684 --- /dev/null +++ b/webapp/public/logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/webapp/routes/auth-routes.js b/webapp/routes/auth-routes.js index 67d1d8f..9d91f15 100644 --- a/webapp/routes/auth-routes.js +++ b/webapp/routes/auth-routes.js @@ -21,7 +21,7 @@ router.get('/github', passport.authenticate('github', { // hand control to passport to use code to grab profile info router.get('/github/redirect', passport.authenticate('github', { failureRedirect: '/login' }), - (req, res) => { res.redirect('/profile'); } + (req, res) => { res.redirect('/console'); } ); // auth with azure @@ -31,7 +31,7 @@ router.get('/azure', passport.authenticate('azure_ad_oauth2')); // hand control to passport to use code to grab profile info router.get('/azure/redirect', passport.authenticate('azure_ad_oauth2', { failureRedirect: '/login' }), - (req, res) => { res.redirect('/profile'); } + (req, res) => { res.redirect('/console'); } ); module.exports = router; diff --git a/webapp/routes/console-routes.js b/webapp/routes/console-routes.js new file mode 100644 index 0000000..e41fdb0 --- /dev/null +++ b/webapp/routes/console-routes.js @@ -0,0 +1,16 @@ +const router = require('express').Router(); + +// middleware to check if user is already logged in +const authCheck = (req, res, next) => { + if(!req.user){ + res.redirect('/auth/login'); + } else { + next(); + } +}; + +router.get('/', authCheck, (req, res) => { + res.render('console', { user: req.user }); +}); + +module.exports = router; \ No newline at end of file diff --git a/webapp/views/console.ejs b/webapp/views/console.ejs new file mode 100644 index 0000000..6df9f9c --- /dev/null +++ b/webapp/views/console.ejs @@ -0,0 +1,93 @@ +<%- include('header') -%> + + + + + + +
+ +
+
+ + +
+
+
+ + + + + +
+
+
+ + +
+
+ + +
+
+ + +
+

© 2020 hapi-robo st, Inc. — All Rights Reserved.

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/webapp/views/home.ejs b/webapp/views/home.ejs index 9b17835..c405080 100644 --- a/webapp/views/home.ejs +++ b/webapp/views/home.ejs @@ -1,13 +1,19 @@ <%- include('header') -%> - - -

Homepage

+ diff --git a/webapp/views/login.ejs b/webapp/views/login.ejs index d641614..5145211 100644 --- a/webapp/views/login.ejs +++ b/webapp/views/login.ejs @@ -1,14 +1,38 @@ <%- include('header') -%> - - - Github - Azure + + +
+ + +
+ + +
+
+ + + + +
+
+
From b012c4902026532a77cd7d3a020c83f9b883a7fb Mon Sep 17 00:00:00 2001 From: Raymond Oung Date: Tue, 28 Apr 2020 19:18:45 +0900 Subject: [PATCH 04/30] Adds dropdown for navigation bar; Adds Serial Number input field --- webapp/app.js | 2 +- webapp/config/passport-setup.js | 6 +- webapp/models/user-model.js | 5 +- webapp/package-lock.json | 793 ++++++++++++++++++++++++++++++-- webapp/package.json | 4 +- webapp/public/js/main.js | 51 +- webapp/routes/profile-routes.js | 16 - webapp/views/console.ejs | 116 +++-- webapp/views/home.ejs | 32 +- webapp/views/login.ejs | 8 +- webapp/views/mobile.ejs | 45 -- 11 files changed, 892 insertions(+), 186 deletions(-) delete mode 100644 webapp/routes/profile-routes.js delete mode 100644 webapp/views/mobile.ejs diff --git a/webapp/app.js b/webapp/app.js index 1f8478f..0952a30 100644 --- a/webapp/app.js +++ b/webapp/app.js @@ -45,7 +45,7 @@ app.use('/console', consoleRoutes); // create home route app.get("/", (req, res) => { - res.render('home', { user: req.user }); + res.render('login', { user: req.user }); }); // start listening on the port diff --git a/webapp/config/passport-setup.js b/webapp/config/passport-setup.js index 0419137..481c5a7 100644 --- a/webapp/config/passport-setup.js +++ b/webapp/config/passport-setup.js @@ -54,6 +54,7 @@ passport.use( callbackURL: '/auth/azure/redirect' }, (accessToken, refreshToken, params, profile, cb) => { + console.log(params); const waadProfile = jwt.decode(params.id_token); console.log(waadProfile); @@ -67,7 +68,10 @@ passport.use( // if not, create user in our db // reference: https://docs.microsoft.com/en-us/azure/active-directory/develop/id-tokens new User({ - username: waadProfile.name, + userName: waadProfile.name, + firstName: waadProfile.given_name, + lastName: waadProfile.family_name, + email: waadProfile.upn, azureId: waadProfile.sub }).save().then((newUser) => { console.log(`New user created: ${newUser}`); diff --git a/webapp/models/user-model.js b/webapp/models/user-model.js index 3bb33ef..4752401 100644 --- a/webapp/models/user-model.js +++ b/webapp/models/user-model.js @@ -2,7 +2,10 @@ const mongoose = require('mongoose'); const Schema = mongoose.Schema; const userSchema = new Schema({ - username: String, + userName: String, + lastName: String, + firstName: String, + email: String, githubId: String, azureId: String, }); diff --git a/webapp/package-lock.json b/webapp/package-lock.json index cdf45f8..18e4f9e 100644 --- a/webapp/package-lock.json +++ b/webapp/package-lock.json @@ -24,11 +24,28 @@ "js-tokens": "^4.0.0" } }, + "@sindresorhus/is": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", + "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==" + }, + "@szmarczak/http-timer": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", + "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", + "requires": { + "defer-to-connect": "^1.0.1" + } + }, "@types/color-name": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", - "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", - "dev": true + "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==" + }, + "abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" }, "accepts": { "version": "1.3.7", @@ -63,6 +80,36 @@ "uri-js": "^4.2.2" } }, + "ansi-align": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.0.tgz", + "integrity": "sha512-ZpClVKqXN3RGBmKibdfWzqCY4lnjEuoNzU5T0oEFpfd/z5qJHVarukridD4juLO2FXMiwUQxr9WqQtaYa8XRYw==", + "requires": { + "string-width": "^3.0.0" + }, + "dependencies": { + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + } + } + }, "ansi-escapes": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz", @@ -83,8 +130,7 @@ "ansi-regex": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "dev": true + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==" }, "ansi-styles": { "version": "3.2.1", @@ -94,6 +140,15 @@ "color-convert": "^1.9.0" } }, + "anymatch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", + "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, "argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -150,6 +205,11 @@ "resolved": "https://registry.npmjs.org/base64url/-/base64url-3.0.1.tgz", "integrity": "sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A==" }, + "binary-extensions": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.0.0.tgz", + "integrity": "sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow==" + }, "bl": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/bl/-/bl-2.2.0.tgz", @@ -196,6 +256,67 @@ } } }, + "boxen": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-4.2.0.tgz", + "integrity": "sha512-eB4uT9RGzg2odpER62bBwSLvUeGC+WbRjjyyFhGsKnc8wp/m0+hQsMUvUe3H2V0D5vw0nBdO1hCJoZo5mKeuIQ==", + "requires": { + "ansi-align": "^3.0.0", + "camelcase": "^5.3.1", + "chalk": "^3.0.0", + "cli-boxes": "^2.2.0", + "string-width": "^4.1.0", + "term-size": "^2.1.0", + "type-fest": "^0.8.1", + "widest-line": "^3.1.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -205,6 +326,14 @@ "concat-map": "0.0.1" } }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "requires": { + "fill-range": "^7.0.1" + } + }, "bson": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/bson/-/bson-1.1.4.tgz", @@ -220,12 +349,46 @@ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" }, + "cacheable-request": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", + "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", + "requires": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^3.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^4.1.0", + "responselike": "^1.0.2" + }, + "dependencies": { + "get-stream": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz", + "integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==", + "requires": { + "pump": "^3.0.0" + } + }, + "lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==" + } + } + }, "callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" + }, "chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", @@ -242,6 +405,31 @@ "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", "dev": true }, + "chokidar": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.0.tgz", + "integrity": "sha512-aXAaho2VJtisB/1fg1+3nlLJqGOuewTzQpd/Tz0yTg2R0e4IGtshYvtjowyEumcBv2z+y4+kc75Mz7j5xJskcQ==", + "requires": { + "anymatch": "~3.1.1", + "braces": "~3.0.2", + "fsevents": "~2.1.2", + "glob-parent": "~5.1.0", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.4.0" + } + }, + "ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==" + }, + "cli-boxes": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.0.tgz", + "integrity": "sha512-gpaBrMAizVEANOpfZp/EEUixTXDyGt7DFzdK5hU+UbWt/J0lB0w20ncZj59Z9a93xHb9u12zF5BS6i9RKbtg4w==" + }, "cli-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", @@ -257,6 +445,14 @@ "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", "dev": true }, + "clone-response": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", + "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", + "requires": { + "mimic-response": "^1.0.0" + } + }, "color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -275,6 +471,19 @@ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, + "configstore": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz", + "integrity": "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==", + "requires": { + "dot-prop": "^5.2.0", + "graceful-fs": "^4.1.2", + "make-dir": "^3.0.0", + "unique-string": "^2.0.0", + "write-file-atomic": "^3.0.0", + "xdg-basedir": "^4.0.0" + } + }, "confusing-browser-globals": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.9.tgz", @@ -377,6 +586,11 @@ } } }, + "crypto-random-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", + "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==" + }, "debug": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", @@ -386,12 +600,30 @@ "ms": "^2.1.1" } }, + "decompress-response": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", + "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", + "requires": { + "mimic-response": "^1.0.0" + } + }, + "deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" + }, "deep-is": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", "dev": true }, + "defer-to-connect": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", + "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==" + }, "define-properties": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", @@ -425,6 +657,19 @@ "esutils": "^2.0.2" } }, + "dot-prop": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.2.0.tgz", + "integrity": "sha512-uEUyaDKoSQ1M4Oq8l45hSE26SnTxL6snNnqvK/VWx5wJhmff5z0FUVJDKDanor/6w3kzE3i7XZOk+7wC0EXr1A==", + "requires": { + "is-obj": "^2.0.0" + } + }, + "duplexer3": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", + "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=" + }, "ecdsa-sig-formatter": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", @@ -449,14 +694,21 @@ "emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, "encodeurl": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" }, + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "requires": { + "once": "^1.4.0" + } + }, "error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -496,6 +748,11 @@ "is-symbol": "^1.0.2" } }, + "escape-goat": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-2.1.1.tgz", + "integrity": "sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q==" + }, "escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", @@ -847,6 +1104,14 @@ "minimatch": "^3.0.4" } }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "requires": { + "to-regex-range": "^5.0.1" + } + }, "finalhandler": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", @@ -918,6 +1183,12 @@ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true }, + "fsevents": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", + "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", + "optional": true + }, "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", @@ -930,6 +1201,14 @@ "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", "dev": true }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "requires": { + "pump": "^3.0.0" + } + }, "glob": { "version": "7.1.6", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", @@ -948,11 +1227,18 @@ "version": "5.1.0", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.0.tgz", "integrity": "sha512-qjtRgnIVmOfnKUE3NJAQEdk+lKrxfw8t5ke7SXtfMTHcjsBfOfWXCQfdb30zfDoZQ2IRSIiidmjtbHZPZ++Ihw==", - "dev": true, "requires": { "is-glob": "^4.0.1" } }, + "global-dirs": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-2.0.1.tgz", + "integrity": "sha512-5HqUqdhkEovj2Of/ms3IeS/EekcO54ytHRLV4PEY2rhRwrHXLQjeVEES0Lhka0xwNDtGYn58wyC4s5+MHsOO6A==", + "requires": { + "ini": "^1.3.5" + } + }, "globals": { "version": "12.4.0", "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", @@ -962,11 +1248,28 @@ "type-fest": "^0.8.1" } }, + "got": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", + "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", + "requires": { + "@sindresorhus/is": "^0.14.0", + "@szmarczak/http-timer": "^1.1.2", + "cacheable-request": "^6.0.0", + "decompress-response": "^3.3.0", + "duplexer3": "^0.1.4", + "get-stream": "^4.1.0", + "lowercase-keys": "^1.0.1", + "mimic-response": "^1.0.1", + "p-cancelable": "^1.0.0", + "to-readable-stream": "^1.0.0", + "url-parse-lax": "^3.0.0" + } + }, "graceful-fs": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", - "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", - "dev": true + "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==" }, "has": { "version": "1.0.3", @@ -988,12 +1291,22 @@ "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", "dev": true }, + "has-yarn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-2.1.0.tgz", + "integrity": "sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==" + }, "hosted-git-info": { "version": "2.8.8", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", "dev": true }, + "http-cache-semantics": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", + "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==" + }, "http-errors": { "version": "1.7.2", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", @@ -1027,6 +1340,11 @@ "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", "dev": true }, + "ignore-by-default": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", + "integrity": "sha1-SMptcvbGo68Aqa1K5odr44ieKwk=" + }, "import-fresh": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz", @@ -1037,11 +1355,15 @@ "resolve-from": "^4.0.0" } }, + "import-lazy": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", + "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=" + }, "imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=" }, "inflight": { "version": "1.0.6", @@ -1058,6 +1380,11 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, + "ini": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==" + }, "inquirer": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.1.0.tgz", @@ -1151,12 +1478,28 @@ "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", "dev": true }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "requires": { + "binary-extensions": "^2.0.0" + } + }, "is-callable": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz", "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==", "dev": true }, + "is-ci": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", + "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", + "requires": { + "ci-info": "^2.0.0" + } + }, "is-date-object": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", @@ -1166,24 +1509,50 @@ "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" }, "is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" }, "is-glob": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", - "dev": true, "requires": { "is-extglob": "^2.1.1" } }, + "is-installed-globally": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.3.2.tgz", + "integrity": "sha512-wZ8x1js7Ia0kecP/CHM/3ABkAmujX7WPvQk6uu3Fly/Mk44pySulQpnHG46OMjHGXApINnV4QhY3SWnECO2z5g==", + "requires": { + "global-dirs": "^2.0.1", + "is-path-inside": "^3.0.1" + } + }, + "is-npm": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-4.0.0.tgz", + "integrity": "sha512-96ECIfh9xtDDlPylNPXhzjsykHsMJZ18ASpaWzQyBr4YRTcVjUvzaHayDAES2oU/3KpljhHUjtSRNiDwi0F0ig==" + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" + }, + "is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==" + }, + "is-path-inside": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.2.tgz", + "integrity": "sha512-/2UGPSgmtqwo1ktx8NDHjuPwZWmHhO+gj0f93EkhLB5RgW9RZevWYYlIkS6zePc6U2WpOdQYIwHe9YC4DWEBVg==" + }, "is-promise": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", @@ -1214,6 +1583,16 @@ "has-symbols": "^1.0.1" } }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" + }, + "is-yarn-global": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.3.0.tgz", + "integrity": "sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==" + }, "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", @@ -1252,6 +1631,11 @@ "esprima": "^4.0.0" } }, + "json-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", + "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=" + }, "json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -1320,6 +1704,22 @@ "tsscmp": "1.0.6" } }, + "keyv": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", + "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", + "requires": { + "json-buffer": "3.0.0" + } + }, + "latest-version": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-5.1.0.tgz", + "integrity": "sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA==", + "requires": { + "package-json": "^6.3.0" + } + }, "levn": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", @@ -1393,6 +1793,19 @@ "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=" }, + "lowercase-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==" + }, + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "requires": { + "semver": "^6.0.0" + } + }, "media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -1438,6 +1851,11 @@ "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", "dev": true }, + "mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==" + }, "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", @@ -1557,6 +1975,46 @@ "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", "dev": true }, + "nodemon": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.3.tgz", + "integrity": "sha512-lLQLPS90Lqwc99IHe0U94rDgvjo+G9I4uEIxRG3evSLROcqQ9hwc0AxlSHKS4T1JW/IMj/7N5mthiN58NL/5kw==", + "requires": { + "chokidar": "^3.2.2", + "debug": "^3.2.6", + "ignore-by-default": "^1.0.1", + "minimatch": "^3.0.4", + "pstree.remy": "^1.1.7", + "semver": "^5.7.1", + "supports-color": "^5.5.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.2", + "update-notifier": "^4.0.0" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "requires": { + "ms": "^2.1.1" + } + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + } + } + }, + "nopt": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", + "integrity": "sha1-bd0hvSoxQXuScn3Vhfim83YI6+4=", + "requires": { + "abbrev": "1" + } + }, "normalize-package-data": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", @@ -1577,6 +2035,16 @@ } } }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" + }, + "normalize-url": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.0.tgz", + "integrity": "sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==" + }, "oauth": { "version": "0.9.15", "resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz", @@ -1647,7 +2115,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, "requires": { "wrappy": "1" } @@ -1681,6 +2148,11 @@ "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", "dev": true }, + "p-cancelable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", + "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==" + }, "p-limit": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", @@ -1705,6 +2177,17 @@ "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", "dev": true }, + "package-json": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/package-json/-/package-json-6.5.0.tgz", + "integrity": "sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ==", + "requires": { + "got": "^9.6.0", + "registry-auth-token": "^4.0.0", + "registry-url": "^5.0.0", + "semver": "^6.2.0" + } + }, "parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -1840,6 +2323,11 @@ "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", "integrity": "sha1-HUCLP9t2kjuVQ9lvtMnf1TXZy10=" }, + "picomatch": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", + "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==" + }, "pify": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", @@ -1861,6 +2349,11 @@ "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", "dev": true }, + "prepend-http": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", + "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=" + }, "prettier": { "version": "1.19.1", "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz", @@ -1887,12 +2380,34 @@ "ipaddr.js": "1.9.1" } }, + "pstree.remy": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.7.tgz", + "integrity": "sha512-xsMgrUwRpuGskEzBFkH8NmTimbZ5PcPup0LA8JJkHIm2IMUbQcpo3yeLNWVrufEYjh8YwtSVh0xz6UeWc5Oh5A==" + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, "punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", "dev": true }, + "pupa": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pupa/-/pupa-2.0.1.tgz", + "integrity": "sha512-hEJH0s8PXLY/cdXh66tNEQGndDrIKNqNC5xmrysZy3i5C3oEoLna7YAOad+7u125+zH1HNXUmGEkrhb3c2VriA==", + "requires": { + "escape-goat": "^2.0.0" + } + }, "qs": { "version": "6.7.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", @@ -1914,6 +2429,29 @@ "unpipe": "1.0.0" } }, + "rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "dependencies": { + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" + }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" + } + } + }, "read-pkg": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", @@ -1949,6 +2487,14 @@ "util-deprecate": "~1.0.1" } }, + "readdirp": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.4.0.tgz", + "integrity": "sha512-0xe001vZBnJEK+uKcj8qOhyAKPzIT+gStxWr3LCB0DwcXR5NZJ3IaC+yGnHCYzB/S7ov3m3EEbZI2zeNvX+hGQ==", + "requires": { + "picomatch": "^2.2.1" + } + }, "regexp-clone": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/regexp-clone/-/regexp-clone-1.0.0.tgz", @@ -1960,6 +2506,22 @@ "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==", "dev": true }, + "registry-auth-token": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.1.1.tgz", + "integrity": "sha512-9bKS7nTl9+/A1s7tnPeGrUpRcVY+LUh7bfFgzpndALdPfXQBfQV77rQVtqgUV3ti4vc/Ik81Ex8UJDWDQ12zQA==", + "requires": { + "rc": "^1.2.8" + } + }, + "registry-url": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-5.1.0.tgz", + "integrity": "sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw==", + "requires": { + "rc": "^1.2.8" + } + }, "require_optional": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/require_optional/-/require_optional-1.0.1.tgz", @@ -1996,6 +2558,14 @@ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true }, + "responselike": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", + "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", + "requires": { + "lowercase-keys": "^1.0.0" + } + }, "restore-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", @@ -2055,8 +2625,15 @@ "semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + }, + "semver-diff": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-3.1.1.tgz", + "integrity": "sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg==", + "requires": { + "semver": "^6.3.0" + } }, "send": { "version": "0.17.1", @@ -2139,8 +2716,7 @@ "signal-exit": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", - "dev": true + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" }, "slice-ansi": { "version": "2.1.0", @@ -2222,7 +2798,6 @@ "version": "4.2.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", - "dev": true, "requires": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -2233,7 +2808,6 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, "requires": { "ansi-regex": "^5.0.0" } @@ -2272,7 +2846,6 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, "requires": { "ansi-regex": "^4.1.0" }, @@ -2280,8 +2853,7 @@ "ansi-regex": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" } } }, @@ -2342,6 +2914,11 @@ } } }, + "term-size": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/term-size/-/term-size-2.2.0.tgz", + "integrity": "sha512-a6sumDlzyHVJWb8+YofY4TW112G6p2FCPEAFk+59gIYHv3XHRhm9ltVQ9kli4hNWeQBwSpe8cRN25x0ROunMOw==" + }, "text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -2363,11 +2940,32 @@ "os-tmpdir": "~1.0.2" } }, + "to-readable-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz", + "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==" + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "requires": { + "is-number": "^7.0.0" + } + }, "toidentifier": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" }, + "touch": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz", + "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==", + "requires": { + "nopt": "~1.0.10" + } + }, "tslib": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.11.1.tgz", @@ -2391,8 +2989,7 @@ "type-fest": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", - "dev": true + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==" }, "type-is": { "version": "1.6.18", @@ -2403,16 +3000,121 @@ "mime-types": "~2.1.24" } }, + "typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "requires": { + "is-typedarray": "^1.0.0" + } + }, "uid2": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/uid2/-/uid2-0.0.3.tgz", "integrity": "sha1-SDEm4Rd03y9xuLY53NeZw3YWK4I=" }, + "undefsafe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.3.tgz", + "integrity": "sha512-nrXZwwXrD/T/JXeygJqdCO6NZZ1L66HrxM/Z7mIq2oPanoN0F1nLx3lwJMu6AwJY69hdixaFQOuoYsMjE5/C2A==", + "requires": { + "debug": "^2.2.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, + "unique-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", + "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", + "requires": { + "crypto-random-string": "^2.0.0" + } + }, "unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" }, + "update-notifier": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-4.1.0.tgz", + "integrity": "sha512-w3doE1qtI0/ZmgeoDoARmI5fjDoT93IfKgEGqm26dGUOh8oNpaSTsGNdYRN/SjOuo10jcJGwkEL3mroKzktkew==", + "requires": { + "boxen": "^4.2.0", + "chalk": "^3.0.0", + "configstore": "^5.0.1", + "has-yarn": "^2.1.0", + "import-lazy": "^2.1.0", + "is-ci": "^2.0.0", + "is-installed-globally": "^0.3.1", + "is-npm": "^4.0.0", + "is-yarn-global": "^0.3.0", + "latest-version": "^5.0.0", + "pupa": "^2.0.1", + "semver-diff": "^3.1.1", + "xdg-basedir": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, "uri-js": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", @@ -2422,6 +3124,14 @@ "punycode": "^2.1.0" } }, + "url-parse-lax": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", + "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", + "requires": { + "prepend-http": "^2.0.0" + } + }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -2462,6 +3172,14 @@ "isexe": "^2.0.0" } }, + "widest-line": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", + "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", + "requires": { + "string-width": "^4.0.0" + } + }, "word-wrap": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", @@ -2471,8 +3189,7 @@ "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, "write": { "version": "1.0.3", @@ -2482,6 +3199,22 @@ "requires": { "mkdirp": "^0.5.1" } + }, + "write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "requires": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "xdg-basedir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", + "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==" } } } diff --git a/webapp/package.json b/webapp/package.json index 68a8f9c..b3d812c 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -5,7 +5,8 @@ "private": true, "main": "app.js", "scripts": { - "start": "node app.js" + "start": "node app.js", + "test": "nodemon app.js" }, "author": "R. Oung", "license": "MIT", @@ -24,6 +25,7 @@ "eslint": "^6.8.0", "eslint-config-airbnb-base": "^14.1.0", "eslint-plugin-import": "^2.20.1", + "nodemon": "^2.0.3", "prettier": "^1.19.1" } } diff --git a/webapp/public/js/main.js b/webapp/public/js/main.js index a4bbfa8..1d396ec 100644 --- a/webapp/public/js/main.js +++ b/webapp/public/js/main.js @@ -8,7 +8,6 @@ let selectedRobot; let client; -// DOCUMENT EVENT HANDLERS function updateRobotCollection() { const robotCollection = document.querySelector('#robot-collection'); robotCollection.className = 'collection'; @@ -28,30 +27,6 @@ function updateRobotCollection() { }); } -// mobile-only -function updateWaypointCollection() { - const waypointCollection = document.querySelector('#waypoint-collection'); - waypointCollection.className = 'collection'; - - // clear list - waypointCollection.textContent = ''; - - if (selectedRobot === undefined) { - console.warn('Robot not selected'); - } else { - // populate list - selectedRobot.waypointList.forEach((waypoint) => { - const a = document.createElement('a'); - a.id = waypoint; - a.className = 'collection-item black white-text waves-effect center-align'; - const text = document.createTextNode(waypoint); - a.appendChild(text); - waypointCollection.insertBefore(a, waypointCollection.firstChild); - }); - } -} - -// desktop-only function updateWaypointModal() { const waypointNav = document.querySelector('#waypoint-modal'); @@ -319,7 +294,6 @@ function onMessageArrived(message) { case 'info': { updateRobotList(robotID, message.payloadString); updateRobotCollection(); - // updateWaypointCollection(); // mobile-only break; } case 'utils': { @@ -344,12 +318,8 @@ function onMessageArrived(message) { // called when the client loses its connection function onConnectionLost(responseObject) { if (responseObject.errorCode !== 0) { - M.toast({ - html: 'Connection Lost', - displayLength: 2000, - classes: 'rounded', - }); console.log(`onConnectionLost: ${responseObject.errorMessage}`); + // TODO: add toast message: https://getbootstrap.com/docs/4.2/components/toasts/ } } @@ -365,7 +335,6 @@ function connectMQTT(host, port) { const id = `user-${d.getTime()}`; client = new Paho.Client(host, port, id); - // sniff and display messages on MQTT bus client.onMessageArrived = onMessageArrived; client.onConnectionLost = onConnectionLost; @@ -374,20 +343,12 @@ function connectMQTT(host, port) { reconnect: false, onSuccess: () => { console.log('Successfully connected to MQTT broker'); - M.toast({ - html: 'Successfully Connected', - displayLength: 2000, - classes: 'rounded', - }); + // TODO: add toast message: https://getbootstrap.com/docs/4.2/components/toasts/ client.subscribe('temi/+/status/#'); }, onFailure: (message) => { console.error(`Fail: ${message.errorMessage}`); - M.toast({ - html: 'Failed to Connect', - displayLength: 3000, - classes: 'rounded', - }); + // TODO: add toast message: https://getbootstrap.com/docs/4.2/components/toasts/ }, }; @@ -395,11 +356,9 @@ function connectMQTT(host, port) { client.connect(options); } -// document.body.style.backgroundColor = 'black'; - // @TODO Make this configurable -// window.onload = connectMQTT('localhost', 9001); -window.onload = connectMQTT('192.168.0.177', 9001); +window.onload = connectMQTT('localhost', 9001); +// window.onload = connectMQTT('192.168.0.177', 9001); window.onload = showRobotMenu(); document.addEventListener('DOMContentLoaded', showWaypointNav); diff --git a/webapp/routes/profile-routes.js b/webapp/routes/profile-routes.js deleted file mode 100644 index 3c9d875..0000000 --- a/webapp/routes/profile-routes.js +++ /dev/null @@ -1,16 +0,0 @@ -const router = require('express').Router(); - -// middleware to check if user is already logged in -const authCheck = (req, res, next) => { - if(!req.user){ - res.redirect('/auth/login'); - } else { - next(); - } -}; - -router.get('/', authCheck, (req, res) => { - res.render('profile', { user: req.user }); -}); - -module.exports = router; \ No newline at end of file diff --git a/webapp/views/console.ejs b/webapp/views/console.ejs index 6df9f9c..b03d160 100644 --- a/webapp/views/console.ejs +++ b/webapp/views/console.ejs @@ -1,37 +1,89 @@ <%- include('header') -%> - +
+
+
+ +
+
+
+
+

Serial Number

+

State

+
+
+

00119462420

+

Online

+
+
+
+ +
+ +
+
+
+
+ +
+
+
+
+ Raymond Oung 9:18 am +

Hello, how are you doing?

+
+
+ temi 9:18 am +

I'm doing well.

+
+
+ Raymond Oung 9:19 am +

Can I help you today?

+
+
+ temi 9:19 am +

Yes, where is the meeting room?

+
+
+ Raymond Oung 9:20 am +

Over by the bathroom.

+
+
+
+
+ +
+
+ +
+ +
+
+
+
+
+
+ + -
+
-
+
Serial Number @@ -48,27 +125,33 @@
+ -
- -
-
Name
- State -
-
--> - - - - -
diff --git a/webapp/views/header.ejs b/webapp/views/header.ejs index 3c13b40..9444dac 100644 --- a/webapp/views/header.ejs +++ b/webapp/views/header.ejs @@ -26,4 +26,7 @@ + + + From 0692218ff08eb433a90c925564ac62f5764f17f7 Mon Sep 17 00:00:00 2001 From: Raymond Oung Date: Mon, 4 May 2020 17:56:50 +0900 Subject: [PATCH 10/30] Adds device page --- webapp/app.js | 6 +- webapp/models/user-model.js | 6 +- webapp/public/js/devices.js | 95 ++++++++++++++++ webapp/public/js/robot-list.js | 97 +++++------------ webapp/routes/api-routes.js | 62 +++++------ webapp/routes/console-routes.js | 36 ------- webapp/routes/devices-routes.js | 101 +++++++++++++++++ webapp/views/console.ejs | 186 ++++++++++++-------------------- webapp/views/devices.ejs | 52 +++++++++ webapp/views/header.ejs | 2 +- webapp/views/navbar.ejs | 26 +++++ 11 files changed, 408 insertions(+), 261 deletions(-) create mode 100644 webapp/public/js/devices.js create mode 100644 webapp/routes/devices-routes.js create mode 100644 webapp/views/devices.ejs create mode 100644 webapp/views/navbar.ejs diff --git a/webapp/app.js b/webapp/app.js index 40b51cd..b19f1bf 100644 --- a/webapp/app.js +++ b/webapp/app.js @@ -18,6 +18,7 @@ const keys = require('./config/keys'); const apiRoutes = require('./routes/api-routes'); const authRoutes = require('./routes/auth-routes'); const consoleRoutes = require('./routes/console-routes'); +const devicesRoutes = require('./routes/devices-routes'); const mqttClient = require('./modules/mqtt-client'); @@ -63,6 +64,7 @@ mongoose app.use('/api', apiRoutes); app.use('/auth', authRoutes); app.use('/console', consoleRoutes); +app.use('/devices', devicesRoutes); // create home route app.get('/', (req, res) => { @@ -70,5 +72,5 @@ app.get('/', (req, res) => { }); // start server -// http.createServer(app).listen(port, () => console.log(`Server is listening on port ${port}`)); -https.createServer(ssl_options, app).listen(port, () => console.log(`Server is listening on port ${port}`)); +http.createServer(app).listen(port, () => console.log(`Server is listening on port ${port}`)); +// https.createServer(ssl_options, app).listen(port, () => console.log(`Server is listening on port ${port}`)); diff --git a/webapp/models/user-model.js b/webapp/models/user-model.js index 55143c6..87a7b3f 100644 --- a/webapp/models/user-model.js +++ b/webapp/models/user-model.js @@ -1,7 +1,7 @@ const mongoose = require('mongoose'); const Schema = mongoose.Schema; -const RobotSchema = new Schema({ +const DeviceSchema = new Schema({ name: { type: String, required: true, @@ -12,7 +12,7 @@ const RobotSchema = new Schema({ }, date: { type: Date, - default: Date.now + default: Date.now() } }); @@ -45,7 +45,7 @@ const UserSchema = new Schema({ type: String, required: false }, - robots: [RobotSchema] + devices: [DeviceSchema] }); const User = mongoose.model('user', UserSchema); diff --git a/webapp/public/js/devices.js b/webapp/public/js/devices.js new file mode 100644 index 0000000..9d4da5c --- /dev/null +++ b/webapp/public/js/devices.js @@ -0,0 +1,95 @@ +// Output device to DOM +function outputDevice(device) { + const a = document.createElement('a'); + a.id = device.serialNumber; + a.className = 'list-group-item list-group-item-action flex-column align-items-start'; + a.innerHTML =`
+
${device.name}
+ +
+
+ ${device.serialNumber} +
`; + document.querySelector('#list-device').appendChild(a); + document.querySelector(`#device-${device.serialNumber}`).addEventListener('click', removeDevice); +} + +// Display robots in a list +function displayDevices(list) { + const deviceListView = document.querySelector('#list-device'); + + // clear list + deviceListView.textContent = ''; + + list.forEach(device => { + console.log(`${device.name} ${device.serialNumber} ${device.batteryPercentage} ${device.state}`); + outputDevice(device); + }); +} + +// get devices from database +function getDevices() { + fetch('/devices/get', { + method: 'GET', + }) + .then(res => res.json()) + .then(obj => displayDevices(obj)) + .catch(err => console.error(err)); +} + +// add device to database +function addDevice() { + console.log(`Adding device...`); + const data = { + serialNumber: document.querySelector('#input-serial-number').value + }; + + fetch('/devices/add', { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(data), + }) + .then(res => res.json()) + .then(obj => { + if ('exists' in obj && obj['exists'] === true) { + console.log(`Device already exists`); + } else { + displayDevices(obj); + } + }) + .catch(err => console.error(err)); +} + +// remove device from database +function removeDevice(event) { + console.log(`Removing device...`); + const serialNumber = event.target.offsetParent.id; + + fetch(`/devices/delete?serialNumber=${serialNumber}`, { + method: 'DELETE', + }) + .then(res => res.json()) + .then(obj => { + if ('exists' in obj && obj['exists'] === false) { + console.log(`Device does not exist`); + } else { + displayDevices(obj); + } + }) + .catch(err => console.error(err)); +} + +// initialize device list +function init() { + getDevices(); +} + +// window event listeners +window.onload = init(); + +// document event listeners +document.querySelector('#btn-add-device').addEventListener('click', addDevice); diff --git a/webapp/public/js/robot-list.js b/webapp/public/js/robot-list.js index f630ec7..4b04a23 100644 --- a/webapp/public/js/robot-list.js +++ b/webapp/public/js/robot-list.js @@ -1,62 +1,30 @@ -// select robot -function selectRobot(e) { - console.log(e.target); - console.log(e.target.offsetParent); - console.log(e.target.offsetParent.id); +// Output robot to DOM +function outputRobot(robot) { + const a = document.createElement('a'); + a.id = robot.serialNumber; + a.className = 'list-group-item list-group-item-action flex-column align-items-start'; + a.innerHTML =`
+
${robot.name}
+ +
+
+ ${robot.serialNumber} +
`; + document.querySelector('#list-robot').appendChild(a); } -// display robots in a list +// Display robots in a list function displayRobots(list) { - const robotListGroup = document.querySelector('#list-robot'); + const robotListView = document.querySelector('#list-robot'); // clear list - robotListGroup.textContent = ''; + robotListView.textContent = ''; list.forEach(robot => { console.log(`${robot.name} ${robot.serialNumber} ${robot.batteryPercentage} ${robot.state}`); - - const a = document.createElement('a'); - a.id = robot.serialNumber; - if (robot.state === 'Offline') { - a.className = 'list-group-item list-group-item-action flex-column align-items-start'; - // a.className = 'list-group-item list-group-item-action flex-column align-items-start disabled'; - } else { - a.className = 'list-group-item list-group-item-action flex-column align-items-start'; - } - - const div = document.createElement('div'); - div.className = 'd-flex w-100 justify-content-between'; - - const name = document.createElement('h5'); - name.appendChild(document.createTextNode(robot.name)); - - const status = document.createElement('small'); - status.class = 'text-muted'; - status.appendChild(document.createTextNode(robot.state)); - - const serialNumber = document.createElement('small'); - serialNumber.class = 'text-muted'; - serialNumber.appendChild(document.createTextNode(robot.serialNumber)); - - div.appendChild(name); - div.appendChild(status); - - a.appendChild(div); - a.appendChild(serialNumber); - - a.addEventListener('click', (e) => { - console.log(`click: ${e.target.id}`); - }); - - a.addEventListener('mouseover', (e) => { - console.log(`mouseover: ${e.target.id}`); - }); - - a.addEventListener('mouseout', (e) => { - console.log(`mouseout: ${e.target.id}`); - }); - - robotListGroup.insertBefore(a, robotListGroup.firstChild); + outputRobot(robot); }); } @@ -70,7 +38,7 @@ function addRobot() { serialNumber: serialNumber }; - fetch('/console/add_robot', { + fetch('/devices/add', { method: "POST", headers: { "Content-Type": "application/json", @@ -82,9 +50,13 @@ function addRobot() { .catch(err => console.error(err)); } +function removeRobot(event) { + console.log(event.target.id); +} + // initialize robot list function init() { - fetch('/console/robot/get', { + fetch('/devices/get', { method: 'GET', }) .then(res => res.json()) @@ -92,24 +64,7 @@ function init() { .catch(err => console.error(err)); } +// Event listeners window.onload = init(); document.querySelector('#btn-add-robot').addEventListener('click', addRobot); -// document.querySelector('#list-robot').addEventListener('click', selectRobot); -// document.querySelector('#test').addEventListener('click', selectRobot); - -document.querySelector('#list-tab').addEventListener('click', function (e) { - console.log(e.target.id); - - // const a = document.getElementById(e.target.id); - // a.className += ' list-group-item-primary' - // console.log(`click`) -}) - -// document.querySelector('#list-home-list').addEventListener('mouseover', function (e) { -// console.log(`mouseover`); -// }) - -// document.querySelector('#list-home-list').addEventListener('mouseout', function (e) { -// console.log(`mouseout`); -// }) \ No newline at end of file diff --git a/webapp/routes/api-routes.js b/webapp/routes/api-routes.js index 76523cf..a554a7c 100644 --- a/webapp/routes/api-routes.js +++ b/webapp/routes/api-routes.js @@ -1,53 +1,53 @@ const router = require('express').Router(); const User = require('../models/user-model'); -// @route GET api/robots -// @desc Get all robots +// @route GET api/devices +// @desc Get all devices // @access Public -router.get('/robots', (req, res) => { +router.get('/devices', (req, res) => { User.findById(req.query.id) - .then(user => res.json(user.robots)) - .catch(err => res.status(404).json({ success: false })); + .then(user => res.json(user.devices)) + .catch(err => res.status(404).json({ 'success': false })); }); -// @route POST api/robots -// @desc Add robot +// @route POST api/devices +// @desc Add device // @access Public -router.post('/robots', (req, res) => { - // @TODO check parameters - const newRobot = { 'name': req.body.name, 'serialNumber': req.body.serialNumber }; - User.find({ _id: req.query.id, 'robots.serialNumber': req.body.serialNumber }) - .then(user => { - if (user === undefined || user.length === 0) { - console.log('Adding robot') - User.update({ _id: req.query.id }, { $push: { robots: newRobot } }) - .then(user => res.json(newRobot)); +router.post('/devices', (req, res) => { + const newDevice = { 'name': req.body.name, 'serialNumber': req.body.serialNumber }; + + User.find({ _id: req.query.id, 'devices.serialNumber': req.body.serialNumber }) + .then(dev => { + if (dev === undefined || dev.length === 0) { + console.log('Adding device') + User.updateOne({ _id: req.query.id }, { $push: { devices: newDevice } }) + .then(() => res.json({ 'success': true })) + .catch(err => res.status(404).json({ 'success': false })); } else { - console.log('Robot already exists'); + console.log('Device already exists'); res.json({ exists: true }); } }) - .catch(err => res.status(404).json({ success: false })); + .catch(err => res.status(404).json({ 'success': false })); }); -// @route DELETE api/robots -// @desc Delete robot +// @route DELETE api/devices +// @desc Delete device // @access Public -router.delete('/robots', (req, res) => { - // @TODO check parameters - User.find({ _id: req.query.id, 'robots.serialNumber': req.query.serialNumber }) - .then(user => { - if (user === undefined || user.length === 0) { - console.log('Robot does not exist'); +router.delete('/devices', (req, res) => { + User.find({ _id: req.query.id, 'devices.serialNumber': req.query.serialNumber }) + .then(dev => { + if (dev === undefined || dev.length === 0) { + console.log('Device does not exist'); res.json({ exists: false }); } else { - console.log('Robot exists'); - User.findByIdAndUpdate(req.query.id, { $pull: { 'robots': { serialNumber: req.query.serialNumber } } }) - .then(user => res.json({ success: true })) - .catch(err => res.status(404).json(user.robots)); + console.log('Device exists'); + User.findByIdAndUpdate(req.query.id, { $pull: { 'devices': { serialNumber: req.query.serialNumber } } }) + .then(() => res.json({ 'success': true })) + .catch(err => res.status(404).json({ 'success': false })); } }) - .catch(err => res.status(404).json({ success: false })); + .catch(err => res.status(404).json({ 'success': false })); }); module.exports = router; diff --git a/webapp/routes/console-routes.js b/webapp/routes/console-routes.js index 554ccbe..b1ef9e8 100644 --- a/webapp/routes/console-routes.js +++ b/webapp/routes/console-routes.js @@ -16,41 +16,5 @@ router.get('/', authCheck, (req, res) => { res.render('console', { user: req.user }); }); -//------------------------------------------------------- - -// @TODO Cannot use req.user.robots because this will change... -router.get('/robot/get', authCheck, (req, res) => { - // collect serial numbers - let serialNumberList = []; - req.user.robots.forEach(robot => { - serialNumberList.push(robot.serialNumber); - }); - console.log(serialNumberList); - - // collect robots - let robotListUser = []; - serialNumberList.forEach(serialNumber => { - const robot = robotListAll.find(robot => robot.serialNumber === serialNumber); - - if (robot === undefined) { - robotListUser.push(new Robot(serialNumber, { 'state': 'Offline' })); - } else { - robotListUser.push(robot); - } - }); - // console.log(robotListUser); - - res.json(robotListUser); -}); - -router.post('/robot/add', authCheck, (req, res) => { - console.log(req.body.serialNumber) - res.json(req.user.robots); -}); - -router.post('/robot/remove', authCheck, (req, res) => { - console.log(req.body.serialNumber) - res.json(req.user.robots); -}); module.exports = router; \ No newline at end of file diff --git a/webapp/routes/devices-routes.js b/webapp/routes/devices-routes.js new file mode 100644 index 0000000..16bf3c4 --- /dev/null +++ b/webapp/routes/devices-routes.js @@ -0,0 +1,101 @@ +const router = require('express').Router(); + +const User = require('../models/user-model'); + +const Robot = require('../modules/robot'); +const robotListAll = require('../modules/mqtt-message-parser'); + + +// constants +const SERIAL_NUMBER_LENGTH = 11; + + +// middleware to check if user is already logged in +const authCheck = (req, res, next) => { + if(!req.user){ + res.redirect('/auth/login'); + } else { + next(); + } +}; + +router.get('/', authCheck, (req, res) => { + res.render('devices', { user: req.user }); +}); + +//------------------------------------------------------- + +// @route GET devices/get +// @desc Get all devices +// @access OAuth +router.get('/get', authCheck, (req, res) => { + // get devices from our database + User.findById(req.user.id) + .then(user => res.json(user.devices)) + .catch(err => res.status(404).json({ 'success': false })); +}); + +// @route POST devices/add +// @desc Add device +// @access OAuth +router.post('/add', authCheck, (req, res) => { + // check parameters + if (req.body.serialNumber.length !== SERIAL_NUMBER_LENGTH) { + res.json({ 'success': false}); + } + + // construct new device + const newDevice = { 'name': 'untitled', 'serialNumber': req.body.serialNumber }; + + // find and add new device + User.find({ _id: req.user.id, 'devices.serialNumber': req.body.serialNumber }) + .then(dev => { + if (dev === undefined || dev.length === 0) { + console.log(`Adding device: ${req.body.serialNumber}`); + User.updateOne({ _id: req.user.id }, { $push: { devices: newDevice } }) + // .then(() => res.json({ 'success': true, 'exists': false })) + .then(() => { + User.findById(req.user.id) + .then(user => res.json(user.devices)) + .catch(err => res.status(404).json({ 'success': false })); + }) + .catch(err => res.status(404).json({ 'success': false })); + } else { + console.log(`Device already exists...`); + res.json({ 'success': true, 'exists': true }); + } + }) + .catch(err => res.status(404).json({ 'success': false })); +}); + +// @route DELETE devices/delete +// @desc Delete device +// @access OAuth +router.delete('/delete', authCheck, (req, res) => { + // check parameters + if (req.query.serialNumber.length !== SERIAL_NUMBER_LENGTH) { + res.json({ 'success': false}); + } + + // find and remove device + User.find({ _id: req.user.id, 'devices.serialNumber': req.query.serialNumber }) + .then(dev => { + if (dev === undefined || dev.length === 0) { + console.log(`Device does not exist...`); + res.json({ 'success': true, 'exists': false }); + } else { + console.log(`Deleting device: ${req.query.serialNumber}`); + User.findByIdAndUpdate(req.user.id, { $pull: { 'devices': { serialNumber: req.query.serialNumber } } }) + // .then(() => res.json({ 'success': true, 'exists': true })) + .then(() => { + User.findById(req.user.id) + .then(user => res.json(user.devices)) + .catch(err => res.status(404).json({ 'success': false })); + }) + .catch(err => res.status(404).json(user.devices)); + } + }) + .catch(err => res.status(404).json({ 'success': false })); +}); + +module.exports = router; \ No newline at end of file diff --git a/webapp/views/console.ejs b/webapp/views/console.ejs index b33016a..0e84a1b 100644 --- a/webapp/views/console.ejs +++ b/webapp/views/console.ejs @@ -1,31 +1,7 @@ <%- include('header') -%> - + <%- include('navbar') -%>
@@ -39,16 +15,24 @@
-
+

Serial Number

-

State

+

Status

+

Battery

-
+

00119462420

Online

+

95%

-
+
+
+ +
+
+ +
- +
-
+ +
+
+ +
+
+ + +
Raymond Oung 9:18 am @@ -87,89 +79,22 @@

Over by the bathroom.

-
-
- -
-
- -
- + +
+
+ +
+ +
+
-
-
-
-
-
- - -
-
-
-
-
- Serial Number -
- -
-
-
-
-
- - -
-
-
- - - - - - +
@@ -177,22 +102,49 @@ © 2020 hapi-robo st, Inc. — All Rights Reserved.
- - - - - - + + + + + + + - + diff --git a/webapp/views/devices.ejs b/webapp/views/devices.ejs new file mode 100644 index 0000000..c0e68a9 --- /dev/null +++ b/webapp/views/devices.ejs @@ -0,0 +1,52 @@ +<%- include('header') -%> + + + <%- include('navbar') -%> + + +
+ + +
+
+
+
+ Serial Number +
+ +
+ +
+
+
+
+ + +
+
+
+
+
+ +
+
+ + +
+ © 2020 hapi-robo st, Inc. — All Rights Reserved. +
+ + + + + + + + + + + + + + + diff --git a/webapp/views/header.ejs b/webapp/views/header.ejs index 9444dac..a2482c3 100644 --- a/webapp/views/header.ejs +++ b/webapp/views/header.ejs @@ -25,7 +25,7 @@ - + diff --git a/webapp/views/navbar.ejs b/webapp/views/navbar.ejs new file mode 100644 index 0000000..9e5a7ed --- /dev/null +++ b/webapp/views/navbar.ejs @@ -0,0 +1,26 @@ + From ddc41271924ea8dad6e86d23cc84a2ce854e11a3 Mon Sep 17 00:00:00 2001 From: Raymond Oung Date: Mon, 4 May 2020 22:01:11 +0900 Subject: [PATCH 11/30] Updates device list from database --- webapp/app.js | 4 +- webapp/public/js/console.js | 77 +++++++++++++++++++++++++++++++++ webapp/public/js/devices.js | 21 ++++++--- webapp/public/js/robot-list.js | 70 ------------------------------ webapp/routes/devices-routes.js | 40 ++++++++++++++++- webapp/views/console.ejs | 17 +++----- webapp/views/devices.ejs | 24 +++++----- webapp/views/navbar.ejs | 1 + 8 files changed, 155 insertions(+), 99 deletions(-) create mode 100644 webapp/public/js/console.js delete mode 100644 webapp/public/js/robot-list.js diff --git a/webapp/app.js b/webapp/app.js index b19f1bf..9c8fdc0 100644 --- a/webapp/app.js +++ b/webapp/app.js @@ -72,5 +72,5 @@ app.get('/', (req, res) => { }); // start server -http.createServer(app).listen(port, () => console.log(`Server is listening on port ${port}`)); -// https.createServer(ssl_options, app).listen(port, () => console.log(`Server is listening on port ${port}`)); +// http.createServer(app).listen(port, () => console.log(`Server is listening on port ${port}`)); +https.createServer(ssl_options, app).listen(port, () => console.log(`Server is listening on port ${port}`)); diff --git a/webapp/public/js/console.js b/webapp/public/js/console.js new file mode 100644 index 0000000..b3da093 --- /dev/null +++ b/webapp/public/js/console.js @@ -0,0 +1,77 @@ +// global variables +let selectedDevices = []; + +// output device details to DOM +function outputDeviceDetails(device) { + +} + +// output device to DOM +function outputDevice(device) { + const a = document.createElement('a'); + a.id = device.serialNumber; + a.className = 'list-group-item'; + a.innerHTML =`${device.name}`; + + a.addEventListener('click', event => { + if (a.className.search("active") > -1) { + a.className = 'list-group-item'; + + // remove device from selection + const index = selectedDevices.indexOf(device.serialNumber); + if (index > -1) { + selectedDevices.splice(index, 1); + } + } else { + a.className = 'list-group-item text-light active'; + + // add device to selection + selectedDevices.push(device.serialNumber); + } + console.log(selectedDevices); + }); + + a.addEventListener('mouseover', event => { + if (a.className.search("active") === -1) { + a.className = 'list-group-item bg-light'; + } + }); + + a.addEventListener('mouseout', event => { + if (a.className.search("active") === -1) { + a.className = 'list-group-item bg-white'; + } + }); + + document.querySelector('#list-device').appendChild(a); +} + +// display device in a list +function displayDevices(list) { + const deviceListView = document.querySelector('#list-device'); + + // clear list + deviceListView.textContent = ''; + + list.forEach(device => { + outputDevice(device); + }); +} + +// get devices from database +function getDevices() { + fetch('/devices/get', { + method: 'GET', + }) + .then(res => res.json()) + .then(obj => displayDevices(obj)) + .catch(err => console.error(err)); +} + +// initialize device list +function init() { + getDevices(); +} + +// window event listeners +window.onload = init(); diff --git a/webapp/public/js/devices.js b/webapp/public/js/devices.js index 9d4da5c..7515fe4 100644 --- a/webapp/public/js/devices.js +++ b/webapp/public/js/devices.js @@ -1,4 +1,4 @@ -// Output device to DOM +// output device to DOM function outputDevice(device) { const a = document.createElement('a'); a.id = device.serialNumber; @@ -16,7 +16,7 @@ function outputDevice(device) { document.querySelector(`#device-${device.serialNumber}`).addEventListener('click', removeDevice); } -// Display robots in a list +// display device in a list function displayDevices(list) { const deviceListView = document.querySelector('#list-device'); @@ -24,7 +24,6 @@ function displayDevices(list) { deviceListView.textContent = ''; list.forEach(device => { - console.log(`${device.name} ${device.serialNumber} ${device.batteryPercentage} ${device.state}`); outputDevice(device); }); } @@ -40,10 +39,14 @@ function getDevices() { } // add device to database -function addDevice() { +function addDevice(event) { + event.preventDefault(); + console.log(`Adding device...`); + const data = { - serialNumber: document.querySelector('#input-serial-number').value + name: event.target.elements.deviceName.value, + serialNumber: event.target.elements.serialNumber.value }; fetch('/devices/add', { @@ -59,6 +62,8 @@ function addDevice() { console.log(`Device already exists`); } else { displayDevices(obj); + event.target.elements.deviceName.value = ''; + event.target.elements.serialNumber.value = ''; } }) .catch(err => console.error(err)); @@ -86,10 +91,14 @@ function removeDevice(event) { // initialize device list function init() { getDevices(); + + // clear inputs + document.querySelector('#deviceName').value = ''; + document.querySelector('#serialNumber').value = ''; } // window event listeners window.onload = init(); // document event listeners -document.querySelector('#btn-add-device').addEventListener('click', addDevice); +document.querySelector('#form-add-device').addEventListener('submit', addDevice); diff --git a/webapp/public/js/robot-list.js b/webapp/public/js/robot-list.js deleted file mode 100644 index 4b04a23..0000000 --- a/webapp/public/js/robot-list.js +++ /dev/null @@ -1,70 +0,0 @@ -// Output robot to DOM -function outputRobot(robot) { - const a = document.createElement('a'); - a.id = robot.serialNumber; - a.className = 'list-group-item list-group-item-action flex-column align-items-start'; - a.innerHTML =`
-
${robot.name}
- -
-
- ${robot.serialNumber} -
`; - document.querySelector('#list-robot').appendChild(a); -} - -// Display robots in a list -function displayRobots(list) { - const robotListView = document.querySelector('#list-robot'); - - // clear list - robotListView.textContent = ''; - - list.forEach(robot => { - console.log(`${robot.name} ${robot.serialNumber} ${robot.batteryPercentage} ${robot.state}`); - outputRobot(robot); - }); -} - -// add serial number to database -function addRobot() { - const serialNumber = document.querySelector('#input-serial-number').value; - - console.log(serialNumber); - - const data = { - serialNumber: serialNumber - }; - - fetch('/devices/add', { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify(data), - }) - .then(res => res.json()) - .then(obj => displayRobots(obj)) - .catch(err => console.error(err)); -} - -function removeRobot(event) { - console.log(event.target.id); -} - -// initialize robot list -function init() { - fetch('/devices/get', { - method: 'GET', - }) - .then(res => res.json()) - .then(obj => displayRobots(obj)) - .catch(err => console.error(err)); -} - -// Event listeners -window.onload = init(); - -document.querySelector('#btn-add-robot').addEventListener('click', addRobot); diff --git a/webapp/routes/devices-routes.js b/webapp/routes/devices-routes.js index 16bf3c4..8ff755e 100644 --- a/webapp/routes/devices-routes.js +++ b/webapp/routes/devices-routes.js @@ -19,11 +19,13 @@ const authCheck = (req, res, next) => { } }; +// render device page router.get('/', authCheck, (req, res) => { res.render('devices', { user: req.user }); }); //------------------------------------------------------- +// Device Routes // @route GET devices/get // @desc Get all devices @@ -45,7 +47,7 @@ router.post('/add', authCheck, (req, res) => { } // construct new device - const newDevice = { 'name': 'untitled', 'serialNumber': req.body.serialNumber }; + const newDevice = { 'name': req.body.name, 'serialNumber': req.body.serialNumber }; // find and add new device User.find({ _id: req.user.id, 'devices.serialNumber': req.body.serialNumber }) @@ -98,4 +100,40 @@ router.delete('/delete', authCheck, (req, res) => { .catch(err => res.status(404).json({ 'success': false })); }); +//------------------------------------------------------- +// Device Location Routes + +// @route GET devices/get +// @desc Get all locations +// @access OAuth +router.get('/location/get', authCheck, (req, res) => { + console.log('Get locations'); + console.log(req.query.serialNumber); + + // get device from memory + res.json({ 'success': false }) +}); + +// @route POST devices/add +// @desc Add device +// @access OAuth +router.post('/location/add', authCheck, (req, res) => { + console.log('Add location'); + console.log(req.query.serialNumber); + + // get device from memory + res.json({ 'success': false }) +}); + +// @route DELETE devices/delete +// @desc Delete device +// @access OAuth +router.delete('/location/delete', authCheck, (req, res) => { + console.log('Delete locations'); + console.log(req.query.serialNumber); + + // get device from memory + res.json({ 'success': false }) +}); + module.exports = router; \ No newline at end of file diff --git a/webapp/views/console.ejs b/webapp/views/console.ejs index 0e84a1b..14dfbf6 100644 --- a/webapp/views/console.ejs +++ b/webapp/views/console.ejs @@ -6,12 +6,7 @@
- +
@@ -56,7 +51,7 @@
-
+ +
@@ -112,9 +107,11 @@ - + + + + + - - - + + + diff --git a/webapp/views/devices.ejs b/webapp/views/devices.ejs index fbdcdab..0307d9e 100644 --- a/webapp/views/devices.ejs +++ b/webapp/views/devices.ejs @@ -3,23 +3,23 @@ <%- include('navbar') -%> - +
-
+
-
+
-
+
diff --git a/webapp/views/error.ejs b/webapp/views/error.ejs deleted file mode 100644 index 18c77ed..0000000 --- a/webapp/views/error.ejs +++ /dev/null @@ -1,15 +0,0 @@ -<%- include('header') -%> - - -
-

Error 404

-

The requested URL was not found on this server.

- - - - - - - - - diff --git a/webapp/views/home.ejs b/webapp/views/home.ejs deleted file mode 100644 index ac8c854..0000000 --- a/webapp/views/home.ejs +++ /dev/null @@ -1,38 +0,0 @@ -<%- include('header') -%> - - - - - - - - - diff --git a/webapp/views/index.ejs b/webapp/views/index.ejs deleted file mode 100644 index 792acdf..0000000 --- a/webapp/views/index.ejs +++ /dev/null @@ -1,24 +0,0 @@ -<%- include('header') -%> - - - - - - - - - - - diff --git a/webapp/views/login.ejs b/webapp/views/login.ejs index 5c7f847..b0af3a3 100644 --- a/webapp/views/login.ejs +++ b/webapp/views/login.ejs @@ -1,14 +1,7 @@ <%- include('header') -%> - - -
+ <%- include('navbar') -%>
@@ -20,9 +13,10 @@
--> +
-
+
diff --git a/webapp/views/navbar.ejs b/webapp/views/navbar.ejs index 41b9292..d152d4a 100644 --- a/webapp/views/navbar.ejs +++ b/webapp/views/navbar.ejs @@ -5,9 +5,10 @@ Connect
+ <% if (user) { -%> + <% } -%> diff --git a/webapp/views/profile.ejs b/webapp/views/profile.ejs deleted file mode 100644 index 57652c8..0000000 --- a/webapp/views/profile.ejs +++ /dev/null @@ -1,83 +0,0 @@ -<%- include('header') -%> - - - - -
- -
-
- - -
-
-
- - - - - -
-
-
- - -
-
- - -
-
- - -
-

© 2020 hapi-robo st, Inc. — All Rights Reserved.

-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - From 2abe6fd668bfb47fc00d22cf6768867bfd94b306 Mon Sep 17 00:00:00 2001 From: Raymond Oung Date: Sun, 10 May 2020 01:12:50 +0900 Subject: [PATCH 14/30] Resizes video height automatically --- webapp/public/js/_main.js | 348 ++++++++++++++++++++++++++++++++++++ webapp/public/js/console.js | 123 +++++++------ webapp/public/js/devices.js | 8 +- webapp/public/js/video.js | 14 +- webapp/views/_chat.ejs | 35 ++++ webapp/views/_error.ejs | 15 ++ webapp/views/_home.ejs | 38 ++++ webapp/views/_index.ejs | 24 +++ webapp/views/_profile.ejs | 83 +++++++++ 9 files changed, 619 insertions(+), 69 deletions(-) create mode 100644 webapp/public/js/_main.js create mode 100644 webapp/views/_chat.ejs create mode 100644 webapp/views/_error.ejs create mode 100644 webapp/views/_home.ejs create mode 100644 webapp/views/_index.ejs create mode 100644 webapp/views/_profile.ejs diff --git a/webapp/public/js/_main.js b/webapp/public/js/_main.js new file mode 100644 index 0000000..a822e70 --- /dev/null +++ b/webapp/public/js/_main.js @@ -0,0 +1,348 @@ +import { Robot } from './modules/robot.js'; +// import { VideoConference } from './modules/videoConference.js'; + +// global variables +// const videoCall = new VideoConference(); +const robotList = []; +let selectedRobot; +let client; + + +function selectRobot(e) { + console.log(`Selected Robot: ${e.target.id}`); + + // check that the selection is valid + const selection = robotList.find((r) => r.id === e.target.id); + if (selection === undefined) { + // TODO: add toast message: https://getbootstrap.com/docs/4.2/components/toasts/ + } else { + // assign selected robot + selectedRobot = selection; + + // show waypoint menu + showCtrlPanel(); + + // update battery state + updateBatteryState(selection.batteryPercentage); + } +} + +function updateBatteryState(value) { + const batteryState = document.querySelector('#battery-state'); + + // @TODO "far fa-battery-bolt" + + if (value >= 87.5) { + batteryState.className = 'fas fa-battery-full'; + } else if (value >= 62.5 && value < 87.5) { + batteryState.className = 'fas fa-battery-threequarters'; + } else if (value >= 37.5 && value < 62.5) { + batteryState.className = 'fas fa-battery-half'; + } else if (value >= 12.5 && value < 37.5) { + batteryState.className = 'fas fa-battery-quarter'; + } else if (value >= 0 && value < 12.5) { + batteryState.className = 'fas fa-battery-empty'; + batteryState.style = 'color:red'; + } else { + console.warn(`Battery value: ${value}`); + } +} + +function updateRobotList(id, payload) { + const index = robotList.findIndex((e) => e.id === id); + + if (index === -1) { + console.log('Append'); + robotList.push(new Robot(id, client)); + + const data = JSON.parse(payload); + robotList[robotList.length - 1].waypointList = data.waypoint_list; + robotList[robotList.length - 1].batteryPercentage = data.battery_percentage; + } else { + console.log('Update'); + + const data = JSON.parse(payload); + robotList[index].waypointList.length = 0; // clear array + robotList[index].waypointList = data.waypoint_list; + robotList[index].batteryPercentage = data.battery_percentage; + } + // console.log(`Number of Robots: ${robotList.length}`); +} + +/* + * General message callback + */ +function onMessageArrived(message) { + // console.log('[RECIEVE]'); + // console.log(`Topic: ${message.destinationName}`); + // console.log(`Payload: ${message.payloadString}`); + + // parse message + const topicTree = message.destinationName.split('/'); + const robotID = topicTree[1]; // [robot-id] + const type = topicTree[2]; // [status, command] + const category = topicTree[3]; + + // console.log(`Robot-ID: ${robotID}`); + // console.log(`Type: ${type}`); + // console.log(`Category: ${category}`); + + if (robotID === undefined) { + console.warn('Message from undefined robot received'); + } else { + switch (type) { + case 'status': + // parse payload + switch (category) { + case 'info': { + updateRobotList(robotID, message.payloadString); + updateRobotCollection(); + break; + } + case 'utils': { + break; + } + default: { + console.warn(`Undefined category: ${category}`); + break; + } + } + break; + + case 'command': + break; + + default: + break; + } + } +} + + +// function selectWaypoint(e) { +// selectedRobot.cmdGoto(e.target.id); +// console.log(`Selected Destination: ${selectedRobot.destination}`); +// } + +// function updateWaypointModal() { +// const waypointNav = document.querySelector('#waypoint-modal'); + +// // clear list +// waypointNav.textContent = ''; + +// if (selectedRobot === undefined) { +// console.warn('Robot not selected'); +// } else { +// console.log(selectedRobot.id); +// selectedRobot.waypointList.forEach((waypoint) => { +// console.log(waypoint); +// const a = document.createElement('a'); +// a.id = waypoint; +// a.className = 'collection-item center-align waves-effect'; +// const text = document.createTextNode(waypoint); +// a.appendChild(text); +// waypointNav.insertBefore(a, waypointNav.firstChild); +// }); +// } +// } + +// function showWaypointNav() { +// const elems = document.querySelectorAll('.modal'); +// M.Modal.init(elems, { +// onOpenStart: updateWaypointModal, +// }); +// } + +// function showRobotMenu() { +// console.log('Show Robot Menu'); + +// // show robot menu +// document.querySelector('#robot-menu').style.height = '80vh'; +// document.querySelector('#robot-menu').style.display = 'block'; + +// // hide other menus +// document.querySelector('#robot-ctrl-panel').style.display = 'none'; +// document.querySelector('#text-input').style.display = 'none'; +// } + +// function showWaypointMenu() { +// console.log('Show Waypoint Menu'); + +// // show waypoint menu +// document.querySelector('#waypoint-menu').style.display = 'block'; +// updateWaypointCollection(); + +// // hide other menus +// document.querySelector('#robot-menu').style.display = 'none'; +// document.querySelector('#text-input').style.display = 'none'; +// } + +// function showCtrlPanel() { +// console.log('Show Control Panel'); + +// // show control panel +// document.querySelector('#robot-ctrl-panel').style.display = 'block'; +// document.querySelector('#video-btn').className = 'btn-flat white-text'; +// document.querySelector('#text-input').style.display = 'block'; + +// // hide other menus +// document.querySelector('#robot-menu').style.display = 'none'; +// } + +// // https://keycode.info/ +// function keyboardEvent(e) { +// if (selectedRobot === undefined) { +// const id = document.querySelector('#temi-id').value; +// const selection = robotList.find((r) => r.id === id); + +// switch (e.keyCode) { +// case 13: // Enter +// console.log('[Keycode] Enter'); +// console.log(`Robot-ID: ${id}`); + +// // check that the selection is valid +// if (selection === undefined) { +// M.toast({ +// html: 'Invalid ID', +// displayLength: 2000, +// classes: 'rounded', +// }); +// } +// break; + +// default: +// console.warn('No robot selected'); +// break; +// } +// } else { +// switch (e.keyCode) { +// case 37: // ArrowLeft +// // case 65: // a +// console.log('[Keycode] ArrowLeft / a'); +// selectedRobot.cmdTurnLeft(); +// break; + +// case 39: // ArrowRight +// // case 68: // d +// console.log('[Keycode] ArrowRight / d'); +// selectedRobot.cmdTurnRight(); +// break; + +// case 38: // ArrowUp +// // case 87: // w +// console.log('[Keycode] ArrowUp / w'); +// selectedRobot.cmdMoveFwd(); +// break; + +// case 40: // ArrowDown +// // case 83: // s +// console.log('[Keycode] ArrowDown / s'); +// selectedRobot.cmdMoveBwd(); +// break; + +// case 187: // = +// console.log('[Keycode] u'); +// selectedRobot.cmdTiltUp(); +// break; + +// case 189: // - +// console.log('[Keycode] j'); +// selectedRobot.cmdTiltDown(); +// break; + +// case 13: // Enter +// console.log('[Keycode] Enter'); +// const utterance = document.querySelector('#utterance').value; +// selectedRobot.cmdTts(utterance); +// document.querySelector('#utterance').value = ''; +// break; + +// default: +// break; +// } + +// if (e.ctrlKey) { +// switch (e.keyCode) { +// case 70: // CTRL + f +// console.log('[Keycode] CTRL + f'); +// selectedRobot.cmdFollow(); +// break; + +// default: +// break; +// } +// } +// } +// } + + +// function startVideoCall() { +// document.querySelector('#video-btn').className = 'btn-flat disabled'; + +// // start new video conference +// console.log('Starting Video Conference...'); +// selectedRobot.cmdCall(); // start the call on the robot's side +// videoCall.open(selectedRobot.id); +// // TODO: This needs to be cleaned up +// videoCall.handle.on('readyToClose', () => { +// console.log('Closing Video Conference...'); +// videoCall.close(); +// // TODO: how to know if other users aren't using the robot? +// selectedRobot.cmdHangup(); +// showRobotMenu(); +// }); +// } + +// called when the client loses its connection +function onConnectionLost(responseObject) { + if (responseObject.errorCode !== 0) { + console.log(`onConnectionLost: ${responseObject.errorMessage}`); + // TODO: add toast message: https://getbootstrap.com/docs/4.2/components/toasts/ + } +} + +/* + * Connect to MQTT broker + * ref: https://www.eclipse.org/paho/files/jsdoc/Paho.MQTT.Client.html + */ +function connectMqtt(host, port) { + console.log(`Connecting to ${host}:${port}`); + + // unique identifier + const d = new Date(); + const id = `user-${d.getTime()}`; + client = new Paho.Client(host, port, id); + + client.onMessageArrived = onMessageArrived; + client.onConnectionLost = onConnectionLost; + + const options = { + timeout: 3, + reconnect: false, + onSuccess: () => { + console.log('Successfully connected to MQTT broker'); + // TODO: add toast message: https://getbootstrap.com/docs/4.2/components/toasts/ + client.subscribe('temi/+/status/#'); + }, + onFailure: (message) => { + console.error(`Fail: ${message.errorMessage}`); + // TODO: add toast message: https://getbootstrap.com/docs/4.2/components/toasts/ + }, + }; + + // attempt to connect + client.connect(options); +} + +window.onload = connectMqtt('localhost', 9001); + +// window.onload = connectMqtt('192.168.0.177', 9001); +// window.onload = showRobotMenu(); + +// document.addEventListener('DOMContentLoaded', showWaypointNav); +// document.addEventListener('keydown', keyboardEvent); + +// document.querySelector('#robot-collection').addEventListener('click', selectRobot); +// document.querySelector('#home-btn').addEventListener('click', showRobotMenu); +// document.querySelector('#video-btn').addEventListener('click', startVideoCall); +// document.querySelector('#waypoint-modal').addEventListener('click', selectWaypoint); diff --git a/webapp/public/js/console.js b/webapp/public/js/console.js index 6cf7f42..1a25050 100644 --- a/webapp/public/js/console.js +++ b/webapp/public/js/console.js @@ -1,65 +1,72 @@ -// global variables +const deviceList = document.querySelector("#list-device"); let selectedSerialNumberList = []; -// display devices in a list -function deviceListView(list) { - const listDevice = document.querySelector("#list-device"); - // reset list - listDevice.textContent = ""; +function onDeviceCick(e) { + const serialNumber = e.target.id; - // append each device element - list.forEach((device) => { - // output device to DOM - const a = document.createElement("a"); - a.id = device.serialNumber; - a.className = "list-group-item"; - a.innerHTML = `${device.name}`; - - a.addEventListener("click", (event) => { - if (a.className.search("active") > -1) { - a.className = "list-group-item"; - - // @TODO only when CTRL is pressed - // remove device from selection - const index = selectedSerialNumberList.indexOf(device.serialNumber); - if (index > -1) { - selectedSerialNumberList.splice(index, 1); - } - } else { - a.className = "list-group-item text-light active"; + // add device to selection list + if (this.className.search("active") > -1) { + this.className = "list-group-item"; - // @TODO only when CTRL is pressed - // add device to selection - selectedSerialNumberList.push(device.serialNumber); - } + // @TODO only when CTRL is pressed + // remove device from selection + const index = selectedSerialNumberList.indexOf(serialNumber); + if (index > -1) { + selectedSerialNumberList.splice(index, 1); + } + } else { + this.className = "list-group-item text-light active"; - // show device details - const deviceDetails = document.querySelector("#div-device-details"); - if (selectedSerialNumberList.length === 1) { - sessionStorage.setItem("selectedSerialNumber", event.target.id); - deviceDetails.style.display = "block"; - showDeviceInfo(a.id); - } else { - sessionStorage.setItem("selectedSerialNumber", null); - deviceDetails.style.display = "none"; - } - }); + // @TODO only when CTRL is pressed + // add device to selection + selectedSerialNumberList.push(serialNumber); + } - a.addEventListener("mouseover", (event) => { - if (a.className.search("active") === -1) { - a.className = "list-group-item"; - } - }); + // show device details + const deviceDetails = document.querySelector("#div-device-details"); + if (selectedSerialNumberList.length === 1) { + sessionStorage.setItem("selectedSerialNumber", serialNumber); + deviceDetails.style.display = "block"; + showDeviceInfo(this.id); + } else { + sessionStorage.setItem("selectedSerialNumber", null); + deviceDetails.style.display = "none"; + } +} - a.addEventListener("mouseout", (event) => { - if (a.className.search("active") === -1) { - a.className = "list-group-item"; - } - }); +function onDeviceHoverIn() { + if (this.className.search("active") === -1) { + this.className = "list-group-item"; + } +} + +function onDeviceHoverOut() { + if (this.className.search("active") === -1) { + this.className = "list-group-item"; + } +} + +function displayDeviceElement(dev) { + const a = document.createElement("a"); + a.id = dev.serialNumber; + a.className = "list-group-item"; + a.innerHTML = `${dev.name}`; + + a.addEventListener("click", onDeviceCick); + a.addEventListener("mouseover", onDeviceHoverIn); + a.addEventListener("mouseout", onDeviceHoverOut); - listDevice.appendChild(a); - }); + deviceList.appendChild(a); +} + +// display devices in a list +function displayDeviceList(list) { + // reset list + deviceList.textContent = ''; + + // append each device element + list.forEach((dev) => displayDeviceElement(dev)); } // display device information @@ -111,7 +118,7 @@ function showDeviceList() { .then((res) => res.json()) .then((obj) => { if (obj.length > 0) { - deviceListView(obj); + displayDeviceList(obj); } else { // no devices to display, show "add device button" console.log("No devices to display"); @@ -160,11 +167,3 @@ window.onload = init(); // element event listener document.querySelector("#btn-goto").addEventListener("click", cmdGoto); - -// window.onresize = () => { -// const width = document.querySelector('#video-container').offsetWidth; -// const height = document.querySelector('#video-container').offsetHeight; - -// console.log(`w: ${width} x h: ${height}`); -// console.log(`w: ${window.innerWidth} x h: ${window.innerHeight}`); -// } diff --git a/webapp/public/js/devices.js b/webapp/public/js/devices.js index e99eb46..87823e9 100644 --- a/webapp/public/js/devices.js +++ b/webapp/public/js/devices.js @@ -25,11 +25,11 @@ function displayDeviceElement(dev) { // display devices in a list function displayDeviceList(list) { - deviceList.textContent = ""; + // reset list + deviceList.textContent = ''; - list.forEach((dev) => { - displayDeviceElement(dev); - }); + // append each device element + list.forEach((dev) => displayDeviceElement(dev)); } // get devices from database diff --git a/webapp/public/js/video.js b/webapp/public/js/video.js index 97bd80c..c30de8e 100644 --- a/webapp/public/js/video.js +++ b/webapp/public/js/video.js @@ -1,3 +1,5 @@ +let videoHandle; + // show video function openVideo() { console.log("Starting Video Call..."); @@ -43,9 +45,9 @@ function openVideo() { }, }; - const videoHandle = new JitsiMeetExternalAPI("meet.jit.si", options); + videoHandle = new JitsiMeetExternalAPI("meet.jit.si", options); - // console.log(videoHandle.getIFrame()); + console.log(videoHandle.getIFrame()); } // @TODO Not yet used @@ -56,4 +58,10 @@ function closeVideo(handle) { handle.dispose(); } -document.querySelector("#btn-video").addEventListener("click", openVideo); + +window.onresize = () => { + console.log(window.innerHeight * 0.6); + videoHandle.getIFrame().style.height = `${window.innerHeight * 0.6}px`; +} + +document.querySelector("#btn-video").addEventListener("click", openVideo); \ No newline at end of file diff --git a/webapp/views/_chat.ejs b/webapp/views/_chat.ejs new file mode 100644 index 0000000..948ce6d --- /dev/null +++ b/webapp/views/_chat.ejs @@ -0,0 +1,35 @@ + +
+
+
+ Raymond Oung 9:18 am +

Hello, how are you doing?

+
+
+ temi 9:18 am +

I'm doing well.

+
+
+ Raymond Oung 9:19 am +

Can I help you today?

+
+
+ temi 9:19 am +

Yes, where is the meeting room?

+
+
+ Raymond Oung 9:20 am +

Over by the bathroom.

+
+
+ +
+
+ +
+ +
+
+
+ +
\ No newline at end of file diff --git a/webapp/views/_error.ejs b/webapp/views/_error.ejs new file mode 100644 index 0000000..18c77ed --- /dev/null +++ b/webapp/views/_error.ejs @@ -0,0 +1,15 @@ +<%- include('header') -%> + + +
+

Error 404

+

The requested URL was not found on this server.

+ + + + + + + + + diff --git a/webapp/views/_home.ejs b/webapp/views/_home.ejs new file mode 100644 index 0000000..ac8c854 --- /dev/null +++ b/webapp/views/_home.ejs @@ -0,0 +1,38 @@ +<%- include('header') -%> + + + + + + + + + diff --git a/webapp/views/_index.ejs b/webapp/views/_index.ejs new file mode 100644 index 0000000..792acdf --- /dev/null +++ b/webapp/views/_index.ejs @@ -0,0 +1,24 @@ +<%- include('header') -%> + + + + + + + + + + + diff --git a/webapp/views/_profile.ejs b/webapp/views/_profile.ejs new file mode 100644 index 0000000..57652c8 --- /dev/null +++ b/webapp/views/_profile.ejs @@ -0,0 +1,83 @@ +<%- include('header') -%> + + + + +
+ +
+
+ + +
+
+
+ + + + + +
+
+
+ + +
+
+ + +
+
+ + +
+

© 2020 hapi-robo st, Inc. — All Rights Reserved.

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + From 7b1a9ee1949d0b0e6f23bbf20a6e8e1fd42da20e Mon Sep 17 00:00:00 2001 From: Raymond Oung Date: Sun, 10 May 2020 13:15:07 +0900 Subject: [PATCH 15/30] Cleans up and modularizes code --- webapp/modules/mqtt-message-parser.js | 7 +- webapp/public/js/_console.js | 180 ++++++++++++++++++ webapp/public/js/console.js | 259 ++++++++++++-------------- webapp/public/js/devices.js | 60 +++--- webapp/public/js/modules/commands.js | 16 ++ webapp/public/js/video.js | 8 +- webapp/views/console.ejs | 26 +-- webapp/views/devices.ejs | 2 +- 8 files changed, 359 insertions(+), 199 deletions(-) create mode 100644 webapp/public/js/_console.js create mode 100644 webapp/public/js/modules/commands.js diff --git a/webapp/modules/mqtt-message-parser.js b/webapp/modules/mqtt-message-parser.js index 61bca61..61af4e4 100644 --- a/webapp/modules/mqtt-message-parser.js +++ b/webapp/modules/mqtt-message-parser.js @@ -20,16 +20,15 @@ function onStatusInfo(serialNumber, payload) { (device) => device.serialNumber === serialNumber ); const data = JSON.parse(payload); - // console.log(data); - if (device === undefined) { + if (typeof device === "undefined") { // append to list - console.log("Append"); + console.log("Append device"); deviceListAll.push(new Device(serialNumber, data)); } else { if (!device.isEqual(data)) { // update parameters - console.log("Update"); + console.log("Update device parameters"); device.update(data); } } diff --git a/webapp/public/js/_console.js b/webapp/public/js/_console.js new file mode 100644 index 0000000..3eccecf --- /dev/null +++ b/webapp/public/js/_console.js @@ -0,0 +1,180 @@ +const deviceList = document.querySelector("#list-device"); +let selectedSerialNumberList = []; + + +function onDeviceClick(e) { + const serialNumber = e.target.id; + + // add device to selection list + if (this.className.search("active") > -1) { + this.className = "list-group-item"; + + // @TODO only when CTRL is pressed + // remove device from selection + const index = selectedSerialNumberList.indexOf(serialNumber); + if (index > -1) { + selectedSerialNumberList.splice(index, 1); + } + } else { + this.className = "list-group-item text-light active"; + + // @TODO only when CTRL is pressed + // add device to selection + selectedSerialNumberList.push(serialNumber); + } + + // show device details + const deviceDetails = document.querySelector("#div-device-details"); + if (selectedSerialNumberList.length === 1) { + sessionStorage.setItem("selectedSerialNumber", serialNumber); + deviceDetails.style.display = "block"; + showDeviceInfo(this.id); + } else { + sessionStorage.setItem("selectedSerialNumber", null); + deviceDetails.style.display = "none"; + } +} + +function onDeviceHoverIn() { + if (this.className.search("active") === -1) { + this.className = "list-group-item"; + } +} + +function onDeviceHoverOut() { + if (this.className.search("active") === -1) { + this.className = "list-group-item"; + } +} + +function displayDeviceElement(dev) { + // plain list group + const a = document.createElement("a"); + a.id = dev.serialNumber; + a.className = "list-group-item"; + a.innerHTML = `${dev.name}`; + + // bootstrap list group + // const a = document.createElement('a'); + // a.id = dev.serialNumber; + // a.className = "list-group-item list-group-item-action"; + // a.setAttribute('data-toggle', 'list'); + // a.setAttribute('role', 'tab'); + // a.setAttribute('aria-controls', `${dev.name}`); + // a.innerHTML = `${dev.name}`; + + // add event listeners + a.addEventListener("click", onDeviceClick); + a.addEventListener("mouseover", onDeviceHoverIn); + a.addEventListener("mouseout", onDeviceHoverOut); + + deviceList.appendChild(a); +} + +// display devices in a list +function displayDeviceList(list) { + // reset list + deviceList.textContent = ''; + + // append each device element + list.forEach((dev) => displayDeviceElement(dev)); +} + +// display device information +function deviceInfoView(obj, serialNumber) { + console.log(serialNumber); + console.log(obj); + const device = obj.find((elem) => elem["serialNumber"] === serialNumber); + + // reset state + document.querySelector("#device-serial").innerHTML = serialNumber; + document.querySelector("#device-battery").innerHTML = ""; + + const selectGoto = document.querySelector("#select-goto"); + selectGoto.innerHTML = ""; // reset content + + if (device !== undefined) { + if (device.batteryPercentage === undefined) { + document.querySelector("#device-battery").innerHTML = `Unknown`; + } else { + document.querySelector( + "#device-battery" + ).innerHTML = `${device.batteryPercentage}%`; + } + + // construct waypoint list + const option = document.createElement("option"); + option.disabled = true; + option.selected = true; + option.style.display = "none"; + option.textContent = "Choose Location..."; + selectGoto.appendChild(option); + + device.waypointList.forEach((waypointName) => { + const option = document.createElement("option"); + option.value = waypointName; + option.textContent = waypointName; + selectGoto.appendChild(option); + }); + } else { + console.log("Device: Offline"); + } +} + +// show device list +function showDeviceList() { + fetch("/devices/get", { + method: "GET", + }) + .then((res) => res.json()) + .then((obj) => { + if (obj.length > 0) { + displayDeviceList(obj); + } else { + // no devices to display, show "add device button" + console.log("No devices to display"); + document.querySelector("#container-no-devices").style.display = "block"; + } + }) + .catch((err) => console.error(err)); +} + +// show device information +function showDeviceInfo(serialNumber) { + fetch("/devices/info", { + method: "GET", + }) + .then((res) => res.json()) + .then((obj) => deviceInfoView(obj, serialNumber)) + .catch((err) => console.error(err)); +} + +function cmdGoto(event) { + const waypoint = document.querySelector("#select-goto").value; + console.log(`Goto: ${waypoint}`); + + fetch("/command/goto", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + serialNumber: sessionStorage.getItem("selectedSerialNumber"), + waypoint: waypoint, + }), + }) + .then((res) => res.json()) + .then((obj) => console.log(obj)) + .catch((err) => console.error(err)); +} + +// initialize device list +function init() { + showDeviceList(); +} + +// window event listeners +window.onload = init(); + +// element event listener +document.querySelector("#btn-goto").addEventListener("click", cmdGoto); diff --git a/webapp/public/js/console.js b/webapp/public/js/console.js index 1a25050..8e81738 100644 --- a/webapp/public/js/console.js +++ b/webapp/public/js/console.js @@ -1,169 +1,142 @@ -const deviceList = document.querySelector("#list-device"); -let selectedSerialNumberList = []; - - -function onDeviceCick(e) { - const serialNumber = e.target.id; - - // add device to selection list - if (this.className.search("active") > -1) { - this.className = "list-group-item"; - - // @TODO only when CTRL is pressed - // remove device from selection - const index = selectedSerialNumberList.indexOf(serialNumber); - if (index > -1) { - selectedSerialNumberList.splice(index, 1); - } - } else { - this.className = "list-group-item text-light active"; +import { cmdGoto } from './modules/commands.js' - // @TODO only when CTRL is pressed - // add device to selection - selectedSerialNumberList.push(serialNumber); - } - - // show device details - const deviceDetails = document.querySelector("#div-device-details"); - if (selectedSerialNumberList.length === 1) { - sessionStorage.setItem("selectedSerialNumber", serialNumber); - deviceDetails.style.display = "block"; - showDeviceInfo(this.id); - } else { - sessionStorage.setItem("selectedSerialNumber", null); - deviceDetails.style.display = "none"; - } -} +let selectedSerialNumberList = []; -function onDeviceHoverIn() { - if (this.className.search("active") === -1) { - this.className = "list-group-item"; - } +function showSelectWaypointElement(dev) { + const inputGoto = document.querySelector("#input-goto"); + + inputGoto.innerHTML = ''; // reset content + + const div = document.createElement("div"); + div.className = "input-group"; + div.innerHTML = `
+ +
+ +
+ +
`; + inputGoto.appendChild(div); + inputGoto.querySelector("#btn-goto").addEventListener("click", cmdGoto); + + const selectGoto = inputGoto.querySelector("#select-goto"); + + // construct waypoint list + const option = document.createElement("option"); + option.disabled = true; + option.selected = true; + option.style.display = "none"; + option.textContent = "Choose Location..."; + selectGoto.appendChild(option); + + dev.waypointList.forEach((waypointName) => { + const option = document.createElement("option"); + option.value = waypointName; + option.textContent = waypointName; + selectGoto.appendChild(option); + }); } -function onDeviceHoverOut() { - if (this.className.search("active") === -1) { - this.className = "list-group-item"; +function showBatteryState(value) { + // @TODO "far fa-battery-bolt" + const i = document.createElement("i"); + + if (value >= 87.5) { + i.className = 'fas fa-battery-full'; + } else if (value >= 62.5 && value < 87.5) { + i.className = 'fas fa-battery-threequarters'; + } else if (value >= 37.5 && value < 62.5) { + i.className = 'fas fa-battery-half'; + } else if (value >= 12.5 && value < 37.5) { + i.className = 'fas fa-battery-quarter'; + } else if (value >= 0 && value < 12.5) { + i.className = 'fas fa-battery-empty'; + i.style.color = 'red'; + } else { + console.warn(`Battery Percentage: ${value}%`); } -} -function displayDeviceElement(dev) { - const a = document.createElement("a"); - a.id = dev.serialNumber; - a.className = "list-group-item"; - a.innerHTML = `${dev.name}`; - - a.addEventListener("click", onDeviceCick); - a.addEventListener("mouseover", onDeviceHoverIn); - a.addEventListener("mouseout", onDeviceHoverOut); - - deviceList.appendChild(a); -} - -// display devices in a list -function displayDeviceList(list) { - // reset list - deviceList.textContent = ''; - - // append each device element - list.forEach((dev) => displayDeviceElement(dev)); + const deviceBattery = document.querySelector('#device-battery'); + deviceBattery.appendChild(i); } -// display device information -function deviceInfoView(obj, serialNumber) { - console.log(serialNumber); - console.log(obj); - const device = obj.find((elem) => elem["serialNumber"] === serialNumber); - - // reset state - document.querySelector("#device-serial").innerHTML = serialNumber; +async function showDeviceConsole(serial) { + // get device information + const res = await fetch("/devices/info", { method: "GET" }); + const data = await res.json(); + const dev = data.find((elem) => elem["serialNumber"] === serial); + + // reset values + document.querySelector("#device-serial").innerHTML = serial; document.querySelector("#device-battery").innerHTML = ""; - const selectGoto = document.querySelector("#select-goto"); - selectGoto.innerHTML = ""; // reset content + // show device details + if (typeof dev !== "undefined") { + document.querySelector("#device-status").innerHTML = "Online"; - if (device !== undefined) { - if (device.batteryPercentage === undefined) { - document.querySelector("#device-battery").innerHTML = `Unknown`; + // show battery status + if (typeof dev.batteryPercentage === "undefined") { + document.querySelector("#device-battery").innerHTML = "Unknown"; } else { - document.querySelector( - "#device-battery" - ).innerHTML = `${device.batteryPercentage}%`; + showBatteryState(dev.batteryPercentage); } - // construct waypoint list - const option = document.createElement("option"); - option.disabled = true; - option.selected = true; - option.style.display = "none"; - option.textContent = "Choose Location..."; - selectGoto.appendChild(option); + // show goto list + if (dev.waypointList.length > 0) { + showSelectWaypointElement(dev); + } + + // show video button + document.querySelector("#btn-video").style.display = "block"; - device.waypointList.forEach((waypointName) => { - const option = document.createElement("option"); - option.value = waypointName; - option.textContent = waypointName; - selectGoto.appendChild(option); - }); } else { - console.log("Device: Offline"); + document.querySelector("#device-status").innerHTML = "Offline"; + document.querySelector("#btn-video").style.dispay = "none"; + document.querySelector("#input-goto").innerHTML = ''; } } -// show device list -function showDeviceList() { - fetch("/devices/get", { - method: "GET", - }) - .then((res) => res.json()) - .then((obj) => { - if (obj.length > 0) { - displayDeviceList(obj); - } else { - // no devices to display, show "add device button" - console.log("No devices to display"); - document.querySelector("#container-no-devices").style.display = "block"; - } - }) - .catch((err) => console.error(err)); -} - -// show device information -function showDeviceInfo(serialNumber) { - fetch("/devices/info", { - method: "GET", - }) - .then((res) => res.json()) - .then((obj) => deviceInfoView(obj, serialNumber)) - .catch((err) => console.error(err)); -} +function onDeviceClick(e) { + const serial = e.target.id; + sessionStorage.setItem("selectedSerial", serial); -function cmdGoto(event) { - const waypoint = document.querySelector("#select-goto").value; - console.log(`Goto: ${waypoint}`); - - fetch("/command/goto", { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - serialNumber: sessionStorage.getItem("selectedSerialNumber"), - waypoint: waypoint, - }), - }) - .then((res) => res.json()) - .then((obj) => console.log(obj)) - .catch((err) => console.error(err)); + // show device details + document.querySelector("#div-device-details").style.display = "block"; + showDeviceConsole(this.id); } -// initialize device list -function init() { - showDeviceList(); +async function showDeviceList() { + const res = await fetch("/devices/get", { method: "GET" }); + const data = await res.json(); + + if (data.length > 0) { + const deviceList = document.querySelector("#list-device"); + + // reset list + deviceList.textContent = ''; + deviceList.setAttribute('role', 'tablist'); + + // append each device element + data.forEach((dev) => { + const a = document.createElement('a'); + a.id = dev.serialNumber; + a.className = "list-group-item list-group-item-action"; + a.setAttribute('data-toggle', 'list'); + a.setAttribute('role', 'tab'); + a.setAttribute('aria-controls', `${dev.name}`); + a.innerHTML = `${dev.name}`; + + // add event listeners + a.addEventListener("click", onDeviceClick); + + deviceList.appendChild(a); + }); + } else { + // no devices to display; show "add device" button + document.querySelector("#container-no-devices").style.display = "block"; + } } // window event listeners -window.onload = init(); +window.onload = showDeviceList(); -// element event listener -document.querySelector("#btn-goto").addEventListener("click", cmdGoto); diff --git a/webapp/public/js/devices.js b/webapp/public/js/devices.js index 87823e9..88187fa 100644 --- a/webapp/public/js/devices.js +++ b/webapp/public/js/devices.js @@ -1,42 +1,40 @@ -const deviceList = document.querySelector("#list-device"); const deviceNameInput = document.querySelector("#deviceName"); -const serialNumberInput = document.querySelector("#serialNumber"); - -// display device element -function displayDeviceElement(dev) { - const a = document.createElement("a"); - a.id = dev.serialNumber; - a.className = - "list-group-item list-group-item-action flex-column align-items-start"; - a.innerHTML = `
-
${dev.name}
- -
-
- ${dev.serialNumber} -
`; - deviceList.appendChild(a); - document - .querySelector(`#delete-${dev.serialNumber}`) - .addEventListener("click", removeDevice); -} +const deviceSerialInput = document.querySelector("#deviceSerial"); // display devices in a list -function displayDeviceList(list) { +function showDeviceList(list) { + const deviceList = document.querySelector("#list-device"); + // reset list deviceList.textContent = ''; // append each device element - list.forEach((dev) => displayDeviceElement(dev)); + list.forEach((dev) => { + const a = document.createElement("a"); + a.id = dev.serialNumber; + a.className = + "list-group-item list-group-item-action flex-column align-items-start"; + a.innerHTML = `
+
${dev.name}
+ +
+
+ ${dev.serialNumber} +
`; + deviceList.appendChild(a); + document + .querySelector(`#delete-${dev.serialNumber}`) + .addEventListener("click", deleteDevice); + }); } // get devices from database async function getDevices() { const res = await fetch("/devices/get", { method: "GET" }); const data = await res.json(); - displayDeviceList(data); + showDeviceList(data); } // add device to database @@ -57,11 +55,11 @@ async function addDevice(e) { const data = await res.json(); if (!("exists" in data)) { - displayDeviceList(data); + showDeviceList(data); // clear input fields deviceNameInput.value = ""; - serialNumberInput.value = ""; + deviceSerialInput.value = ""; } else { console.log("Device already exists"); // @TODO Show alert @@ -69,7 +67,7 @@ async function addDevice(e) { } // remove device from database -async function removeDevice(e) { +async function deleteDevice(e) { const serialNumber = e.target.offsetParent.id; const res = await fetch(`/devices/delete?serialNumber=${serialNumber}`, { @@ -79,7 +77,7 @@ async function removeDevice(e) { const data = await res.json(); if (!("exists" in data)) { - displayDeviceList(data); + showDeviceList(data); } } @@ -89,7 +87,7 @@ function init() { // clear inputs deviceNameInput.value = ""; - serialNumberInput.value = ""; + deviceSerialInput.value = ""; } // window event listeners diff --git a/webapp/public/js/modules/commands.js b/webapp/public/js/modules/commands.js new file mode 100644 index 0000000..5287c59 --- /dev/null +++ b/webapp/public/js/modules/commands.js @@ -0,0 +1,16 @@ +async function cmdGoto() { + const waypoint = document.querySelector("#select-goto").value; + + const res = await fetch("/command/goto", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + serialNumber: sessionStorage.getItem("selectedSerial"), + waypoint: waypoint, + }), + }); + const data = await res.json(); + console.log(data); +} + +export { cmdGoto }; diff --git a/webapp/public/js/video.js b/webapp/public/js/video.js index c30de8e..ffdcf1b 100644 --- a/webapp/public/js/video.js +++ b/webapp/public/js/video.js @@ -13,7 +13,7 @@ function openVideo() { // https://github.com/jitsi/jitsi-meet/blob/master/interface_config.js const options = { roomName: `temi-${sessionStorage.getItem("selectedSerialNumber")}`, - height: window.innerHeight * 0.6, + height: window.innerHeight * 0.7, parentNode: divVideo, interfaceConfigOverwrite: { DEFAULT_BACKGROUND: "#ffffff", @@ -60,8 +60,10 @@ function closeVideo(handle) { window.onresize = () => { - console.log(window.innerHeight * 0.6); - videoHandle.getIFrame().style.height = `${window.innerHeight * 0.6}px`; + if (videoHandle !== undefined) { + console.log(window.innerHeight * 0.6); + videoHandle.getIFrame().style.height = `${window.innerHeight * 0.6}px`; + } } document.querySelector("#btn-video").addEventListener("click", openVideo); \ No newline at end of file diff --git a/webapp/views/console.ejs b/webapp/views/console.ejs index e96f5bb..20f2783 100644 --- a/webapp/views/console.ejs +++ b/webapp/views/console.ejs @@ -25,31 +25,23 @@