diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..ef5e4de --- /dev/null +++ b/.editorconfig @@ -0,0 +1,16 @@ +root = true + +[*] +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true +charset = utf-8 + +[*.ts] +indent_style = tab +indent_size = 2 +tab_width = 2 + +[{package.json,*.yml,*.cjson}] +indent_style = tab +indent_size = 4 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bdac3a9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,16 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Dependency directories +node_modules/ + +# Compiled Typescript files +dist/ + +# Config files +src/config/*.ini +!src/config/*.example.ini diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..9ab90f5 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,18 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "node", + "request": "launch", + "name": "Launch Program", + "program": "${workspaceFolder}\\dist\\server.js", + "outFiles": [ + "${workspaceFolder}/**/*.js" + ], + "console": "integratedTerminal" + } + ] +} diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..f00fb93 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,28 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=733558 + // for the documentation about the tasks.json format + "version": "2.0.0", + "tasks": [ + { + "type": "typescript", + "tsconfig": "tsconfig.json", + "problemMatcher": [ + "$tsc" + ], + "label": "build" + }, + { + "type": "typescript", + "tsconfig": "tsconfig.json", + "option": "watch", + "problemMatcher": [ + "$tsc-watch" + ], + "label": "watch", + "group": { + "kind": "build", + "isDefault": true + } + } + ] +} diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..b02e62d --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 FRC-3313-Team + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..e0bb1eb --- /dev/null +++ b/package-lock.json @@ -0,0 +1,1154 @@ +{ + "name": "scouting-back", + "version": "0.1.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@types/body-parser": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.17.0.tgz", + "integrity": "sha512-a2+YeUjPkztKJu5aIF2yArYFQQp8d51wZ7DavSHjFuY1mqVgidGyzEQ41JIVNy82fXj8yPgy2vJmfIywgESW6w==", + "dev": true, + "requires": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "@types/bson": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@types/bson/-/bson-1.0.11.tgz", + "integrity": "sha512-j+UcCWI+FsbI5/FQP/Kj2CXyplWAz39ktHFkXk84h7dNblKRSoNJs95PZFRd96NQGqsPEPgeclqnznWZr14ZDA==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/connect": { + "version": "3.4.32", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.32.tgz", + "integrity": "sha512-4r8qa0quOvh7lGD0pre62CAb1oni1OO6ecJLGCezTmhQ8Fz50Arx9RUszryR8KlgK6avuSXvviL6yWyViQABOg==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/cookie-parser": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@types/cookie-parser/-/cookie-parser-1.4.1.tgz", + "integrity": "sha512-iJY6B3ZGufLiDf2OCAgiAAQuj1sMKC/wz/7XCEjZ+/MDuultfFJuSwrBKcLSmJ5iYApLzCCYBYJZs0Ws8GPmwA==", + "dev": true, + "requires": { + "@types/express": "*" + } + }, + "@types/ejs": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/@types/ejs/-/ejs-2.6.1.tgz", + "integrity": "sha512-WD8015V8Y+uDfFIjiVHU7t9SgHptqTGGN8w0A2LcrL0NLtqColM15cswkHZMUfodyuTf35sup8vW0hpWRHu0dQ==", + "dev": true + }, + "@types/events": { + "version": "1.2.0", + "resolved": "http://registry.npmjs.org/@types/events/-/events-1.2.0.tgz", + "integrity": "sha512-KEIlhXnIutzKwRbQkGWb/I4HFqBuUykAdHgDED6xqwXJfONCjF5VoE0cXEiurh3XauygxzeDzgtXUqvLkxFzzA==", + "dev": true + }, + "@types/express": { + "version": "4.16.0", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.16.0.tgz", + "integrity": "sha512-TtPEYumsmSTtTetAPXlJVf3kEqb6wZK0bZojpJQrnD/djV4q1oB6QQ8aKvKqwNPACoe02GNiy5zDzcYivR5Z2w==", + "dev": true, + "requires": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "*", + "@types/serve-static": "*" + } + }, + "@types/express-serve-static-core": { + "version": "4.16.0", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.16.0.tgz", + "integrity": "sha512-lTeoCu5NxJU4OD9moCgm0ESZzweAx0YqsAcab6OB0EB3+As1OaHtKnaGJvcngQxYsi9UNv0abn4/DRavrRxt4w==", + "dev": true, + "requires": { + "@types/events": "*", + "@types/node": "*", + "@types/range-parser": "*" + } + }, + "@types/ini": { + "version": "1.3.30", + "resolved": "https://registry.npmjs.org/@types/ini/-/ini-1.3.30.tgz", + "integrity": "sha512-2+iF8zPSbpU83UKE+PNd4r/MhwNAdyGpk3H+VMgEH3EhjFZq1kouLgRoZrmIcmoGX97xFvqdS44DkICR5Nz3tQ==", + "dev": true + }, + "@types/jsonwebtoken": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-8.3.0.tgz", + "integrity": "sha512-YKnUTR4VxwljbPORPrRon9E3uel1aD8nUdvzqArCCdMTWPvo0gnI2UZkwIHN2QATdj6HYXV/Iq3/KcecAO42Ww==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/mime": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-2.0.0.tgz", + "integrity": "sha512-A2TAGbTFdBw9azHbpVd+/FkdW2T6msN1uct1O9bH3vTerEHKZhTXJUQXy+hNq1B0RagfU8U+KBdqiZpxjhOUQA==", + "dev": true + }, + "@types/mongodb": { + "version": "3.1.15", + "resolved": "https://registry.npmjs.org/@types/mongodb/-/mongodb-3.1.15.tgz", + "integrity": "sha512-JSvtmrdrh88WH0Lo8Hq7sB1FkEChkrt6+fAZdFhEsRXcUetnrdU7wd2yar40tPg5wfRI2t31yduQgPiMUvgEEA==", + "dev": true, + "requires": { + "@types/bson": "*", + "@types/node": "*" + } + }, + "@types/mongoose": { + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/@types/mongoose/-/mongoose-5.3.4.tgz", + "integrity": "sha512-VHP6aUswXIaFlLfF+noeMYf4NdkgylGlrAujr5g6aFcX1fYt3PpBsI+LGFnuK11HL7XPYQR3IigiqDq1rnBjeg==", + "dev": true, + "requires": { + "@types/mongodb": "*", + "@types/node": "*" + } + }, + "@types/morgan": { + "version": "1.7.35", + "resolved": "https://registry.npmjs.org/@types/morgan/-/morgan-1.7.35.tgz", + "integrity": "sha512-E9qFi0seOkdlQnCTPv54brNfGWeFdRaEhI5tSue4pdx/V+xfxvMETsxXhOEcj1cYL+0n/jcTEmj/jD2gjzCwMg==", + "dev": true, + "requires": { + "@types/express": "*" + } + }, + "@types/node": { + "version": "10.12.12", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.12.tgz", + "integrity": "sha512-Pr+6JRiKkfsFvmU/LK68oBRCQeEg36TyAbPhc2xpez24OOZZCuoIhWGTd39VZy6nGafSbxzGouFPTFD/rR1A0A==", + "dev": true + }, + "@types/range-parser": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.3.tgz", + "integrity": "sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA==", + "dev": true + }, + "@types/serve-static": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.2.tgz", + "integrity": "sha512-/BZ4QRLpH/bNYgZgwhKEh+5AsboDBcUdlBYgzoLX0fpj3Y2gp6EApyOlM3bK53wQS/OE1SrdSYBAbux2D1528Q==", + "dev": true, + "requires": { + "@types/express-serve-static-core": "*", + "@types/mime": "*" + } + }, + "@types/uuid": { + "version": "3.4.4", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-3.4.4.tgz", + "integrity": "sha512-tPIgT0GUmdJQNSHxp0X2jnpQfBSTfGxUMc/2CXBU2mnyTFVYVa2ojpoQ74w0U2yn2vw3jnC640+77lkFFpdVDw==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "accepts": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz", + "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=", + "requires": { + "mime-types": "~2.1.18", + "negotiator": "0.6.1" + } + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "array-flatten": { + "version": "1.1.1", + "resolved": "http://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + }, + "async": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.1.tgz", + "integrity": "sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ==", + "requires": { + "lodash": "^4.17.10" + } + }, + "babel-code-frame": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", + "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", + "dev": true, + "requires": { + "chalk": "^1.1.3", + "esutils": "^2.0.2", + "js-tokens": "^3.0.2" + }, + "dependencies": { + "chalk": { + "version": "1.1.3", + "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + } + } + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "basic-auth": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", + "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", + "requires": { + "safe-buffer": "5.1.2" + } + }, + "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.18.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz", + "integrity": "sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=", + "requires": { + "bytes": "3.0.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "~1.6.3", + "iconv-lite": "0.4.23", + "on-finished": "~2.3.0", + "qs": "6.5.2", + "raw-body": "2.3.3", + "type-is": "~1.6.16" + } + }, + "brace-expansion": { + "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.0", + "resolved": "https://registry.npmjs.org/bson/-/bson-1.1.0.tgz", + "integrity": "sha512-9Aeai9TacfNtWXOYarkFJRW2CWo+dRon+fuLZYJmvLV3+MiUp0bEI6IAZfXEIg7/Pl/7IWlLaDnhzTsD81etQA==" + }, + "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=" + }, + "builtin-modules": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", + "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", + "dev": true + }, + "bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" + }, + "chalk": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", + "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "dependencies": { + "ansi-styles": { + "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" + } + }, + "supports-color": { + "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" + } + } + } + }, + "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" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "commander": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.19.0.tgz", + "integrity": "sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==", + "dev": true + }, + "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 + }, + "content-disposition": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", + "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=" + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" + }, + "cookie": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", + "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" + }, + "cookie-parser": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.3.tgz", + "integrity": "sha1-D+MfoZ0AC5X0qt8fU/3CuKIDuqU=", + "requires": { + "cookie": "0.3.1", + "cookie-signature": "1.0.6" + } + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + }, + "csprng": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/csprng/-/csprng-0.1.2.tgz", + "integrity": "sha1-S8aPEvo2jSUqWYQcusqXSxirReI=", + "requires": { + "sequin": "*" + } + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "diff": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "dev": true + }, + "ecdsa-sig-formatter": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.10.tgz", + "integrity": "sha1-HFlQAPBKiJffuFAAiSoPTDOvhsM=", + "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": "2.6.1", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-2.6.1.tgz", + "integrity": "sha512-0xy4A/twfrRCnkhfk8ErDi5DqdAsAqeGxht4xkCUrsvhhbQNs7E+4jV0CN7+NKIY0aHE72+XvqtBIXzD31ZbXQ==" + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "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 + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, + "esutils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", + "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", + "dev": true + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" + }, + "express": { + "version": "4.16.4", + "resolved": "https://registry.npmjs.org/express/-/express-4.16.4.tgz", + "integrity": "sha512-j12Uuyb4FMrd/qQAm6uCHAkPtO8FDTRJZBDd5D2KOL2eLaz1yUNdUB/NOIyq0iU4q4cFarsUCrnFDPBcnksuOg==", + "requires": { + "accepts": "~1.3.5", + "array-flatten": "1.1.1", + "body-parser": "1.18.3", + "content-disposition": "0.5.2", + "content-type": "~1.0.4", + "cookie": "0.3.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.1.1", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.2", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.4", + "qs": "6.5.2", + "range-parser": "~1.2.0", + "safe-buffer": "5.1.2", + "send": "0.16.2", + "serve-static": "1.13.2", + "setprototypeof": "1.1.0", + "statuses": "~1.4.0", + "type-is": "~1.6.16", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "dependencies": { + "statuses": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", + "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==" + } + } + }, + "finalhandler": { + "version": "1.1.1", + "resolved": "http://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", + "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==", + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.2", + "statuses": "~1.4.0", + "unpipe": "~1.0.0" + }, + "dependencies": { + "statuses": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", + "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==" + } + } + }, + "forwarded": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "glob": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "http-errors": { + "version": "1.6.3", + "resolved": "http://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + } + }, + "iconv-lite": { + "version": "0.4.23", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", + "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "ini": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==" + }, + "ipaddr.js": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.8.0.tgz", + "integrity": "sha1-6qM9bd16zo9/b+DJygRA5wZzix4=" + }, + "js-tokens": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", + "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", + "dev": true + }, + "js-yaml": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.0.tgz", + "integrity": "sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "jsonwebtoken": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.4.0.tgz", + "integrity": "sha512-coyXjRTCy0pw5WYBpMvWOMN+Kjaik2MwTUIq9cna/W7NpO9E+iYbumZONAz3hcr+tXFJECoQVrtmIoC3Oz0gvg==", + "requires": { + "jws": "^3.1.5", + "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" + }, + "dependencies": { + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + } + } + }, + "jwa": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.1.6.tgz", + "integrity": "sha512-tBO/cf++BUsJkYql/kBbJroKOgHWEigTKBAjjBEmrMGYd1QMBC74Hr4Wo2zCZw6ZrVhlJPvoMrkcOnlWR/DJfw==", + "requires": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.10", + "safe-buffer": "^5.0.1" + } + }, + "jws": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.1.5.tgz", + "integrity": "sha512-GsCSexFADNQUr8T5HPJvayTjvPIfoyJPtLQBwn5a4WZQchcrPMPMAWcC1AzJVRDKyD6ZPROPAxgv6rfHViO4uQ==", + "requires": { + "jwa": "^1.1.5", + "safe-buffer": "^5.0.1" + } + }, + "kareem": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.3.0.tgz", + "integrity": "sha512-6hHxsp9e6zQU8nXsP+02HGWXwTkOEw6IROhF2ZA28cYbUk4eJ6QbtZvdqZOdD9YPKghG3apk5eOCvs+tLl3lRg==" + }, + "lodash": { + "version": "4.17.11", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", + "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==" + }, + "lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=" + }, + "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": "http://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" + }, + "memory-pager": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.1.0.tgz", + "integrity": "sha512-Mf9OHV/Y7h6YWDxTzX/b4ZZ4oh9NSXblQL8dtPCOomOtZciEHxePR78+uHFLLlsk01A6jVHhHsQZZ/WcIPpnzg==", + "optional": true + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" + }, + "mime": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", + "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==" + }, + "mime-db": { + "version": "1.37.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.37.0.tgz", + "integrity": "sha512-R3C4db6bgQhlIhPU48fUtdVmKnflq+hRdad7IyKhtFj06VPNVdk2RhiYL3UjQIlso8L+YxAtFkobT0VK+S/ybg==" + }, + "mime-types": { + "version": "2.1.21", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.21.tgz", + "integrity": "sha512-3iL6DbwpyLzjR3xHSFNFeb9Nz/M8WDkX33t1GFQnFOllWk8pOrh/LSrB5OXlnlW5P9LH73X6loW/eogc+F5lJg==", + "requires": { + "mime-db": "~1.37.0" + } + }, + "minimatch": { + "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" + } + }, + "mongodb": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.1.10.tgz", + "integrity": "sha512-Uml42GeFxhTGQVml1XQ4cD0o/rp7J2ROy0fdYUcVitoE7vFqEhKH4TYVqRDpQr/bXtCJVxJdNQC1ntRxNREkPQ==", + "requires": { + "mongodb-core": "3.1.9", + "safe-buffer": "^5.1.2" + } + }, + "mongodb-core": { + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/mongodb-core/-/mongodb-core-3.1.9.tgz", + "integrity": "sha512-MJpciDABXMchrZphh3vMcqu8hkNf/Mi+Gk6btOimVg1XMxLXh87j6FAvRm+KmwD1A9fpu3qRQYcbQe4egj23og==", + "requires": { + "bson": "^1.1.0", + "require_optional": "^1.0.1", + "safe-buffer": "^5.1.2", + "saslprep": "^1.0.0" + } + }, + "mongoose": { + "version": "5.3.15", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-5.3.15.tgz", + "integrity": "sha512-51PchMQn2kFZYPSe0vfr8RcJxm6g7RokfcVl2xVORogtT5GUKu6RgnRXsUV7v9vZ+b3Wh//ph53kuIyhtXi56g==", + "requires": { + "async": "2.6.1", + "bson": "~1.1.0", + "kareem": "2.3.0", + "lodash.get": "4.4.2", + "mongodb": "3.1.10", + "mongodb-core": "3.1.9", + "mongoose-legacy-pluralize": "1.0.2", + "mpath": "0.5.1", + "mquery": "3.2.0", + "ms": "2.0.0", + "regexp-clone": "0.0.1", + "safe-buffer": "5.1.2", + "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==" + }, + "morgan": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.9.1.tgz", + "integrity": "sha512-HQStPIV4y3afTiCYVxirakhlCfGkI161c76kKFca7Fk1JusM//Qeo1ej2XaMniiNeaZklMVrh3vTtIzpzwbpmA==", + "requires": { + "basic-auth": "~2.0.0", + "debug": "2.6.9", + "depd": "~1.1.2", + "on-finished": "~2.3.0", + "on-headers": "~1.0.1" + } + }, + "mpath": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.5.1.tgz", + "integrity": "sha512-H8OVQ+QEz82sch4wbODFOz+3YQ61FYz/z3eJ5pIdbMEaUzDqA268Wd+Vt4Paw9TJfvDgVKaayC0gBzMIw2jhsg==" + }, + "mquery": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/mquery/-/mquery-3.2.0.tgz", + "integrity": "sha512-qPJcdK/yqcbQiKoemAt62Y0BAc0fTEKo1IThodBD+O5meQRJT/2HSe5QpBNwaa4CjskoGrYWsEyjkqgiE0qjhg==", + "requires": { + "bluebird": "3.5.1", + "debug": "3.1.0", + "regexp-clone": "0.0.1", + "safe-buffer": "5.1.2", + "sliced": "1.0.1" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "requires": { + "ms": "2.0.0" + } + } + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "negotiator": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", + "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=" + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "requires": { + "ee-first": "1.1.1" + } + }, + "on-headers": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.1.tgz", + "integrity": "sha1-ko9dD0cNSTQmUepnlLCFfBAGk/c=" + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "parseurl": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", + "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=" + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "http://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "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==", + "dev": true + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + }, + "proxy-addr": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.4.tgz", + "integrity": "sha512-5erio2h9jp5CHGwcybmxmVqHmnCBZeewlfJ0pex+UW7Qny7OOZXTtH56TGNyBizkgiOwhJtMKrVzDTeKcySZwA==", + "requires": { + "forwarded": "~0.1.2", + "ipaddr.js": "1.8.0" + } + }, + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" + }, + "range-parser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", + "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=" + }, + "raw-body": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz", + "integrity": "sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==", + "requires": { + "bytes": "3.0.0", + "http-errors": "1.6.3", + "iconv-lite": "0.4.23", + "unpipe": "1.0.0" + } + }, + "regexp-clone": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/regexp-clone/-/regexp-clone-0.0.1.tgz", + "integrity": "sha1-p8LgmJH9vzj7sQ03b7cwA+aKxYk=" + }, + "require_optional": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require_optional/-/require_optional-1.0.1.tgz", + "integrity": "sha512-qhM/y57enGWHAe3v/NcwML6a3/vfESLe/sGM2dII+gEO0BpKRUkWZow/tyloNqJyN6kXSl3RyyM8Ll5D/sJP8g==", + "requires": { + "resolve-from": "^2.0.0", + "semver": "^5.1.0" + } + }, + "resolve": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.8.1.tgz", + "integrity": "sha512-AicPrAC7Qu1JxPCZ9ZgCZlY35QgFnNqc+0LtbRNxnVw4TXvjQ72wnuL9JQcEBgXkI9JM8MsT9kaQoHcpCRJOYA==", + "dev": true, + "requires": { + "path-parse": "^1.0.5" + } + }, + "resolve-from": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz", + "integrity": "sha1-lICrIOlP+h2egKgEx+oUdhGWa1c=" + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "saslprep": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/saslprep/-/saslprep-1.0.2.tgz", + "integrity": "sha512-4cDsYuAjXssUSjxHKRe4DTZC0agDwsCqcMqtJAQPzC74nJ7LfAJflAtC1Zed5hMzEQKj82d3tuzqdGNRsLJ4Gw==", + "optional": true, + "requires": { + "sparse-bitfield": "^3.0.3" + } + }, + "semver": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", + "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==" + }, + "send": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", + "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", + "requires": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.6.2", + "mime": "1.4.1", + "ms": "2.0.0", + "on-finished": "~2.3.0", + "range-parser": "~1.2.0", + "statuses": "~1.4.0" + }, + "dependencies": { + "statuses": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", + "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==" + } + } + }, + "sequin": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/sequin/-/sequin-0.1.1.tgz", + "integrity": "sha1-XC04nWajg3NOqvvEXt6ywcsb5wE=" + }, + "serve-static": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz", + "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==", + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.2", + "send": "0.16.2" + } + }, + "setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" + }, + "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" + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + }, + "tslib": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", + "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==", + "dev": true + }, + "tslint": { + "version": "5.11.0", + "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.11.0.tgz", + "integrity": "sha1-mPMMAurjzecAYgHkwzywi0hYHu0=", + "dev": true, + "requires": { + "babel-code-frame": "^6.22.0", + "builtin-modules": "^1.1.1", + "chalk": "^2.3.0", + "commander": "^2.12.1", + "diff": "^3.2.0", + "glob": "^7.1.1", + "js-yaml": "^3.7.0", + "minimatch": "^3.0.4", + "resolve": "^1.3.2", + "semver": "^5.3.0", + "tslib": "^1.8.0", + "tsutils": "^2.27.2" + } + }, + "tsutils": { + "version": "2.29.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz", + "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + }, + "type-is": { + "version": "1.6.16", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz", + "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==", + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.18" + } + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" + }, + "uuid": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", + "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..c44dee2 --- /dev/null +++ b/package.json @@ -0,0 +1,47 @@ +{ + "name": "scouting-back", + "version": "0.1.0", + "description": "", + "main": "./src/app.js", + "scripts": { + "build": "tsc", + "dev": "ts-node ./src/server.ts", + "start": "node ./dist/server.js", + "prod": "npm run build && npm start", + "lint": "tslint -p tsconfig.json" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/FRC-3313-Team/scouting-back.git" + }, + "author": "FRC Team 3313", + "license": "MIT", + "bugs": { + "url": "https://github.com/FRC-3313-Team/scouting-back/issues" + }, + "homepage": "https://github.com/FRC-3313-Team/scouting-back#readme", + "dependencies": { + "body-parser": "^1.18.3", + "cookie-parser": "^1.4.3", + "csprng": "^0.1.2", + "ejs": "^2.6.1", + "express": "^4.16.4", + "ini": "^1.3.5", + "jsonwebtoken": "^8.4.0", + "mongoose": "^5.3.15", + "morgan": "^1.9.1", + "uuid": "^3.3.2" + }, + "devDependencies": { + "@types/body-parser": "^1.17.0", + "@types/cookie-parser": "^1.4.1", + "@types/ejs": "^2.6.1", + "@types/express": "^4.16.0", + "@types/ini": "^1.3.30", + "@types/jsonwebtoken": "^8.3.0", + "@types/mongoose": "^5.3.4", + "@types/morgan": "^1.7.35", + "@types/uuid": "^3.4.4", + "tslint": "^5.11.0" + } +} \ No newline at end of file diff --git a/src/app.ts b/src/app.ts new file mode 100644 index 0000000..f07c5fd --- /dev/null +++ b/src/app.ts @@ -0,0 +1,60 @@ +import * as path from "path"; + +import * as express from "express"; +import * as bodyParser from "body-parser"; +import * as cookieParser from "cookie-parser"; +import * as morgan from "morgan"; + +import { authentication } from "./middleware/authentication"; + +import api from "./routes/api"; +import dashboard from "./routes/dashboard"; + +class App { + public app: express.Application; + + constructor() { + this.app = express(); + + this.config(); + this.middleware(); + this.routes(); + } + + private config(): void { + this.app.set("view engine", "ejs"); + this.app.set("views", path.join(__dirname, "../views")); + } + + private middleware(): void { + this.app.use(morgan("dev")); + this.app.use(bodyParser.json()); + this.app.use(bodyParser.urlencoded({ extended: false })); + this.app.use(cookieParser()); + + this.app.use(authentication); + } + + private routes(): void { + this.app.use(express.static("static")); + this.app.use("/favicon.ico", express.static("static/img/favicon.ico")); + + this.app.use("/dash", dashboard); + this.app.use("/api", api); + + this.app.use("/", (req: express.Request, res: express.Response, next: express.NextFunction) => { + if (req.url === "/") { + res.redirect("/dash"); + } else { + next(); + } + }); + + this.app.use("*", (req: express.Request, res: express.Response) => { + res.status(404) + .render("pages/404"); + }); + } +} + +export default new App().app; diff --git a/src/config/app.example.ini b/src/config/app.example.ini new file mode 100644 index 0000000..2dfa997 --- /dev/null +++ b/src/config/app.example.ini @@ -0,0 +1,14 @@ +[server] +port=3000 + +[database] +url=mongodb://localhost:27017/ ; URL for connecting to MongoDB server +mainDatabase=dev-frc ; Name of database used for storing all event-agnostic data, e.g. devices +eventDatabase=dev-frc- ; Name of database used for storing event-specific data, the event code (e.g. 2018ndgf) is appended to this + +[device] +activationCodeLength=6 ; Length of activation codes for new devices + +[dashboard] +password=password ; Password for the web dashboard +jwtSigningKey=Please Change This In Production ; Key used for sign JWTs for dashboard logins diff --git a/src/middleware/authentication.ts b/src/middleware/authentication.ts new file mode 100644 index 0000000..9c6a10c --- /dev/null +++ b/src/middleware/authentication.ts @@ -0,0 +1,49 @@ +import * as express from "express"; + +import config from "../util/config"; +import asyncWrapper from "../util/asyncWrapper"; +import * as jwt from "jsonwebtoken"; + +import { Device } from "../models/device"; + +export enum AuthorizationType { + Dashboard, + Device, + None, +} + +export const authentication = asyncWrapper(async (req: express.Request, res: express.Response, next: express.NextFunction) => { + req.user = { + authorizationType: AuthorizationType.None, + }; + + // Device authentication + const deviceToken = req.headers["device-token"] as string; + + if (deviceToken) { + const device = await Device.findOne({ token: deviceToken }); + + if (device && device.active) { + req.user.authorizationType = AuthorizationType.Device; + return next(); + } + } + + // Dashboard authentication + const dashboardToken = req.cookies["dashboard-token"] as string; + + if (dashboardToken) { + jwt.verify(dashboardToken, config.dashboard.jwtSigningKey, (err, decoded) => { + if (err) { + return next(); + } + + // @ts-ignore: this is 100% fine, I promise + req.user = decoded; + + return next(); + }); + } else { + return next(); + } +}); diff --git a/src/middleware/requireAuth.ts b/src/middleware/requireAuth.ts new file mode 100644 index 0000000..15d58d9 --- /dev/null +++ b/src/middleware/requireAuth.ts @@ -0,0 +1,19 @@ +import * as express from "express"; + +import { AuthorizationType } from "./authentication"; +import asyncWrapper from "../util/asyncWrapper"; + +const requireAuth = +(authorizationType: AuthorizationType): (req: express.Request, res: express.Response, next: express.NextFunction) => void => { + return asyncWrapper(async (req: express.Request, res: express.Response, next: express.NextFunction) => { + if (req.user.authorizationType === authorizationType) { + return next(); + } else { + res.status(403) + .contentType("text/plain") + .send(`${AuthorizationType[authorizationType]} authorization required to access this endpoint`); + } + }); +}; + +export default requireAuth; diff --git a/src/models/device.ts b/src/models/device.ts new file mode 100644 index 0000000..6147645 --- /dev/null +++ b/src/models/device.ts @@ -0,0 +1,21 @@ +import { Document, Schema, Model, model } from "mongoose"; + +import { connections } from "../util/db"; + +interface IDevice { + active: boolean; + name: string; + activationCode?: string; + token?: string; +} + +interface IDeviceModel extends IDevice, Document { } + +export const DeviceSchema: Schema = new Schema({ + active: Boolean, + name: String, + activationCode: String, + token: String, +}); + +export const Device: Model = connections.main.model("Device", DeviceSchema); diff --git a/src/routes/api.ts b/src/routes/api.ts new file mode 100644 index 0000000..6d39474 --- /dev/null +++ b/src/routes/api.ts @@ -0,0 +1,13 @@ +import * as express from "express"; + +import device from "./api/device"; +import login from "./api/login"; +import logout from "./api/logout"; + +const router = express.Router(); + +router.use("/device", device); +router.use("/login", login); +router.use("/logout", logout); + +export default router; diff --git a/src/routes/api/device.ts b/src/routes/api/device.ts new file mode 100644 index 0000000..8095cfd --- /dev/null +++ b/src/routes/api/device.ts @@ -0,0 +1,60 @@ +import * as express from "express"; +import * as rand from "csprng"; +import * as uuid from "uuid"; + +import config from "../../util/config"; +import asyncWrapper from "../../util/asyncWrapper"; +import requireAuth from "../../middleware/requireAuth"; + +import { Device } from "../../models/device"; +import { AuthorizationType } from "../../middleware/authentication"; + +const router = express.Router(); + +router.post("/new", + requireAuth(AuthorizationType.Dashboard), + asyncWrapper(async (req: express.Request, res: express.Response, next: express.NextFunction) => { + const deviceName = req.body.name ? req.body.name : rand(12, 36); + + const checkForExistingName = await Device.findOne({ name: deviceName }); + if (checkForExistingName) { + return res.status(409) + .contentType("text/plain") + .send("a device with this name already exists"); + } + + let newDevice = new Device({ + active: false, + name: deviceName, + activationCode: rand(config.device.activationCodeLength * 3, 10), + }); + + newDevice = await newDevice.save(); + + res.status(200).send({ + code: newDevice.activationCode, + name: newDevice.name, + }); +})); + +router.post("/register", asyncWrapper(async (req: express.Request, res: express.Response, next: express.NextFunction) => { + let device = await Device.findOne({ activationCode: req.body.activationCode }); + + if (device === null) { + return res.status(400) + .contentType("text/plain") + .send("invalid code"); + } + + device.activationCode = undefined; + device.active = true; + device.token = uuid.v4(); + + device = await device.save(); + + res.status(200).send({ + token: device.token, + }); +})); + +export default router; diff --git a/src/routes/api/login.ts b/src/routes/api/login.ts new file mode 100644 index 0000000..9f12677 --- /dev/null +++ b/src/routes/api/login.ts @@ -0,0 +1,28 @@ +import * as express from "express"; +import * as jwt from "jsonwebtoken"; + +import config from "../../util/config"; +import asyncWrapper from "../../util/asyncWrapper"; +import { AuthorizationType } from "../../middleware/authentication"; + +const router = express.Router(); + +router.post("/", asyncWrapper(async (req: express.Request, res: express.Response, next: express.NextFunction) => { + const password = req.body.password; + + if (password === config.dashboard.password) { + const payload = { + authorizationType: AuthorizationType.Dashboard, + }; + const signedToken = jwt.sign(payload, config.dashboard.jwtSigningKey, { expiresIn: "1h" }); + + res.status(200) + .cookie("dashboard-token", signedToken) + .send(); + } else { + res.status(403) + .send(); + } +})); + +export default router; diff --git a/src/routes/api/logout.ts b/src/routes/api/logout.ts new file mode 100644 index 0000000..e3811d4 --- /dev/null +++ b/src/routes/api/logout.ts @@ -0,0 +1,17 @@ +import * as express from "express"; + +import asyncWrapper from "../../util/asyncWrapper"; +import requireAuth from "../../middleware/requireAuth"; + +import { AuthorizationType } from "../../middleware/authentication"; + +const router = express.Router(); + +router.post("/", + requireAuth(AuthorizationType.Dashboard), + asyncWrapper(async (req: express.Request, res: express.Response, next: express.NextFunction) => { + res.clearCookie("dashboard-token") + .send(); +})); + +export default router; diff --git a/src/routes/dashboard.ts b/src/routes/dashboard.ts new file mode 100644 index 0000000..33ad12b --- /dev/null +++ b/src/routes/dashboard.ts @@ -0,0 +1,22 @@ +import * as express from "express"; + +import { AuthorizationType } from "../middleware/authentication"; +import asyncWrapper from "../util/asyncWrapper"; + +import login from "./dashboard/login"; +import home from "./dashboard/home"; + +const router = express.Router(); + +router.use("/login", login); +router.use("/home", home); + +router.get("/", asyncWrapper(async (req: express.Request, res: express.Response, next: express.NextFunction) => { + if (req.user.authorizationType === AuthorizationType.Dashboard) { + res.redirect("/dash/home"); + } else { + res.redirect("/dash/login"); + } +})); + +export default router; diff --git a/src/routes/dashboard/home.ts b/src/routes/dashboard/home.ts new file mode 100644 index 0000000..a327740 --- /dev/null +++ b/src/routes/dashboard/home.ts @@ -0,0 +1,16 @@ +import * as express from "express"; + +import asyncWrapper from "../../util/asyncWrapper"; +import { AuthorizationType } from "../../middleware/authentication"; + +const router = express.Router(); + +router.get("/", asyncWrapper(async (req: express.Request, res: express.Response, next: express.NextFunction) => { + if (req.user.authorizationType !== AuthorizationType.Dashboard) { + return res.redirect("/dash/login"); + } + + res.render("pages/home"); +})); + +export default router; diff --git a/src/routes/dashboard/login.ts b/src/routes/dashboard/login.ts new file mode 100644 index 0000000..039007e --- /dev/null +++ b/src/routes/dashboard/login.ts @@ -0,0 +1,11 @@ +import * as express from "express"; + +import asyncWrapper from "../../util/asyncWrapper"; + +const router = express.Router(); + +router.get("/", asyncWrapper(async (req: express.Request, res: express.Response, next: express.NextFunction) => { + res.render("pages/login"); +})); + +export default router; diff --git a/src/server.ts b/src/server.ts new file mode 100644 index 0000000..3e39f5b --- /dev/null +++ b/src/server.ts @@ -0,0 +1,9 @@ +import config from "./util/config"; + +import app from "./app"; + +const PORT = config.server.port; + +app.listen(PORT, () => { + console.log(`Server listening on port: ${PORT}`); +}); diff --git a/src/types/csprng.d.ts b/src/types/csprng.d.ts new file mode 100644 index 0000000..105e10d --- /dev/null +++ b/src/types/csprng.d.ts @@ -0,0 +1,5 @@ +declare module "csprng" { + function csprng(bit: number, radix: number): string; + namespace csprng { } + export = csprng; +} diff --git a/src/types/express.d.ts b/src/types/express.d.ts new file mode 100644 index 0000000..3e60b1c --- /dev/null +++ b/src/types/express.d.ts @@ -0,0 +1,11 @@ +import { AuthorizationType } from "../middleware/authentication"; + +declare global { + namespace Express { + export interface Request { + user: { + authorizationType: AuthorizationType + } + } + } +} diff --git a/src/util/asyncWrapper.ts b/src/util/asyncWrapper.ts new file mode 100644 index 0000000..433eccf --- /dev/null +++ b/src/util/asyncWrapper.ts @@ -0,0 +1,9 @@ +import * as express from "express"; + +const asyncWrapper = (fn: any) => + (req: express.Request, res: express.Response, next: express.NextFunction) => { + Promise.resolve(fn(req, res, next)) + .catch(next); +}; + +export default asyncWrapper; diff --git a/src/util/config.ts b/src/util/config.ts new file mode 100644 index 0000000..1e51b35 --- /dev/null +++ b/src/util/config.ts @@ -0,0 +1,30 @@ +import * as ini from "ini"; +import * as fs from "fs"; + +interface IConfig { + server: { + port: number, + }; + + database: { + url: string, + mainDatabase: string, + eventDatabase: string, + }; + + device: { + activationCodeLength: number, + }; + + dashboard: { + password: string, + jwtSigningKey: string, + }; +} + +const appConfig = ini.parse(fs.readFileSync("./src/config/app.ini", "utf-8")) as IConfig; +const fallbackConfig = ini.parse(fs.readFileSync("./src/config/app.example.ini", "utf-8")) as IConfig; + +const mergedConfig: IConfig = { ...fallbackConfig, ...appConfig }; + +export default mergedConfig; diff --git a/src/util/db.ts b/src/util/db.ts new file mode 100644 index 0000000..bf6a22a --- /dev/null +++ b/src/util/db.ts @@ -0,0 +1,16 @@ +import * as mongoose from "mongoose"; + +import config from "./config"; + +interface IConnections { + [propName: string]: mongoose.Connection; +} + +function createConnection(dbName: string): mongoose.Connection { + return mongoose.createConnection(`${config.database.url}${dbName}`, { useNewUrlParser: true }); +} + +export const connections: IConnections = { + main: createConnection(config.database.mainDatabase), + // event: createConnection(config.database.eventDatabase), TODO: Connect to correct event database given a TBA event key +}; diff --git a/static/img/favicon.ico b/static/img/favicon.ico new file mode 100644 index 0000000..bb7c3bd Binary files /dev/null and b/static/img/favicon.ico differ diff --git a/static/img/first-logo-horizontal.png b/static/img/first-logo-horizontal.png new file mode 100644 index 0000000..c65d9d7 Binary files /dev/null and b/static/img/first-logo-horizontal.png differ diff --git a/static/js/home.js b/static/js/home.js new file mode 100644 index 0000000..369b5c6 --- /dev/null +++ b/static/js/home.js @@ -0,0 +1,18 @@ +new Vue({ + el: '#app', + data: { + password: '', + hasError: false, + }, + methods: { + logout: function(event){ + axios.post("../../api/logout") + .then(res => { + window.location.href = "/dash/login" + }) + .catch(err => { + console.error(err) + }); + } + } +}); diff --git a/static/js/login.js b/static/js/login.js new file mode 100644 index 0000000..6343d06 --- /dev/null +++ b/static/js/login.js @@ -0,0 +1,22 @@ +new Vue({ + el: '#app', + data: { + password: '', + hasError: false, + }, + methods: { + login: function(event){ + axios.post("../../api/login", { + password: this.password, + }) + .then(res => { + this.hasError = false; + + window.location.href = "/dash/home" + }) + .catch(err => { + this.hasError = true; + }); + } + } +}); diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..05ac5be --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "lib": ["es2018"], + "module": "commonjs", + "moduleResolution": "node", + "pretty": true, + "sourceMap": true, + "target": "es2018", + "outDir": "./dist", + "baseUrl": "./src", + "strict": true, + "strictNullChecks": true + }, + "include": [ + "src/**/*.ts" + ], + "exclude": [ + "node_modules", + "dist" + ] +} diff --git a/tslint.json b/tslint.json new file mode 100644 index 0000000..bc1105f --- /dev/null +++ b/tslint.json @@ -0,0 +1,53 @@ +{ + "extends": "tslint:recommended", + "rules": { + "array-type": [true, "generic"], + "arrow-parens": false, + "class-name": true, + "curly": true, + "forin": false, + "indent": [true, "tabs", 4], + "no-namespace": false, + "max-classes-per-file": false, + "max-line-length": [true, 135], + "no-bitwise": false, + "no-console": false, + "no-empty-interface": false, + "no-empty": false, + "no-var-requires": true, + "no-require-imports": true, + "no-string-literal": true, + "no-unnecessary-qualifier": true, + "no-unused-variable": true, + "no-var-keyword": true, + "ordered-imports": false, + "object-literal-sort-keys": false, + "prefer-const": true, + "quotemark": [true, "double", "avoid-escape" ], + "semicolon": [true, "always", "ignore-bound-class-methods"], + "triple-equals": true, + "member-ordering": [true, { + "order": [ + "private-static-field", + "protected-static-field", + "public-static-field", + + "private-instance-field", + "protected-instance-field", + "public-instance-field", + + "private-constructor", + "protected-constructor", + "public-constructor", + + "private-static-method", + "protected-static-method", + "public-static-method", + + "private-instance-method", + "protected-instance-method", + "public-instance-method" + ] + }] + } +} diff --git a/views/pages/404.ejs b/views/pages/404.ejs new file mode 100644 index 0000000..ee14043 --- /dev/null +++ b/views/pages/404.ejs @@ -0,0 +1,18 @@ + + + + <% include ../partials/head %> + + FRC Scout | 404 + + +
+
+
+

Error: 404

+

We couldn't find this page

+
+
+
+ + diff --git a/views/pages/home.ejs b/views/pages/home.ejs new file mode 100644 index 0000000..0260a74 --- /dev/null +++ b/views/pages/home.ejs @@ -0,0 +1,33 @@ + + + + <% include ../partials/head %> + + + + FRC Scout | Dashboard + + +
+ +
+ + diff --git a/views/pages/login.ejs b/views/pages/login.ejs new file mode 100644 index 0000000..111273a --- /dev/null +++ b/views/pages/login.ejs @@ -0,0 +1,50 @@ + + + + <% include ../partials/head %> + + + + FRC Scout | Login + + +
+
+
+

Login

+

FRC Scout 2019

+
+ + +
+
+
+
+ +
+ +

Incorrect password

+
+ +
+
+ +
+
+
+
+
+
+
+ + diff --git a/views/partials/head.ejs b/views/partials/head.ejs new file mode 100644 index 0000000..65fc798 --- /dev/null +++ b/views/partials/head.ejs @@ -0,0 +1,8 @@ + + + + + + + +