diff --git a/.vscode/settings.json b/.vscode/settings.json index 3eb39ee5..74276cc5 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -5,5 +5,6 @@ "this":false, "bad_property":false }, - "search.usePCRE2": true + "search.usePCRE2": true, + "editor.formatOnSave": true } \ No newline at end of file diff --git a/assets/email/Ticket.hbs b/assets/email/Ticket.hbs new file mode 100644 index 00000000..a6ee46bf --- /dev/null +++ b/assets/email/Ticket.hbs @@ -0,0 +1,500 @@ + + + + +
+ +
+
+
|
+
a hacker's unique email
" + "field": "id", + "description": "a hacker's unique mongoID
" }] } }, @@ -1324,12 +1324,12 @@ define({ "filename": "routes/api/hacker.js", "groupTitle": "Hacker", "sampleRequest": [{ - "url": "https://api.mchacks.ca/api/hacker/email/:email" + "url": "https://api.mchacks.ca/api/hacker/:id" }] }, { "type": "get", - "url": "/hacker/:id", + "url": "/hacker/email/:email", "title": "get a hacker's information", "name": "getHacker", "group": "Hacker", @@ -1340,8 +1340,8 @@ define({ "group": "param", "type": "String", "optional": false, - "field": "id", - "description": "a hacker's unique mongoID
" + "field": "email", + "description": "a hacker's unique email
" }] } }, @@ -1396,7 +1396,7 @@ define({ "filename": "routes/api/hacker.js", "groupTitle": "Hacker", "sampleRequest": [{ - "url": "https://api.mchacks.ca/api/hacker/:id" + "url": "https://api.mchacks.ca/api/hacker/email/:email" }] }, { @@ -1869,6 +1869,58 @@ define({ "url": "https://api.mchacks.ca/api/hacker/resume/:id" }] }, + { + "type": "post", + "url": "/hacker/email/weekOf/:id", + "title": "", + "description": "Sends a hacker the week-of email, along with the HackPass QR code to view their hacker profile (for checkin purposes). Hackers must be eitherconfirmed, or checked in.
", + "name": "postHackerSendWeekOfEmail", + "group": "Hacker", + "version": "0.0.9", + "parameter": { + "fields": { + "param": [{ + "group": "param", + "type": "string", + "optional": true, + "field": "status", + "description": "The hacker ID
" + }] + } + }, + "success": { + "fields": { + "Success 200": [{ + "group": "Success 200", + "type": "string", + "optional": false, + "field": "message", + "description": "Success message
" + }, + { + "group": "Success 200", + "type": "object", + "optional": false, + "field": "data", + "description": "empty
" + } + ] + }, + "examples": [{ + "title": "Success-Response: ", + "content": "{\n \"message\": \"Hacker week-of email sent.\", \n \"data\": {}\n}", + "type": "object" + }] + }, + "permission": [{ + "name": "Administrator" + }], + "filename": "routes/api/hacker.js", + "groupTitle": "Hacker", + "sampleRequest": [{ + "url": "https://api.mchacks.ca/api/hacker/email/weekOf/:id" + }] + }, { "type": "get", "url": "/sponsor/self", @@ -2645,7 +2697,7 @@ define({ }, "examples": [{ "title": "Success-Response: ", - "content": "{\n \"message\": \"Successfully retrieved team information\", \n \"data\": {...}\n }", + "content": "{\n \"message\": \"Team retrieval successful\", \n \"data\": { \n \"team\": {\n \"name\":\"foo\",\n \"members\": [\n ObjectId('...')\n ],\n \"devpostURL\": \"www.devpost.com/foo\",\n \"projectName\": \"fooey\"\n },\n \"members\": [\n {\n \"firstName\": \"John\",\n \"lastName\": \"Doe\"\n }\n ],\n }\n }", "type": "object" }] }, diff --git a/docs/api/api_data.json b/docs/api/api_data.json index 06066e0d..9406fb6b 100644 --- a/docs/api/api_data.json +++ b/docs/api/api_data.json @@ -1256,7 +1256,7 @@ }, { "type": "get", - "url": "/hacker/email/:email", + "url": "/hacker/:id", "title": "get a hacker's information", "name": "getHacker", "group": "Hacker", @@ -1267,8 +1267,8 @@ "group": "param", "type": "String", "optional": false, - "field": "email", - "description": "a hacker's unique email
" + "field": "id", + "description": "a hacker's unique mongoID
" }] } }, @@ -1323,12 +1323,12 @@ "filename": "routes/api/hacker.js", "groupTitle": "Hacker", "sampleRequest": [{ - "url": "https://api.mchacks.ca/api/hacker/email/:email" + "url": "https://api.mchacks.ca/api/hacker/:id" }] }, { "type": "get", - "url": "/hacker/:id", + "url": "/hacker/email/:email", "title": "get a hacker's information", "name": "getHacker", "group": "Hacker", @@ -1339,8 +1339,8 @@ "group": "param", "type": "String", "optional": false, - "field": "id", - "description": "a hacker's unique mongoID
" + "field": "email", + "description": "a hacker's unique email
" }] } }, @@ -1395,7 +1395,7 @@ "filename": "routes/api/hacker.js", "groupTitle": "Hacker", "sampleRequest": [{ - "url": "https://api.mchacks.ca/api/hacker/:id" + "url": "https://api.mchacks.ca/api/hacker/email/:email" }] }, { @@ -1868,6 +1868,58 @@ "url": "https://api.mchacks.ca/api/hacker/resume/:id" }] }, + { + "type": "post", + "url": "/hacker/email/weekOf/:id", + "title": "", + "description": "Sends a hacker the week-of email, along with the HackPass QR code to view their hacker profile (for checkin purposes). Hackers must be eitherconfirmed, or checked in.
", + "name": "postHackerSendWeekOfEmail", + "group": "Hacker", + "version": "0.0.9", + "parameter": { + "fields": { + "param": [{ + "group": "param", + "type": "string", + "optional": true, + "field": "status", + "description": "The hacker ID
" + }] + } + }, + "success": { + "fields": { + "Success 200": [{ + "group": "Success 200", + "type": "string", + "optional": false, + "field": "message", + "description": "Success message
" + }, + { + "group": "Success 200", + "type": "object", + "optional": false, + "field": "data", + "description": "empty
" + } + ] + }, + "examples": [{ + "title": "Success-Response: ", + "content": "{\n \"message\": \"Hacker week-of email sent.\", \n \"data\": {}\n}", + "type": "object" + }] + }, + "permission": [{ + "name": "Administrator" + }], + "filename": "routes/api/hacker.js", + "groupTitle": "Hacker", + "sampleRequest": [{ + "url": "https://api.mchacks.ca/api/hacker/email/weekOf/:id" + }] + }, { "type": "get", "url": "/sponsor/self", @@ -2643,7 +2695,7 @@ }, "examples": [{ "title": "Success-Response: ", - "content": "{\n \"message\": \"Successfully retrieved team information\", \n \"data\": {...}\n }", + "content": "{\n \"message\": \"Team retrieval successful\", \n \"data\": { \n \"team\": {\n \"name\":\"foo\",\n \"members\": [\n ObjectId('...')\n ],\n \"devpostURL\": \"www.devpost.com/foo\",\n \"projectName\": \"fooey\"\n },\n \"members\": [\n {\n \"firstName\": \"John\",\n \"lastName\": \"Doe\"\n }\n ],\n }\n }", "type": "object" }] }, diff --git a/docs/api/api_project.js b/docs/api/api_project.js index 278259a4..8730e140 100644 --- a/docs/api/api_project.js +++ b/docs/api/api_project.js @@ -1,16 +1,16 @@ -define({ - "name": "hackerAPI", - "version": "0.0.8", - "description": "Documentation for the API used for mchacks", - "defaultVersion": "0.0.8", - "title": "hackerAPI documentation", - "url": "https://api.mchacks.ca/api", - "sampleUrl": "https://api.mchacks.ca/api", - "apidoc": "0.3.0", - "generator": { - "name": "apidoc", - "time": "2019-01-21T04:13:36.482Z", - "url": "http://apidocjs.com", - "version": "0.17.7" - } +define({ + "name": "hackerAPI", + "version": "0.0.8", + "description": "Documentation for the API used for mchacks", + "defaultVersion": "0.0.8", + "title": "hackerAPI documentation", + "url": "https://api.mchacks.ca/api", + "sampleUrl": "https://api.mchacks.ca/api", + "apidoc": "0.3.0", + "generator": { + "name": "apidoc", + "time": "2019-01-23T23:37:14.991Z", + "url": "http://apidocjs.com", + "version": "0.17.7" + } }); \ No newline at end of file diff --git a/docs/api/api_project.json b/docs/api/api_project.json index 13eedf19..75fa20a2 100644 --- a/docs/api/api_project.json +++ b/docs/api/api_project.json @@ -1,16 +1,16 @@ -{ - "name": "hackerAPI", - "version": "0.0.8", - "description": "Documentation for the API used for mchacks", - "defaultVersion": "0.0.8", - "title": "hackerAPI documentation", - "url": "https://api.mchacks.ca/api", - "sampleUrl": "https://api.mchacks.ca/api", - "apidoc": "0.3.0", - "generator": { - "name": "apidoc", - "time": "2019-01-21T04:13:36.482Z", - "url": "http://apidocjs.com", - "version": "0.17.7" - } +{ + "name": "hackerAPI", + "version": "0.0.8", + "description": "Documentation for the API used for mchacks", + "defaultVersion": "0.0.8", + "title": "hackerAPI documentation", + "url": "https://api.mchacks.ca/api", + "sampleUrl": "https://api.mchacks.ca/api", + "apidoc": "0.3.0", + "generator": { + "name": "apidoc", + "time": "2019-01-23T23:37:14.991Z", + "url": "http://apidocjs.com", + "version": "0.17.7" + } } \ No newline at end of file diff --git a/middlewares/hacker.middleware.js b/middlewares/hacker.middleware.js index 22e982ec..c061fa03 100644 --- a/middlewares/hacker.middleware.js +++ b/middlewares/hacker.middleware.js @@ -7,6 +7,7 @@ const Services = { Storage: require("../services/storage.service"), Email: require("../services/email.service"), Account: require("../services/account.service"), + Env: require("../services/env.service"), }; const Middleware = { Util: require("./util.middleware") @@ -308,6 +309,29 @@ async function sendAppliedStatusEmail(req, res, next) { Services.Email.sendStatusUpdate(account.firstName, account.email, Constants.General.HACKER_STATUS_APPLIED, next); } +/** + * Sends an email telling the user that they have applied. This is used exclusively when we POST a hacker. + * @param {{body: {hacker: {accountId: string}}}} req + * @param {*} res + * @param {(err?:*)=>void} next + */ +async function sendTicketEmail(req, res, next) { + const hacker = req.body.hacker; + const address = Services.Env.isProduction() ? process.env.FRONTEND_ADDRESS_DEPLOY : process.env.FRONTEND_ADDRESS_DEV; + const httpOrHttps = (address.includes("localhost")) ? "http" : "https"; + const singleHackerViewLink = Services.Hacker.generateHackerViewLink(httpOrHttps, address, hacker._id.toString()); + const ticketSVG = await Services.Hacker.generateQRCode(singleHackerViewLink); + const account = await Services.Account.findById(hacker.accountId); + if (!account || !ticketSVG) { + return next({ + status: 500, + message: Constants.Error.GENERIC_500_MESSAGE, + error: {} + }); + } + Services.Email.sendTicketEmail(account.firstName, account.email, ticketSVG, next); +} + /** * If the current hacker's status is Constants.HACKER_STATUS_NONE, and the hacker's application is completed, * then it will change the status of the hacker to Constants.General.HACKER_STATUS_APPLIED, and then email the hacker to @@ -520,6 +544,7 @@ module.exports = { ensureAccountLinkedToHacker: ensureAccountLinkedToHacker, uploadResume: Middleware.Util.asyncMiddleware(uploadResume), downloadResume: Middleware.Util.asyncMiddleware(downloadResume), + sendTicketEmail: Middleware.Util.asyncMiddleware(sendTicketEmail), sendStatusUpdateEmail: Middleware.Util.asyncMiddleware(sendStatusUpdateEmail), sendAppliedStatusEmail: Middleware.Util.asyncMiddleware(sendAppliedStatusEmail), updateHacker: Middleware.Util.asyncMiddleware(updateHacker), diff --git a/middlewares/team.middleware.js b/middlewares/team.middleware.js index 32c8f4af..830866e7 100644 --- a/middlewares/team.middleware.js +++ b/middlewares/team.middleware.js @@ -407,13 +407,14 @@ async function populateMemberAccountsById(req, res, next) { data: {} }); } - + let hackerIds = []; let teamMembers = []; for (const member of team.members) { teamMembers.push(member.accountId); + hackerIds.push(member._id); } - + team.members = hackerIds; req.body.team = team; req.body.teamMembers = teamMembers; return next(); diff --git a/package-lock.json b/package-lock.json index 7a59fd3b..8a6795c3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -160,20 +160,20 @@ "integrity": "sha512-QzB0/IMvB0eFxFK7Eqh+bfC8NLv3E9ScjWQrPOk6GgfNroxcVITdTlT8NRsRrcp5+QQJVPLkRqKG0PUdaWXmHw==" }, "@google-cloud/storage": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@google-cloud/storage/-/storage-2.3.1.tgz", - "integrity": "sha512-AXfze/I1J6NNk9yuyuKgYL2E1qxS7nvDGFvwitUyAb0FgDEA5UpXiKbDWouu4mwqVMYWl7HXlQiLUSkYNXMB1w==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@google-cloud/storage/-/storage-2.4.1.tgz", + "integrity": "sha512-g9GQ/CWkB2Y1wyMnlWvj7dGbChIxfuran0O6kUtvqTKTLBpZpsvWo86xQV723JMXi/BenLkE1vy353bnRsa3Fw==", "requires": { - "@google-cloud/common": "^0.26.0", + "@google-cloud/common": "^0.30.2", "@google-cloud/paginator": "^0.1.0", "@google-cloud/promisify": "^0.3.0", "arrify": "^1.0.0", "async": "^2.0.1", "compressible": "^2.0.12", - "concat-stream": "^1.5.0", + "concat-stream": "^2.0.0", "duplexify": "^3.5.0", "extend": "^3.0.0", - "gcs-resumable-upload": "^0.13.0", + "gcs-resumable-upload": "^0.14.1", "hash-stream-validation": "^0.2.1", "mime": "^2.2.0", "mime-types": "^2.0.8", @@ -181,15 +181,15 @@ "pumpify": "^1.5.1", "snakeize": "^0.1.0", "stream-events": "^1.0.1", - "teeny-request": "^3.11.0", + "teeny-request": "^3.11.3", "through2": "^3.0.0", "xdg-basedir": "^3.0.0" }, "dependencies": { "@google-cloud/common": { - "version": "0.26.2", - "resolved": "https://registry.npmjs.org/@google-cloud/common/-/common-0.26.2.tgz", - "integrity": "sha512-xJ2M/q3MrUbnYZuFlpF01caAlEhAUoRn0NXp93Hn3pkFpfSOG8YfbKbpBAHvcKVbBOAKVIwPsleNtuyuabUwLQ==", + "version": "0.30.2", + "resolved": "https://registry.npmjs.org/@google-cloud/common/-/common-0.30.2.tgz", + "integrity": "sha512-JLIaSpMhhUetiwS30iC2SwNP51mayuSbyhRVdagqFC6rDVcqMHHsN8QJwUhCjOoHyfQ04n9SinfMCQzJTR9Liw==", "requires": { "@google-cloud/projectify": "^0.3.2", "@google-cloud/promisify": "^0.3.0", @@ -199,77 +199,108 @@ "duplexify": "^3.6.0", "ent": "^2.2.0", "extend": "^3.0.1", - "google-auth-library": "^2.0.0", + "google-auth-library": "^3.0.0", "pify": "^4.0.0", - "retry-request": "^4.0.0", - "through2": "^3.0.0" + "retry-request": "^4.0.0" + } + }, + "concat-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", + "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.0.2", + "typedarray": "^0.0.6" }, "dependencies": { - "retry-request": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/retry-request/-/retry-request-4.0.0.tgz", - "integrity": "sha512-S4HNLaWcMP6r8E4TMH52Y7/pM8uNayOcTDDQNBwsCccL1uI+Ol2TljxRDPzaNfbhOB30+XWP5NnZkB3LiJxi1w==", + "readable-stream": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.1.1.tgz", + "integrity": "sha512-DkN66hPyqDhnIQ6Jcsvx9bFjhw214O4poMBcIMgPVpQvNy9a0e0Uhg5SqySyDKAmUlwt8LonTBz1ezOnM8pUdA==", "requires": { - "through2": "^2.0.0" - }, - "dependencies": { - "through2": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", - "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", - "requires": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" - } - } + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" } } } }, "gcp-metadata": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-0.7.0.tgz", - "integrity": "sha512-ffjC09amcDWjh3VZdkDngIo7WoluyC5Ag9PAYxZbmQLOLNI8lvPtoKTSCyU54j2gwy5roZh6sSMTfkY2ct7K3g==", + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-0.9.3.tgz", + "integrity": "sha512-caV4S84xAjENtpezLCT/GILEAF5h/bC4cNqZFmt/tjTn8t+JBtTkQrgBrJu3857YdsnlM8rxX/PMcKGtE8hUlw==", "requires": { - "axios": "^0.18.0", - "extend": "^3.0.1", - "retry-axios": "0.3.2" + "gaxios": "^1.0.2", + "json-bigint": "^0.3.0" } }, "google-auth-library": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-2.0.1.tgz", - "integrity": "sha512-CWLKZxqYw4SE+fE3GWbVT9r/10h75w8lB3cdmmLpLtCfccFDcsI84qI5rx7npemlrHtKJh3C2HUz4s6SihCeIQ==", - "requires": { - "axios": "^0.18.0", - "gcp-metadata": "^0.7.0", - "gtoken": "^2.3.0", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-3.0.1.tgz", + "integrity": "sha512-ZGTBMiQga/pwEw26ZKCn+q9PTPXvE4v5sL2V9HV3f2Gt0lrS+2H7XgbVCx850jrvlEL59JIheFiDqEn9CIa0nA==", + "requires": { + "base64-js": "^1.3.0", + "fast-text-encoding": "^1.0.0", + "gaxios": "^1.2.1", + "gcp-metadata": "^0.9.3", + "gtoken": "^2.3.2", "https-proxy-agent": "^2.2.1", "jws": "^3.1.5", - "lodash.isstring": "^4.0.1", - "lru-cache": "^4.1.3", + "lru-cache": "^5.0.0", "semver": "^5.5.0" } }, + "gtoken": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-2.3.2.tgz", + "integrity": "sha512-F8EObUGyC8Qd3WXTloNULZBwfUsOABoHElihB1F6zGhT/cy38iPL09wGLRY712I+hQnOyA+sYlgPFX2cOKz0qg==", + "requires": { + "gaxios": "^1.0.4", + "google-p12-pem": "^1.0.0", + "jws": "^3.1.5", + "mime": "^2.2.0", + "pify": "^4.0.0" + } + }, "lru-cache": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.3.tgz", - "integrity": "sha512-fFEhvcgzuIoJVUF8fYr5KR0YqxD238zgObTps31YdADwPPAp82a4M8TrckkWyx7ekNlf9aBcVn81cFwwXngrJA==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", "requires": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" + "yallist": "^3.0.2" } }, "mime": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.3.1.tgz", - "integrity": "sha512-OEUllcVoydBHGN1z84yfQDimn58pZNNNXgZlHXSboxMlFvgI6MXSWpWKpFRra7H1HxpVhHTkrghfRW49k6yjeg==" + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.0.tgz", + "integrity": "sha512-ikBcWwyqXQSHKtciCcctu9YfPbFYZ4+gbHEmE0Q8jzcTYQg5dHCr3g2wwAZjPoJfQVXZq6KXAjpXOTf5/cjT7w==" }, "pify": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==" }, + "retry-request": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/retry-request/-/retry-request-4.0.0.tgz", + "integrity": "sha512-S4HNLaWcMP6r8E4TMH52Y7/pM8uNayOcTDDQNBwsCccL1uI+Ol2TljxRDPzaNfbhOB30+XWP5NnZkB3LiJxi1w==", + "requires": { + "through2": "^2.0.0" + }, + "dependencies": { + "through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "requires": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + } + } + }, "through2": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.0.tgz", @@ -278,6 +309,11 @@ "readable-stream": "2 || 3", "xtend": "~4.0.1" } + }, + "yallist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", + "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==" } } }, @@ -462,9 +498,9 @@ "dev": true }, "@types/express": { - "version": "4.11.1", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.11.1.tgz", - "integrity": "sha512-ttWle8cnPA5rAelauSWeWJimtY2RsUf2aspYZs7xPHiWgOlPn6nnUfBMtrkcnjFJuIHJF4gNOdVvpLK2Zmvh6g==", + "version": "4.16.1", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.16.1.tgz", + "integrity": "sha512-V0clmJow23WeyblmACoxbHBu2JKlE5TiIme6Lem14FnPW9gsttyHtk6wq7njcdIWH1njAaFgR8gW09lgY98gQg==", "dev": true, "requires": { "@types/body-parser": "*", @@ -473,13 +509,14 @@ } }, "@types/express-serve-static-core": { - "version": "4.11.2", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.11.2.tgz", - "integrity": "sha512-5ukJmirhZqJh/jEDFn40GANZYtO95C7Pu3Xd9s8hHCtGhZORDVXiFtKLHKDE/s8T72Uvy4BZSTqsgFQMWGg/RA==", + "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/node": "*", + "@types/range-parser": "*" } }, "@types/form-data": { @@ -522,9 +559,9 @@ } }, "@types/mongoose": { - "version": "5.3.8", - "resolved": "https://registry.npmjs.org/@types/mongoose/-/mongoose-5.3.8.tgz", - "integrity": "sha512-JYqTaP15pt03e7z4Hbzn14RiUwQMia3mSJSyrTfR59x+7n7WeX1v2G+k+xO2AOYPbVdq3X9W3nVGTNTO0FYkbg==", + "version": "5.3.10", + "resolved": "https://registry.npmjs.org/@types/mongoose/-/mongoose-5.3.10.tgz", + "integrity": "sha512-Sl5zbzBtjBMdEldbfiZEuF6p1FQZ+7R9TaFYROpeQzBKW9ZK/Xf0Y+E2mtWSNVM3mMjM/Hzw+20K+j/WV6TenQ==", "dev": true, "requires": { "@types/mongodb": "*", @@ -550,6 +587,12 @@ "resolved": "https://registry.npmjs.org/@types/proxyquire/-/proxyquire-1.3.28.tgz", "integrity": "sha512-SQaNzWQ2YZSr7FqAyPPiA3FYpux2Lqh3HWMZQk47x3xbMCqgC/w0dY3dw9rGqlweDDkrySQBcaScXWeR+Yb11Q==" }, + "@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/request": { "version": "2.47.1", "resolved": "https://registry.npmjs.org/@types/request/-/request-2.47.1.tgz", @@ -959,6 +1002,11 @@ } } }, + "base64-js": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz", + "integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw==" + }, "bcrypt": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-3.0.0.tgz", @@ -1398,6 +1446,11 @@ "tweetnacl": "^0.14.3" } }, + "bignumber.js": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-7.2.1.tgz", + "integrity": "sha512-S4XzBk5sMB+Rcb/LNcpzXr57VRTxgAvaAEDAl1AwRx27j00hT84O6OkteE7u8UB3NuaaygCRrEpqox4uDOrbdQ==" + }, "binary-extensions": { "version": "1.11.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.11.0.tgz", @@ -1678,6 +1731,14 @@ } } }, + "can-promise": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/can-promise/-/can-promise-0.0.1.tgz", + "integrity": "sha512-gzVrHyyrvgt0YpDm7pn04MQt8gjh0ZAhN4ZDyCRtGl6YnuuK6b4aiUTD7G52r9l4YNmxfTtEscb92vxtAlL6XQ==", + "requires": { + "window-or-global": "^1.0.1" + } + }, "capture-stack-trace": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/capture-stack-trace/-/capture-stack-trace-1.0.0.tgz", @@ -2366,6 +2427,11 @@ "resolved": "https://registry.npmjs.org/diff-match-patch/-/diff-match-patch-1.0.4.tgz", "integrity": "sha512-Uv3SW8bmH9nAtHKaKSanOQmj2DnlH65fUpcrMdfdaOxUG02QQ4YGZ8AE7kKOMisF7UqvOlGKVYWRvezdncW9lg==" }, + "dijkstrajs": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.1.tgz", + "integrity": "sha1-082BIh4+pAdCz83lVtTpnpjdxxs=" + }, "dir-glob": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-2.0.0.tgz", @@ -2545,9 +2611,9 @@ } }, "es6-promise": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.4.tgz", - "integrity": "sha512-/NdNZVJg+uZgtm9eS3O6lrOLYmQag2DjdEXuPaHlZ6RuVqgqaVZfgYCepEIKsLqwdQArOPtC3XzRLqGGfT8KQQ==" + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.5.tgz", + "integrity": "sha512-n6wvpdE43VFtJq+lUDYDBFUwV8TZbuGXLV4D6wKafg13ldznKsyEvatubnmUe31zcvelSzOHF+XbaT+Bl9ObDg==" }, "es6-promisify": { "version": "5.0.0", @@ -2857,6 +2923,11 @@ "integrity": "sha512-q8BZ89jjc+mz08rSxROs8VsrBBcn1SIw1kq9NjolL509tkABRk9io01RAjSaEv1Xb2uFLt8VtRiZbGp5H8iDtg==", "dev": true }, + "fast-text-encoding": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fast-text-encoding/-/fast-text-encoding-1.0.0.tgz", + "integrity": "sha512-R9bHCvweUxxwkDwhjav5vxpFvdPGlVngtqmx4pIZfSUhM/Q4NiIUHB456BAf+Q1Nwu3HEZYONtu+Rya+af4jiQ==" + }, "fecha": { "version": "2.3.3", "resolved": "http://registry.npmjs.org/fecha/-/fecha-2.3.3.tgz", @@ -3560,6 +3631,23 @@ } } }, + "gaxios": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-1.2.6.tgz", + "integrity": "sha512-A7IVK12d5SavNAGTtL5aBDJ6auqWDCbyMazX+QQIklMdashrIZs4QIm1a6TpenJYy0OskCks2sMqglGt6ZThEQ==", + "requires": { + "extend": "^3.0.2", + "https-proxy-agent": "^2.2.1", + "node-fetch": "^2.2.0" + }, + "dependencies": { + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + } + } + }, "gcp-metadata": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-0.6.3.tgz", @@ -3571,22 +3659,21 @@ } }, "gcs-resumable-upload": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/gcs-resumable-upload/-/gcs-resumable-upload-0.13.0.tgz", - "integrity": "sha512-hrSYPFJWyx8FDLJEK3XeqbNcCjkRqcuKSaUxL1RpwEAWAxtV+AdUH+NX3n7st/U6/JddQkdb1mmWAy3jgRDflw==", + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/gcs-resumable-upload/-/gcs-resumable-upload-0.14.1.tgz", + "integrity": "sha512-vkIxLeVyW20DdcyhI8GvOkISV62y7+fKAdelUTn8F5en8AmPduqro5xz3VoHkj/RJ3PQmqNovYYaYPyPHwebzw==", "requires": { - "axios": "^0.18.0", "configstore": "^4.0.0", - "google-auth-library": "^2.0.0", + "google-auth-library": "^3.0.0", "pumpify": "^1.5.1", "request": "^2.87.0", "stream-events": "^1.0.4" }, "dependencies": { "ajv": { - "version": "6.5.5", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.5.tgz", - "integrity": "sha512-7q7gtRQDJSyuEHjuVgHoUa2VuemFiCMrfQc9Tc08XTAc4Zj/5U1buQJ0HU6i7fKjXU09SVgSmxa4sLvuvS8Iyg==", + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.7.0.tgz", + "integrity": "sha512-RZXPviBTtfmtka9n9sy1N5M5b82CbxWIR6HIis4s3WQTXDJamc/0gpCWNGz6EWdWp4DOfjzJfhz/AS9zVPjjWg==", "requires": { "fast-deep-equal": "^2.0.1", "fast-json-stable-stringify": "^2.0.0", @@ -3612,36 +3699,53 @@ "xdg-basedir": "^3.0.0" } }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, "fast-deep-equal": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=" }, "gcp-metadata": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-0.7.0.tgz", - "integrity": "sha512-ffjC09amcDWjh3VZdkDngIo7WoluyC5Ag9PAYxZbmQLOLNI8lvPtoKTSCyU54j2gwy5roZh6sSMTfkY2ct7K3g==", + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-0.9.3.tgz", + "integrity": "sha512-caV4S84xAjENtpezLCT/GILEAF5h/bC4cNqZFmt/tjTn8t+JBtTkQrgBrJu3857YdsnlM8rxX/PMcKGtE8hUlw==", "requires": { - "axios": "^0.18.0", - "extend": "^3.0.1", - "retry-axios": "0.3.2" + "gaxios": "^1.0.2", + "json-bigint": "^0.3.0" } }, "google-auth-library": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-2.0.1.tgz", - "integrity": "sha512-CWLKZxqYw4SE+fE3GWbVT9r/10h75w8lB3cdmmLpLtCfccFDcsI84qI5rx7npemlrHtKJh3C2HUz4s6SihCeIQ==", - "requires": { - "axios": "^0.18.0", - "gcp-metadata": "^0.7.0", - "gtoken": "^2.3.0", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-3.0.1.tgz", + "integrity": "sha512-ZGTBMiQga/pwEw26ZKCn+q9PTPXvE4v5sL2V9HV3f2Gt0lrS+2H7XgbVCx850jrvlEL59JIheFiDqEn9CIa0nA==", + "requires": { + "base64-js": "^1.3.0", + "fast-text-encoding": "^1.0.0", + "gaxios": "^1.2.1", + "gcp-metadata": "^0.9.3", + "gtoken": "^2.3.2", "https-proxy-agent": "^2.2.1", "jws": "^3.1.5", - "lodash.isstring": "^4.0.1", - "lru-cache": "^4.1.3", + "lru-cache": "^5.0.0", "semver": "^5.5.0" } }, + "gtoken": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-2.3.2.tgz", + "integrity": "sha512-F8EObUGyC8Qd3WXTloNULZBwfUsOABoHElihB1F6zGhT/cy38iPL09wGLRY712I+hQnOyA+sYlgPFX2cOKz0qg==", + "requires": { + "gaxios": "^1.0.4", + "google-p12-pem": "^1.0.0", + "jws": "^3.1.5", + "mime": "^2.2.0", + "pify": "^4.0.0" + } + }, "har-validator": { "version": "5.1.3", "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", @@ -3657,14 +3761,18 @@ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" }, "lru-cache": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.3.tgz", - "integrity": "sha512-fFEhvcgzuIoJVUF8fYr5KR0YqxD238zgObTps31YdADwPPAp82a4M8TrckkWyx7ekNlf9aBcVn81cFwwXngrJA==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", "requires": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" + "yallist": "^3.0.2" } }, + "mime": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.0.tgz", + "integrity": "sha512-ikBcWwyqXQSHKtciCcctu9YfPbFYZ4+gbHEmE0Q8jzcTYQg5dHCr3g2wwAZjPoJfQVXZq6KXAjpXOTf5/cjT7w==" + }, "mime-db": { "version": "1.37.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.37.0.tgz", @@ -3683,6 +3791,11 @@ "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==" + }, "qs": { "version": "6.5.2", "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", @@ -3713,13 +3826,6 @@ "tough-cookie": "~2.4.3", "tunnel-agent": "^0.6.0", "uuid": "^3.3.2" - }, - "dependencies": { - "extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" - } } }, "safe-buffer": { @@ -3740,9 +3846,19 @@ "version": "3.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" + }, + "yallist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", + "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==" } } }, + "get-caller-file": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==" + }, "get-func-name": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", @@ -4905,8 +5021,7 @@ "is-stream": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", - "dev": true + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" }, "is-stream-ended": { "version": "0.1.4", @@ -4936,8 +5051,7 @@ "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" }, "isobject": { "version": "3.0.1", @@ -4980,24 +5094,39 @@ } }, "jslint": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/jslint/-/jslint-0.12.0.tgz", - "integrity": "sha512-RoCsyICcKA+6TFsbys9DpKTfPVaC71Mm5QSjvrWA0lDVN+LIvx6apa42FFisMqmCTvJ8DxkcoQGJ0j7m3kTVow==", + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/jslint/-/jslint-0.12.1.tgz", + "integrity": "sha512-q5iHswjOmJffbsGVq/1umGh4YBxb5pCArNHCZeHpkuVDDKM6IldqUn4hLehKSwQr7Bn07VXjD34Lx3nw+6j8eA==", "dev": true, "requires": { "exit": "~0.1.2", - "glob": "~7.1.2", - "nopt": "~3.0.1", + "glob": "~7.1.3", + "nopt": "~4.0.1", "readable-stream": "~2.1.5" }, "dependencies": { + "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" + } + }, "nopt": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", - "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", + "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", "dev": true, "requires": { - "abbrev": "1" + "abbrev": "1", + "osenv": "^0.1.4" } }, "process-nextick-args": { @@ -5029,6 +5158,14 @@ } } }, + "json-bigint": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-0.3.0.tgz", + "integrity": "sha1-DM2RLEuCcNBfBW+9E4FLU9OCWx4=", + "requires": { + "bignumber.js": "^7.0.0" + } + }, "json-schema": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", @@ -5186,7 +5323,7 @@ }, "load-json-file": { "version": "1.1.0", - "resolved": "http://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", "requires": { "graceful-fs": "^4.1.2", @@ -5203,6 +5340,22 @@ } } }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "dependencies": { + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" + } + } + }, "lodash": { "version": "4.17.10", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz", @@ -5323,6 +5476,14 @@ "pify": "^3.0.0" } }, + "map-age-cleaner": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", + "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", + "requires": { + "p-defer": "^1.0.0" + } + }, "map-cache": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", @@ -5379,6 +5540,16 @@ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" }, + "mem": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mem/-/mem-4.0.0.tgz", + "integrity": "sha512-WQxG/5xYc3tMbYLXoXPm81ET2WDULiU5FxbuIoNbJqLOOI8zehXFdZuiUEgfdrU2mVB1pxBZUGlYORSrpuJreA==", + "requires": { + "map-age-cleaner": "^0.1.1", + "mimic-fn": "^1.0.0", + "p-is-promise": "^1.1.0" + } + }, "memory-cache": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/memory-cache/-/memory-cache-0.2.0.tgz", @@ -5477,6 +5648,11 @@ "mime-db": "~1.33.0" } }, + "mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==" + }, "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", @@ -5587,9 +5763,9 @@ } }, "mongoose": { - "version": "5.4.5", - "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-5.4.5.tgz", - "integrity": "sha512-TO1xFVyZHALSaN3EEXY5rhpKNYDF0SQxK2TlYbCOGD/rurv9c9PQiNV6xcDZ09FPIoj/cOy4xDJnt3LnthTv5A==", + "version": "5.4.8", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-5.4.8.tgz", + "integrity": "sha512-2wgBi1LBxL/pcG0nNP8zXBLRmWZFzg8pb2MH4JkMPIxP/HGtQQEP0DbG1Yzy+Igsp3M1+V/59uXW37O2Fg5mBQ==", "requires": { "async": "2.6.1", "bson": "~1.1.0", @@ -5718,6 +5894,11 @@ "inherits": "~2.0.1" } }, + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" + }, "node-fetch": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.3.0.tgz", @@ -5790,7 +5971,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", - "dev": true, "requires": { "path-key": "^2.0.0" } @@ -5900,6 +6080,12 @@ "resolved": "https://registry.npmjs.org/optjs/-/optjs-3.2.2.tgz", "integrity": "sha1-aabOicRCpEQDFBrS+bNwvVu29O4=" }, + "os-homedir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", + "dev": true + }, "os-locale": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", @@ -5913,11 +6099,51 @@ "resolved": "http://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" }, + "osenv": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", + "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", + "dev": true, + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, + "p-defer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", + "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=" + }, "p-finally": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", - "dev": true + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" + }, + "p-is-promise": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-1.1.0.tgz", + "integrity": "sha1-nJRWmJ6fZYgBewQ01WCXZ1w9oF4=" + }, + "p-limit": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.1.0.tgz", + "integrity": "sha512-NhURkNcrVB+8hNfLuysU8enY5xn2KXphsHBaC2YmRNTZRc7RWusw6apSpdEj3jo4CMb6W9nrF6tTnsJsJeyu6g==", + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.0.0.tgz", + "integrity": "sha512-hMp0onDKIajHfIkdRk3P4CdCmErkYAxxDtP3Wx/4nZ3aGlau2VKh3mZpcuFkH27WQkL/3WBCPOktzA9ZOAnMQQ==" }, "package-json": { "version": "4.0.1", @@ -5998,8 +6224,7 @@ "path-key": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", - "dev": true + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=" }, "path-to-regexp": { "version": "0.1.7", @@ -6057,6 +6282,11 @@ "pinkie": "^2.0.0" } }, + "pngjs": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-3.3.3.tgz", + "integrity": "sha512-1n3Z4p3IOxArEs1VRXnZ/RXdfEniAUS9jb68g58FIXMNkPJeZd+Qh4Uq7/e0LVxAQGos1eIUrqrt4FpjdnEd+Q==" + }, "posix-character-classes": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", @@ -6238,9 +6468,9 @@ "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" }, "psl": { - "version": "1.1.29", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.29.tgz", - "integrity": "sha512-AeUmQ0oLN02flVHXWh9sSJF7mcdFq0ppid/JkErufc3hGIV/AMa8Fo9VgDo/cT2jFdOWoFvHp90qqBH54W+gjQ==" + "version": "1.1.31", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.31.tgz", + "integrity": "sha512-/6pt4+C+T+wZUieKR620OpzN/LlnNKuWjy1iFLQ/UG35JqHlR/89MP1d96dUfkf6Dne3TuLQzOYEYshJ+Hx8mw==" }, "pstree.remy": { "version": "1.1.0", @@ -6280,6 +6510,155 @@ "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=" }, + "qrcode": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.3.3.tgz", + "integrity": "sha512-SH7V13AcJusH3GT8bMNOGz4w0L+LjcpNOU/NiOgtBhT/5DoWeZE6D5ntMJnJ84AMkoaM4kjJJoHoh9g++8lWFg==", + "requires": { + "can-promise": "0.0.1", + "dijkstrajs": "^1.0.1", + "isarray": "^2.0.1", + "pngjs": "^3.3.0", + "yargs": "^12.0.5" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" + }, + "cliui": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", + "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", + "requires": { + "string-width": "^2.1.1", + "strip-ansi": "^4.0.0", + "wrap-ansi": "^2.0.0" + } + }, + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "requires": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "requires": { + "locate-path": "^3.0.0" + } + }, + "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" + } + }, + "invert-kv": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", + "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==" + }, + "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=" + }, + "isarray": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.4.tgz", + "integrity": "sha512-GMxXOiUirWg1xTKRipM0Ek07rX+ubx4nNVElTJdNLYmNO/2YrDkgJGw9CljXn+r4EWiDQg/8lsRdHyg2PJuUaA==" + }, + "lcid": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", + "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", + "requires": { + "invert-kv": "^2.0.0" + } + }, + "os-locale": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", + "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", + "requires": { + "execa": "^1.0.0", + "lcid": "^2.0.0", + "mem": "^4.0.0" + } + }, + "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" + } + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "requires": { + "ansi-regex": "^3.0.0" + } + }, + "yargs": { + "version": "12.0.5", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz", + "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==", + "requires": { + "cliui": "^4.0.0", + "decamelize": "^1.2.0", + "find-up": "^3.0.0", + "get-caller-file": "^1.0.1", + "os-locale": "^3.0.0", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^2.0.0", + "which-module": "^2.0.0", + "y18n": "^3.2.1 || ^4.0.0", + "yargs-parser": "^11.1.1" + } + } + } + }, "qs": { "version": "6.5.1", "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", @@ -6501,6 +6880,16 @@ "uuid": "^3.1.0" } }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" + }, + "require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=" + }, "require_optional": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/require_optional/-/require_optional-1.0.1.tgz", @@ -6624,6 +7013,11 @@ "send": "0.16.2" } }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" + }, "set-immediate-shim": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", @@ -6660,7 +7054,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", - "dev": true, "requires": { "shebang-regex": "^1.0.0" } @@ -6668,8 +7061,7 @@ "shebang-regex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", - "dev": true + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" }, "shelljs": { "version": "0.3.0", @@ -7055,8 +7447,7 @@ "strip-eof": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", - "dev": true + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=" }, "strip-indent": { "version": "1.0.1", @@ -7121,9 +7512,9 @@ } }, "teeny-request": { - "version": "3.11.0", - "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-3.11.0.tgz", - "integrity": "sha512-bBULmB5Lk2RaEdz2MJMl4+T5+IHSZojDV9ZrxcIv5q1xJvHwCqhfS5nE3JR1D+a4cl6Anhj61CHNuiTM6SRnyg==", + "version": "3.11.3", + "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-3.11.3.tgz", + "integrity": "sha512-CKncqSF7sH6p4rzCgkb/z/Pcos5efl0DmolzvlqRQUNcpRIruOhY9+T1FsIlyEbfWd7MsFpodROOwHYh2BaXzw==", "requires": { "https-proxy-agent": "^2.2.1", "node-fetch": "^2.2.0", @@ -7596,11 +7987,15 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/which/-/which-1.3.0.tgz", "integrity": "sha512-xcJpopdamTuY5duC/KnTTNBraPK54YwpenP4lzxU8H91GudWpFv38u0CKjclE1Wi2EH2EDz5LRcHcKbCIzqGyg==", - "dev": true, "requires": { "isexe": "^2.0.0" } }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=" + }, "widest-line": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-2.0.0.tgz", @@ -7643,6 +8038,11 @@ } } }, + "window-or-global": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/window-or-global/-/window-or-global-1.0.1.tgz", + "integrity": "sha1-2+RboqKRqrxW1iz2bEW3+jIpRt4=" + }, "window-size": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.4.tgz", @@ -7740,6 +8140,22 @@ "window-size": "^0.1.4", "y18n": "^3.2.0" } + }, + "yargs-parser": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz", + "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==", + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "dependencies": { + "camelcase": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.0.0.tgz", + "integrity": "sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA==" + } + } } } -} \ No newline at end of file +} diff --git a/package.json b/package.json index 42ccd8d3..5b315bdf 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ }, "dependencies": { "@google-cloud/logging-winston": "^0.9.0", - "@google-cloud/storage": "^2.3.0", + "@google-cloud/storage": "^2.4.1", "@sendgrid/mail": "^6.3.1", "bcrypt": "^3.0.0", "cookie-parser": "~1.4.3", @@ -27,25 +27,26 @@ "handlebars": "^4.0.12", "jsonwebtoken": "^8.1.0", "memory-cache": "^0.2.0", - "mongoose": "^5.4.5", + "mongoose": "^5.4.8", "multer": "^1.3.1", "passport": "^0.4.0", "passport-local": "^1.0.0", "q": "^1.5.1", + "qrcode": "^1.3.3", "winston": "^2.4.4" }, "devDependencies": { - "@types/express": "^4.11.1", + "@types/express": "^4.16.1", "@types/google-cloud__storage": "^1.7.2", "@types/mongodb": "^3.1.19", - "@types/mongoose": "^5.3.8", + "@types/mongoose": "^5.3.10", "@types/multer": "^1.3.7", "apidoc": "^0.17.7", "chai-http": "^4.2.1", "chai": "^4.2.0", "jshint": "^2.9.7", - "jslint": "^0.12.0", + "jslint": "^0.12.1", "mocha": "^5.2.0", "nodemon": "^1.17.3" } -} \ No newline at end of file +} diff --git a/routes/api/hacker.js b/routes/api/hacker.js index d7f690d8..e714e0eb 100644 --- a/routes/api/hacker.js +++ b/routes/api/hacker.js @@ -585,6 +585,36 @@ module.exports = { Middleware.Hacker.sendStatusUpdateEmail, Controllers.Hacker.updatedHacker ); + + /** + * @api {post} /hacker/email/weekOf/:id + * @apiDescription Sends a hacker the week-of email, along with the HackPass QR code to view their hacker profile (for checkin purposes). Hackers must be eitherconfirmed, or checked in. + * @apiName postHackerSendWeekOfEmail + * @apiGroup Hacker + * @apiVersion 0.0.9 + * + * @apiParam (param) {string} [status] The hacker ID + * @apiSuccess {string} message Success message + * @apiSuccess {object} data empty + * @apiSuccessExample {object} Success-Response: + * { + * "message": "Hacker week-of email sent.", + * "data": {} + * } + * @apiPermission Administrator + */ + hackerRouter.route("/email/weekOf/:id").post( + Middleware.Auth.ensureAuthenticated(), + Middleware.Auth.ensureAuthorized([Services.Hacker.findById]), + + Middleware.Validator.RouteParam.idValidator, + Middleware.parseBody.middleware, + Middleware.Hacker.findById, + Middleware.Hacker.checkStatus([CONSTANTS.HACKER_STATUS_CONFIRMED, CONSTANTS.HACKER_STATUS_CHECKED_IN]), + Middleware.Hacker.sendTicketEmail, + Controllers.Hacker.sentWeekOfEmail + ) + apiRouter.use("/hacker", hackerRouter); } }; \ No newline at end of file diff --git a/routes/api/team.js b/routes/api/team.js index 41b1098e..1a0b872f 100644 --- a/routes/api/team.js +++ b/routes/api/team.js @@ -122,8 +122,23 @@ module.exports = { * @apiSuccess {Object} data Team object * @apiSuccessExample {object} Success-Response: * { - "message": "Successfully retrieved team information", - "data": {...} + "message": "Team retrieval successful", + "data": { + "team": { + "name":"foo", + "members": [ + ObjectId('...') + ], + "devpostURL": "www.devpost.com/foo", + "projectName": "fooey" + }, + "members": [ + { + "firstName": "John", + "lastName": "Doe" + } + ], + } } * @apiError {String} message Error message diff --git a/scripts/batch_update.py b/scripts/batch_update.py new file mode 100644 index 00000000..c6586cce --- /dev/null +++ b/scripts/batch_update.py @@ -0,0 +1,74 @@ +import requests +import json +import getpass + +print("Enter target API") +DEFAULT_API = 'https://api.mchacks.ca' + +API_URL = input('Target API (Default '+DEFAULT_API+'):') + +if API_URL == '': + API_URL = DEFAULT_API + +# Get credentials +print("Enter credentials for", API_URL) +username = input("Email: ") + +s = requests.Session() + +logged_in = False + +while not logged_in: + password = getpass.getpass("Password: ") + CREDENTIALS = { + 'email': username, + 'password': password + } + r = s.post('{0}/api/auth/login'.format(API_URL), CREDENTIALS) + + if r.status_code != 200: + print("Incorrect password, please try again.") + else: + print('Logged in as ', username) + logged_in = True + +# Get information about batch actions + +ACCEPT_ID_FILE = input("Path to file with hackerIDs:") + +VALID_ACTIONS = ['Applied', 'Accepted', 'Waitlisted', + 'Confirmed', 'Cancelled', 'Checked-in'] + +INITIAL_STATUS = input( + "Inital status required " + str(VALID_ACTIONS) + ":") + +NEW_STATUS = input( + "Status to update to " + str(VALID_ACTIONS) + ":") + + +hackerIds = [] +with open(ACCEPT_ID_FILE, 'r') as f: + rows = f.readlines() + for r in rows: + hackerIds.append(r.strip()) + +# remove duplicates +hackerIds = list(set(hackerIds)) + +for index, ID in enumerate(hackerIds): + # so that we aren't 0-based index + index = index + 1 + r1 = s.get('{0}/api/hacker/{1}'.format(API_URL, ID)) + if r1.status_code != 200: + print('({0}/{1}) ERROR cannot get {2}'.format(index, len(hackerIds), ID)) + else: + hackerInfo = json.loads(r1.content) + if hackerInfo['data']['status'] == INITIAL_STATUS: + r2 = s.patch('{0}/api/hacker/status/{1}'.format(API_URL, ID), + {"status": NEW_STATUS}) + if r2.status_code != 200: + print( + '({0}/{1}) ERROR cannot update status for {2}'.format(index, len(hackerIds), ID)) + else: + print( + '({0}/{1}) {2} {3}'.format(index, len(hackerIds), NEW_STATUS, ID)) diff --git a/services/email.service.js b/services/email.service.js index f0a8ed55..ccde0da8 100644 --- a/services/email.service.js +++ b/services/email.service.js @@ -51,6 +51,35 @@ class EmailService { } }); } + /** + * Send email with ticket. + * @param {string} firstName the recipient's first name + * @param {string} recipient the recipient's email address + * @param {string} ticket the ticket image (must be base-64 string) + * @param {(err?)=>void} callback + */ + sendTicketEmail(firstName, recipient, ticket, callback) { + const handlebarsPath = path.join(__dirname, `../assets/email/Ticket.hbs`); + const html = this.renderEmail(handlebarsPath, { + firstName: firstName, + ticket: ticket + }); + const mailData = { + to: recipient, + from: process.env.NO_REPLY_EMAIL, + subject: Constants.EMAIL_SUBJECTS[Constants.WEEK_OF], + html: html + }; + this.send(mailData).then( + (response) => { + if (response[0].statusCode >= 200 && response[0].statusCode < 300) { + callback(); + } else { + callback(response[0]); + } + }, callback); + + } sendStatusUpdate(firstName, recipient, status, callback) { const handlebarsPath = path.join(__dirname, `../assets/email/statusEmail/${status}.hbs`); diff --git a/services/hacker.service.js b/services/hacker.service.js index ff0adb60..5993b8b0 100644 --- a/services/hacker.service.js +++ b/services/hacker.service.js @@ -5,6 +5,9 @@ const logger = require("./logger.service"); const cache = require("memory-cache"); const Constants = require("../constants/general.constant"); + +const QRCode = require("qrcode"); + /** * @function createHacker * @param {{_id: ObjectId, accountId: ObjectId, school: string, gender: string, needsBus: boolean, application: {Object}}} hackerDetails @@ -94,6 +97,28 @@ async function getStatsAllHackersCached() { return getStats(allHackers); } +/** + * Generate a QR code for the hacker. + * @param {string} str The string to be encoded in the QR code. + */ +async function generateQRCode(str) { + const response = await QRCode.toDataURL(str, { + scale: 4 + }); + return response; +} + +/** + * Generate the link for the single hacker view page on frontend. + * @param {string} httpOrHttps either HTTP or HTTPs + * @param {string} domain The domain of the frontend site + * @param {string} id The ID of the hacker to view + */ +function generateHackerViewLink(httpOrHttps, domain, id) { + const link = `${httpOrHttps}://${domain}/application/view/${id}`; + return link; +} + function getStats(hackers) { const TAG = `[ hacker Service # getStats ]`; const stats = { @@ -151,4 +176,6 @@ module.exports = { findByAccountId: findByAccountId, getStats: getStats, getStatsAllHackersCached: getStatsAllHackersCached, + generateQRCode: generateQRCode, + generateHackerViewLink: generateHackerViewLink, }; \ No newline at end of file diff --git a/services/search.service.js b/services/search.service.js index 7d0f3b3e..597a4cb1 100644 --- a/services/search.service.js +++ b/services/search.service.js @@ -17,10 +17,12 @@ function executeQuery(model, queryArray, page, limit, sort, sort_by, shouldExpan var query; switch (model.toLowerCase()) { case "hacker": - query = (shouldExpand) ? Hacker.find().populate({ + query = (shouldExpand) ? Hacker.find().populate([{ path: "accountId", select: " -password" - }) : Hacker.find(); + }, { + path: "teamId" + }]) : Hacker.find(); break; default: return []; diff --git a/tests/hacker.test.js b/tests/hacker.test.js index db317b33..76948a78 100644 --- a/tests/hacker.test.js +++ b/tests/hacker.test.js @@ -910,4 +910,56 @@ describe("GET Hacker stats", function () { done(); }); }); +}); + +describe("POST send week-of email", function () { + it("It should FAIL to send the week-of email due to invalid Authentication", function (done) { + //this takes a lot of time for some reason + chai.request(server.app) + .post(`/api/hacker/email/weekOf/${noTeamHacker0._id}`) + .end(function (err, res) { + res.should.have.status(401); + res.should.be.json; + res.body.should.have.property("message"); + res.body.message.should.equal(Constants.Error.AUTH_401_MESSAGE); + res.body.should.have.property("data"); + done(); + }); + }); + it("It should FAIL to send the week-of email due to invalid Authorization", function (done) { + //this takes a lot of time for some reason + util.auth.login(agent, noTeamHacker0, (error) => { + if (error) { + return done(error); + } + return agent + .post(`/api/hacker/email/weekOf/${noTeamHacker0._id}`) + .end(function (err, res) { + res.should.have.status(403); + res.should.be.json; + res.body.should.have.property("message"); + res.body.message.should.equal(Constants.Error.AUTH_403_MESSAGE); + res.body.should.have.property("data"); + done(); + }); + }); + }); + it("It should SUCCEED to send the week-of email", function (done) { + //this takes a lot of time for some reason + util.auth.login(agent, Admin0, (error) => { + if (error) { + return done(error); + } + return agent + .post(`/api/hacker/email/weekOf/${TeamHacker0._id}`) + .end(function (err, res) { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property("message"); + res.body.message.should.equal(Constants.Success.HACKER_SENT_WEEK_OF); + res.body.should.have.property("data"); + done(); + }); + }); + }); }); \ No newline at end of file diff --git a/tests/team.test.js b/tests/team.test.js index 3c37cb95..931e62ca 100644 --- a/tests/team.test.js +++ b/tests/team.test.js @@ -76,6 +76,12 @@ describe("GET team", function () { res.body.should.have.property("data"); res.body.data.should.have.property("team"); res.body.data.team.name.should.equal("FullTeam"); + res.body.data.team.should.have.property("members"); + res.body.data.team.members[0].should.equal(util.hacker.TeamHacker1._id.toString()); + res.body.data.team.members[1].should.equal(util.hacker.TeamHacker2._id.toString()); + res.body.data.team.members[2].should.equal(util.hacker.TeamHacker3._id.toString()); + res.body.data.team.members[3].should.equal(util.hacker.TeamHacker4._id.toString()); + res.body.data.should.have.property("members"); res.body.data.members[0].firstName.should.equal(util.account.hackerAccounts.stored.team[1].firstName); res.body.data.members[0].lastName.should.equal(util.account.hackerAccounts.stored.team[1].lastName);