From fd70eb11c9585a2b1c149c265ae5b1cbe508428d Mon Sep 17 00:00:00 2001 From: Pierre Theo Klein Date: Sun, 13 Jan 2019 09:22:06 -0500 Subject: [PATCH 1/8] WIP --- assets/email/Ticket.hbs | 0 middlewares/hacker.middleware.js | 21 ++ package-lock.json | 338 +++++++++++++++++++++++++------ package.json | 3 +- services/email.service.js | 22 ++ services/hacker.service.js | 12 ++ 6 files changed, 335 insertions(+), 61 deletions(-) create mode 100644 assets/email/Ticket.hbs diff --git a/assets/email/Ticket.hbs b/assets/email/Ticket.hbs new file mode 100644 index 00000000..e69de29b diff --git a/middlewares/hacker.middleware.js b/middlewares/hacker.middleware.js index 2e4fbd71..4f70b519 100644 --- a/middlewares/hacker.middleware.js +++ b/middlewares/hacker.middleware.js @@ -308,6 +308,26 @@ 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 ticketSVG = await Services.Hacker.generateHackerQRCode(hacker._id); + 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 +540,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/package-lock.json b/package-lock.json index 09491221..e64be60c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,7 +6,7 @@ "dependencies": { "@google-cloud/common": { "version": "0.17.0", - "resolved": "https://registry.npmjs.org/@google-cloud/common/-/common-0.17.0.tgz", + "resolved": "http://registry.npmjs.org/@google-cloud/common/-/common-0.17.0.tgz", "integrity": "sha512-HRZLSU762E6HaKoGfJGa8W95yRjb9rY7LePhjaHK9ILAnFacMuUGVamDbTHu1csZomm1g3tZTtXfX/aAhtie/Q==", "requires": { "array-uniq": "^1.0.3", @@ -71,7 +71,7 @@ "dependencies": { "@google-cloud/common": { "version": "0.16.2", - "resolved": "https://registry.npmjs.org/@google-cloud/common/-/common-0.16.2.tgz", + "resolved": "http://registry.npmjs.org/@google-cloud/common/-/common-0.16.2.tgz", "integrity": "sha512-GrkaFoj0/oO36pNs4yLmaYhTujuA3i21FdQik99Fd/APix1uhf01VlpJY4lAteTDFLRNkRx6ydEh7OVvmeUHng==", "requires": { "array-uniq": "^1.0.3", @@ -96,7 +96,7 @@ }, "google-auto-auth": { "version": "0.9.7", - "resolved": "https://registry.npmjs.org/google-auto-auth/-/google-auto-auth-0.9.7.tgz", + "resolved": "http://registry.npmjs.org/google-auto-auth/-/google-auto-auth-0.9.7.tgz", "integrity": "sha512-Nro7aIFrL2NP0G7PoGrJqXGMZj8AjdBOcbZXRRm/8T3w08NUHIiNN3dxpuUYzDsZizslH+c8e+7HXL8vh3JXTQ==", "requires": { "async": "^2.3.0", @@ -1668,6 +1668,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", @@ -2059,7 +2067,7 @@ "dependencies": { "pify": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" } } @@ -2091,7 +2099,7 @@ }, "globby": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-4.1.0.tgz", + "resolved": "http://registry.npmjs.org/globby/-/globby-4.1.0.tgz", "integrity": "sha1-CA9UVJ7BuCpsYOYx/ILhIR2+lfg=", "requires": { "array-union": "^1.0.1", @@ -2104,7 +2112,7 @@ }, "pify": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" } } @@ -2130,7 +2138,6 @@ "version": "5.1.0", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", - "dev": true, "requires": { "lru-cache": "^4.0.1", "shebang-command": "^1.2.0", @@ -2349,6 +2356,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", @@ -2596,7 +2608,6 @@ "version": "0.7.0", "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", - "dev": true, "requires": { "cross-spawn": "^5.0.1", "get-stream": "^3.0.0", @@ -2987,8 +2998,7 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "aproba": { "version": "1.2.0", @@ -3009,14 +3019,12 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, - "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -3031,20 +3039,17 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "core-util-is": { "version": "1.0.2", @@ -3161,8 +3166,7 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "ini": { "version": "1.3.5", @@ -3174,7 +3178,6 @@ "version": "1.0.0", "bundled": true, "dev": true, - "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -3189,7 +3192,6 @@ "version": "3.0.4", "bundled": true, "dev": true, - "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -3197,14 +3199,12 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "minipass": { "version": "2.2.4", "bundled": true, "dev": true, - "optional": true, "requires": { "safe-buffer": "^5.1.1", "yallist": "^3.0.0" @@ -3223,7 +3223,6 @@ "version": "0.5.1", "bundled": true, "dev": true, - "optional": true, "requires": { "minimist": "0.0.8" } @@ -3304,8 +3303,7 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "object-assign": { "version": "4.1.1", @@ -3317,7 +3315,6 @@ "version": "1.4.0", "bundled": true, "dev": true, - "optional": true, "requires": { "wrappy": "1" } @@ -3403,8 +3400,7 @@ "safe-buffer": { "version": "5.1.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "safer-buffer": { "version": "2.1.2", @@ -3440,7 +3436,6 @@ "version": "1.0.2", "bundled": true, "dev": true, - "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -3460,7 +3455,6 @@ "version": "3.0.1", "bundled": true, "dev": true, - "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -3504,20 +3498,18 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "yallist": { "version": "3.0.2", "bundled": true, - "dev": true, - "optional": true + "dev": true } } }, "gcp-metadata": { "version": "0.6.3", - "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-0.6.3.tgz", + "resolved": "http://registry.npmjs.org/gcp-metadata/-/gcp-metadata-0.6.3.tgz", "integrity": "sha512-MSmczZctbz91AxCvqp9GHBoZOSbJKAICV7Ow/AIWSJZRrRchUd5NL1b2P4OfP+4m490BEUPhhARfpHdqCxuCvg==", "requires": { "axios": "^0.18.0", @@ -3698,6 +3690,11 @@ } } }, + "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", @@ -3712,8 +3709,7 @@ "get-stream": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", - "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", - "dev": true + "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=" }, "get-value": { "version": "2.0.6", @@ -4860,8 +4856,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", @@ -4891,8 +4886,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", @@ -5153,11 +5147,27 @@ "dependencies": { "pify": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" } } }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "requires": { + "p-locate": "^2.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", @@ -5264,7 +5274,6 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.2.tgz", "integrity": "sha512-wgeVXhrDwAWnIF/yZARsFnMBtdFXOg1b8RIrhilp+0iDYN4mdQcNZElDZ0e4B64BhaxeQ5zN7PMyvu7we1kPeQ==", - "dev": true, "requires": { "pseudomap": "^1.0.2", "yallist": "^2.1.2" @@ -5334,6 +5343,14 @@ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" }, + "mem": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/mem/-/mem-1.1.0.tgz", + "integrity": "sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=", + "requires": { + "mimic-fn": "^1.0.0" + } + }, "memory-cache": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/memory-cache/-/memory-cache-0.2.0.tgz", @@ -5369,7 +5386,7 @@ }, "minimist": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" } } @@ -5432,6 +5449,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", @@ -5745,7 +5767,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" } @@ -5871,8 +5892,28 @@ "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-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=" }, "package-json": { "version": "4.0.1", @@ -5953,8 +5994,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", @@ -6012,6 +6052,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", @@ -6235,6 +6280,143 @@ "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=" }, + "qrcode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.3.2.tgz", + "integrity": "sha512-sYo+Tf7nx14YZSNPqnyEWefVrjUEpM4/NxOrxOk+jcQ9NhM2LCNuzGmQctDhGTQdO2YJorKY55dypx+hvVo5jw==", + "requires": { + "can-promise": "0.0.1", + "dijkstrajs": "^1.0.1", + "isarray": "^2.0.1", + "pngjs": "^3.3.0", + "yargs": "^8.0.2" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" + }, + "camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=" + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "requires": { + "locate-path": "^2.0.0" + } + }, + "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==" + }, + "load-json-file": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", + "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "strip-bom": "^3.0.0" + } + }, + "os-locale": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz", + "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==", + "requires": { + "execa": "^0.7.0", + "lcid": "^1.0.0", + "mem": "^1.1.0" + } + }, + "path-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", + "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", + "requires": { + "pify": "^2.0.0" + } + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" + }, + "read-pkg": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", + "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", + "requires": { + "load-json-file": "^2.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^2.0.0" + } + }, + "read-pkg-up": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", + "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", + "requires": { + "find-up": "^2.0.0", + "read-pkg": "^2.0.0" + } + }, + "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" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=" + }, + "yargs": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-8.0.2.tgz", + "integrity": "sha1-YpmpBVsc78lp/355wdkY3Osiw2A=", + "requires": { + "camelcase": "^4.1.0", + "cliui": "^3.2.0", + "decamelize": "^1.1.1", + "get-caller-file": "^1.0.1", + "os-locale": "^2.0.0", + "read-pkg-up": "^2.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", + "yargs-parser": "^7.0.0" + } + } + } + }, "qs": { "version": "6.5.1", "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", @@ -6321,7 +6503,7 @@ }, "pify": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" } } @@ -6456,6 +6638,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", @@ -6569,6 +6761,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", @@ -6605,7 +6802,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" } @@ -6613,8 +6809,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", @@ -6992,8 +7187,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", @@ -7522,11 +7716,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", @@ -7569,6 +7767,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", @@ -7666,6 +7869,21 @@ "window-size": "^0.1.4", "y18n": "^3.2.0" } + }, + "yargs-parser": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-7.0.0.tgz", + "integrity": "sha1-jQrELxbqVd69MyyvTEA4s+P139k=", + "requires": { + "camelcase": "^4.1.0" + }, + "dependencies": { + "camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=" + } + } } } } diff --git a/package.json b/package.json index a01139d6..5055d9c4 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "passport": "^0.4.0", "passport-local": "^1.0.0", "q": "^1.5.1", + "qrcode": "^1.3.2", "winston": "^2.4.4" }, "devDependencies": { @@ -48,4 +49,4 @@ "mocha": "^5.2.0", "nodemon": "^1.17.3" } -} +} \ No newline at end of file diff --git a/services/email.service.js b/services/email.service.js index f0a8ed55..00d39183 100644 --- a/services/email.service.js +++ b/services/email.service.js @@ -52,6 +52,28 @@ class EmailService { }); } + sendTicketEmail(firstName, recipient, ticketSVG, callback) { + const handlebarsPath = path.join(__dirname, `../assets/email/statusEmail/Ticket.hbs`); + const mailData = { + to: recipient, + from: process.env.NO_REPLY_EMAIL, + subject: Constants.EMAIL_SUBJECTS[status], + html: this.renderEmail(handlebarsPath, { + firstName: firstName, + ticketSVG: ticketSVG + }) + }; + 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`); const mailData = { diff --git a/services/hacker.service.js b/services/hacker.service.js index ff0adb60..d35bb1de 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,14 @@ async function getStatsAllHackersCached() { return getStats(allHackers); } + +async function generateHackerQRCode(hacker) { + const response = await QRCode.toString(hacker.id, { + type: "svg" + }); + return response; +} + function getStats(hackers) { const TAG = `[ hacker Service # getStats ]`; const stats = { @@ -151,4 +162,5 @@ module.exports = { findByAccountId: findByAccountId, getStats: getStats, getStatsAllHackersCached: getStatsAllHackersCached, + generateHackerQRCode: generateHackerQRCode, }; \ No newline at end of file From 387d3dfa6730b610b6516bbf667805509dff7bd5 Mon Sep 17 00:00:00 2001 From: Pierre Theo Klein Date: Wed, 23 Jan 2019 18:06:11 -0500 Subject: [PATCH 2/8] Finish route --- assets/email/Ticket.hbs | 1 + constants/general.constant.js | 7 ++++++- constants/routes.constant.js | 8 ++++++++ constants/success.constant.js | 3 +++ controllers/hacker.controller.js | 8 ++++++++ middlewares/hacker.middleware.js | 6 +++++- package.json | 2 +- routes/api/hacker.js | 13 +++++++++++++ services/email.service.js | 13 +++++++------ services/hacker.service.js | 15 ++++++++++----- 10 files changed, 62 insertions(+), 14 deletions(-) diff --git a/assets/email/Ticket.hbs b/assets/email/Ticket.hbs index e69de29b..ecdf997b 100644 --- a/assets/email/Ticket.hbs +++ b/assets/email/Ticket.hbs @@ -0,0 +1 @@ +
{{firstName}}
\ No newline at end of file diff --git a/constants/general.constant.js b/constants/general.constant.js index 913a1a5b..2fe34abc 100644 --- a/constants/general.constant.js +++ b/constants/general.constant.js @@ -68,6 +68,8 @@ const ANY_REGEX = /^.+$/; const MAX_TEAM_SIZE = 4; +const WEEK_OF = 'Week Of'; + const EMAIL_SUBJECTS = {}; EMAIL_SUBJECTS[HACKER_STATUS_NONE] = `Application for ${HACKATHON_NAME} incomplete`; EMAIL_SUBJECTS[HACKER_STATUS_APPLIED] = `Thanks for applying to ${HACKATHON_NAME}`; @@ -77,6 +79,8 @@ EMAIL_SUBJECTS[HACKER_STATUS_CONFIRMED] = `Thanks for confirming your attendance EMAIL_SUBJECTS[HACKER_STATUS_CANCELLED] = "Sorry to see you go"; EMAIL_SUBJECTS[HACKER_STATUS_CHECKED_IN] = `Welcome to ${HACKATHON_NAME}`; +EMAIL_SUBJECTS[WEEK_OF] = `Welcome to ${HACKATHON_NAME}`; + const CONFIRM_ACC_EMAIL_SUBJECT = `Please complete your hacker application for ${HACKATHON_NAME}`; const CREATE_ACC_EMAIL_SUBJECTS = {}; CREATE_ACC_EMAIL_SUBJECTS[HACKER] = `You've been invited to create a hacker account for ${HACKATHON_NAME}`; @@ -123,5 +127,6 @@ module.exports = { POST_ROLES: POST_ROLES, CACHE_TIMEOUT_STATS: CACHE_TIMEOUT_STATS, CACHE_KEY_STATS: CACHE_KEY_STATS, - MAX_TEAM_SIZE: MAX_TEAM_SIZE + MAX_TEAM_SIZE: MAX_TEAM_SIZE, + WEEK_OF: WEEK_OF, }; \ No newline at end of file diff --git a/constants/routes.constant.js b/constants/routes.constant.js index fbeb9f6c..d7039d2b 100644 --- a/constants/routes.constant.js +++ b/constants/routes.constant.js @@ -124,6 +124,14 @@ const hackerRoutes = { requestType: Constants.REQUEST_TYPES.PATCH, uri: "/api/hacker/confirmation/" + Constants.ROLE_CATEGORIES.SELF, }, + "postAnySendWeekOfEmail": { + requestType: Constants.REQUEST_TYPES.POST, + uri: "/api/hacker/email/weekOf/" + Constants.ROLE_CATEGORIES.ALL, + }, + "postSelfSendWeekOfEmail": { + requestType: Constants.REQUEST_TYPES.POST, + uri: "/api/hacker/email/weekOf/" + Constants.ROLE_CATEGORIES.SELF, + }, }; const sponsorRoutes = { diff --git a/constants/success.constant.js b/constants/success.constant.js index 19932f18..c8af029b 100644 --- a/constants/success.constant.js +++ b/constants/success.constant.js @@ -21,6 +21,7 @@ const HACKER_GET_BY_ID = "Hacker found by id."; const HACKER_READ = "Hacker retrieval successful."; const HACKER_CREATE = "Hacker creation successful."; const HACKER_UPDATE = "Hacker update successful."; +const HACKER_SENT_WEEK_OF = "Hacker week-of email sent." const RESUME_UPLOAD = "Resume upload successful."; const RESUME_DOWNLOAD = "Resume download successful."; @@ -66,6 +67,8 @@ module.exports = { HACKER_CREATE: HACKER_CREATE, HACKER_UPDATE: HACKER_UPDATE, + HACKER_SENT_WEEK_OF: HACKER_SENT_WEEK_OF, + RESUME_UPLOAD: RESUME_UPLOAD, RESUME_DOWNLOAD: RESUME_DOWNLOAD, diff --git a/controllers/hacker.controller.js b/controllers/hacker.controller.js index abf4d60d..daf49bf1 100644 --- a/controllers/hacker.controller.js +++ b/controllers/hacker.controller.js @@ -79,6 +79,13 @@ function gotStats(req, res) { } +function sentWeekOfEmail(req, res) { + return res.status(200).json({ + message: Constants.Success.HACKER_SENT_WEEK_OF, + data: {} + }); +} + module.exports = { updatedHacker: updatedHacker, createdHacker: createdHacker, @@ -86,4 +93,5 @@ module.exports = { downloadedResume: downloadedResume, showHacker: showHacker, gotStats: gotStats, + sentWeekOfEmail: sentWeekOfEmail, }; \ No newline at end of file diff --git a/middlewares/hacker.middleware.js b/middlewares/hacker.middleware.js index 4f70b519..5a2c56a2 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") @@ -316,7 +317,10 @@ async function sendAppliedStatusEmail(req, res, next) { */ async function sendTicketEmail(req, res, next) { const hacker = req.body.hacker; - const ticketSVG = await Services.Hacker.generateHackerQRCode(hacker._id); + 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({ diff --git a/package.json b/package.json index 5055d9c4..9db7c058 100644 --- a/package.json +++ b/package.json @@ -49,4 +49,4 @@ "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 5c04897c..ba615c63 100644 --- a/routes/api/hacker.js +++ b/routes/api/hacker.js @@ -585,6 +585,19 @@ module.exports = { Middleware.Hacker.sendStatusUpdateEmail, Controllers.Hacker.updatedHacker ); + + 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/services/email.service.js b/services/email.service.js index 00d39183..eeec8c71 100644 --- a/services/email.service.js +++ b/services/email.service.js @@ -53,15 +53,16 @@ class EmailService { } sendTicketEmail(firstName, recipient, ticketSVG, callback) { - const handlebarsPath = path.join(__dirname, `../assets/email/statusEmail/Ticket.hbs`); + const handlebarsPath = path.join(__dirname, `../assets/email/Ticket.hbs`); + const html = this.renderEmail(handlebarsPath, { + firstName: firstName, + svg: ticketSVG + }); const mailData = { to: recipient, from: process.env.NO_REPLY_EMAIL, - subject: Constants.EMAIL_SUBJECTS[status], - html: this.renderEmail(handlebarsPath, { - firstName: firstName, - ticketSVG: ticketSVG - }) + subject: Constants.EMAIL_SUBJECTS[Constants.WEEK_OF], + html: html }; this.send(mailData).then( (response) => { diff --git a/services/hacker.service.js b/services/hacker.service.js index d35bb1de..fe4cb8fa 100644 --- a/services/hacker.service.js +++ b/services/hacker.service.js @@ -98,13 +98,17 @@ async function getStatsAllHackersCached() { } -async function generateHackerQRCode(hacker) { - const response = await QRCode.toString(hacker.id, { - type: "svg" - }); +async function generateQRCode(str) { + const response = await QRCode.toDataURL(str, { scale: 10 }); return response; } +function generateHackerViewLink(httpOrHttps, domain, id) { + const link = `${httpOrHttps}://${domain}/hacker/view/${id}`; + return link; +} + + function getStats(hackers) { const TAG = `[ hacker Service # getStats ]`; const stats = { @@ -162,5 +166,6 @@ module.exports = { findByAccountId: findByAccountId, getStats: getStats, getStatsAllHackersCached: getStatsAllHackersCached, - generateHackerQRCode: generateHackerQRCode, + generateQRCode: generateQRCode, + generateHackerViewLink: generateHackerViewLink, }; \ No newline at end of file From 2703a94e26dc5ad5f6c919347d057929930f1f50 Mon Sep 17 00:00:00 2001 From: Pierre Theo Klein Date: Wed, 23 Jan 2019 18:44:26 -0500 Subject: [PATCH 3/8] Add docs --- .vscode/settings.json | 3 ++- assets/email/Ticket.hbs | 2 +- docs/api/api_data.js | 52 ++++++++++++++++++++++++++++++++++++++ docs/api/api_data.json | 52 ++++++++++++++++++++++++++++++++++++++ docs/api/api_project.js | 30 +++++++++++----------- docs/api/api_project.json | 30 +++++++++++----------- routes/api/hacker.js | 19 +++++++++++++- services/email.service.js | 12 ++++++--- services/hacker.service.js | 16 +++++++++--- 9 files changed, 177 insertions(+), 39 deletions(-) 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 index ecdf997b..d9d006ff 100644 --- a/assets/email/Ticket.hbs +++ b/assets/email/Ticket.hbs @@ -1 +1 @@ -
{{firstName}}
\ No newline at end of file +
{{firstName}}
\ No newline at end of file diff --git a/docs/api/api_data.js b/docs/api/api_data.js index d2b5cc09..c1ce7f64 100644 --- a/docs/api/api_data.js +++ b/docs/api/api_data.js @@ -1790,6 +1790,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": "/hacker/self", diff --git a/docs/api/api_data.json b/docs/api/api_data.json index 04a94c3f..5f23c23c 100644 --- a/docs/api/api_data.json +++ b/docs/api/api_data.json @@ -1789,6 +1789,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": "/hacker/self", diff --git a/docs/api/api_project.js b/docs/api/api_project.js index 0cd83632..c65c1a87 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-10T23:24:21.462Z", - "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 8ad885dc..380ebe26 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-10T23:24:21.462Z", - "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/routes/api/hacker.js b/routes/api/hacker.js index ba615c63..b5d67e32 100644 --- a/routes/api/hacker.js +++ b/routes/api/hacker.js @@ -586,6 +586,23 @@ module.exports = { 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]), @@ -593,7 +610,7 @@ module.exports = { Middleware.Validator.RouteParam.idValidator, Middleware.parseBody.middleware, Middleware.Hacker.findById, - Middleware.Hacker.checkStatus([CONSTANTS.HACKER_STATUS_CONFIRMED,CONSTANTS.HACKER_STATUS_CHECKED_IN]), + Middleware.Hacker.checkStatus([CONSTANTS.HACKER_STATUS_CONFIRMED, CONSTANTS.HACKER_STATUS_CHECKED_IN]), Middleware.Hacker.sendTicketEmail, Controllers.Hacker.sentWeekOfEmail ) diff --git a/services/email.service.js b/services/email.service.js index eeec8c71..ccde0da8 100644 --- a/services/email.service.js +++ b/services/email.service.js @@ -51,12 +51,18 @@ class EmailService { } }); } - - sendTicketEmail(firstName, recipient, ticketSVG, callback) { + /** + * 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, - svg: ticketSVG + ticket: ticket }); const mailData = { to: recipient, diff --git a/services/hacker.service.js b/services/hacker.service.js index fe4cb8fa..e615e170 100644 --- a/services/hacker.service.js +++ b/services/hacker.service.js @@ -97,18 +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: 10 }); + const response = await QRCode.toDataURL(str, { + scale: 3 + }); 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}/hacker/view/${id}`; return link; } - function getStats(hackers) { const TAG = `[ hacker Service # getStats ]`; const stats = { From 423d525e3bd8a2de73d41dcf9a123b2d80989afb Mon Sep 17 00:00:00 2001 From: Pierre Theo Klein Date: Wed, 23 Jan 2019 18:56:11 -0500 Subject: [PATCH 4/8] Add tests --- tests/hacker.test.js | 53 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/tests/hacker.test.js b/tests/hacker.test.js index db317b33..e81f4bcd 100644 --- a/tests/hacker.test.js +++ b/tests/hacker.test.js @@ -910,4 +910,57 @@ 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/${noTeamHacker0._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"); + res.body.data.should.equal(JSON.stringify({})); + done(); + }); + }); + }); }); \ No newline at end of file From 824e6a930d2572bd7dbf1937e5fa4540ec502280 Mon Sep 17 00:00:00 2001 From: Pierre Theo Klein Date: Wed, 23 Jan 2019 20:28:56 -0500 Subject: [PATCH 5/8] Switch to member who has confirmed --- tests/hacker.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/hacker.test.js b/tests/hacker.test.js index e81f4bcd..4db0f0ad 100644 --- a/tests/hacker.test.js +++ b/tests/hacker.test.js @@ -951,7 +951,7 @@ describe("POST send week-of email", function () { return done(error); } return agent - .post(`/api/hacker/email/weekOf/${noTeamHacker0._id}`) + .post(`/api/hacker/email/weekOf/${TeamHacker0._id}`) .end(function (err, res) { res.should.have.status(200); res.should.be.json; From 458c6c8cbb50946016ac00c3c9517e858be0fdfd Mon Sep 17 00:00:00 2001 From: Pierre Theo Klein Date: Thu, 24 Jan 2019 10:29:39 -0500 Subject: [PATCH 6/8] Fix test --- tests/hacker.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/hacker.test.js b/tests/hacker.test.js index 4db0f0ad..7b6e4ff2 100644 --- a/tests/hacker.test.js +++ b/tests/hacker.test.js @@ -958,7 +958,7 @@ describe("POST send week-of email", function () { res.body.should.have.property("message"); res.body.message.should.equal(Constants.Success.HACKER_SENT_WEEK_OF); res.body.should.have.property("data"); - res.body.data.should.equal(JSON.stringify({})); + res.body.data.should.equal({}); done(); }); }); From f7701568a5734abfcf88be99dd15ba76809a22c2 Mon Sep 17 00:00:00 2001 From: Pierre Theo Klein Date: Thu, 24 Jan 2019 14:29:54 -0500 Subject: [PATCH 7/8] Remove line --- tests/hacker.test.js | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/hacker.test.js b/tests/hacker.test.js index 7b6e4ff2..76948a78 100644 --- a/tests/hacker.test.js +++ b/tests/hacker.test.js @@ -958,7 +958,6 @@ describe("POST send week-of email", function () { res.body.should.have.property("message"); res.body.message.should.equal(Constants.Success.HACKER_SENT_WEEK_OF); res.body.should.have.property("data"); - res.body.data.should.equal({}); done(); }); }); From 9ec7ca701578e167c246ed2ddda1daa6a12401ec Mon Sep 17 00:00:00 2001 From: Pierre Theo Klein Date: Fri, 25 Jan 2019 15:53:38 -0500 Subject: [PATCH 8/8] Change URL of QR code --- services/hacker.service.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/hacker.service.js b/services/hacker.service.js index e615e170..24259f21 100644 --- a/services/hacker.service.js +++ b/services/hacker.service.js @@ -115,7 +115,7 @@ async function generateQRCode(str) { * @param {string} id The ID of the hacker to view */ function generateHackerViewLink(httpOrHttps, domain, id) { - const link = `${httpOrHttps}://${domain}/hacker/view/${id}`; + const link = `${httpOrHttps}://${domain}/application/view/${id}`; return link; }