From 64026a791e9c67efffe81c050cb7b2ef11f55608 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Wed, 24 Nov 2021 12:46:21 -0700 Subject: [PATCH 01/35] Add packages for user authentication --- package-lock.json | 142 ++++++++++++++++++++++++++++++++++++++++++---- package.json | 1 + 2 files changed, 131 insertions(+), 12 deletions(-) diff --git a/package-lock.json b/package-lock.json index 225db7b..9ec6365 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "debug": "~2.6.9", "dotenv": "^10.0.0", "express": "~4.16.0", + "express-session": "^1.17.2", "http-errors": "~1.6.2", "morgan": "~1.9.0", "nodemailer": "^6.6.5", @@ -520,6 +521,59 @@ "node": ">= 0.10.0" } }, + "node_modules/express-session": { + "version": "1.17.2", + "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.17.2.tgz", + "integrity": "sha512-mPcYcLA0lvh7D4Oqr5aNJFMtBMKPLl++OKKxkHzZ0U0oDq1rpKBnkR5f5vCHR26VeArlTOEF9td4x5IjICksRQ==", + "dependencies": { + "cookie": "0.4.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~2.0.0", + "on-headers": "~1.0.2", + "parseurl": "~1.3.3", + "safe-buffer": "5.2.1", + "uid-safe": "~2.1.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/express-session/node_modules/cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express-session/node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/express-session/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", @@ -965,9 +1019,9 @@ } }, "node_modules/on-headers": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.1.tgz", - "integrity": "sha1-ko9dD0cNSTQmUepnlLCFfBAGk/c=", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", "engines": { "node": ">= 0.8" } @@ -987,9 +1041,9 @@ "integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==" }, "node_modules/parseurl": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", - "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", "engines": { "node": ">= 0.8" } @@ -1299,6 +1353,14 @@ "node": ">=0.6" } }, + "node_modules/random-bytes": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", + "integrity": "sha1-T2ih3Arli9P7lYSMMDJNt11kNgs=", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/range-parser": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", @@ -1602,6 +1664,17 @@ "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=", "optional": true }, + "node_modules/uid-safe": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz", + "integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==", + "dependencies": { + "random-bytes": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", @@ -2091,6 +2164,38 @@ "vary": "~1.1.2" } }, + "express-session": { + "version": "1.17.2", + "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.17.2.tgz", + "integrity": "sha512-mPcYcLA0lvh7D4Oqr5aNJFMtBMKPLl++OKKxkHzZ0U0oDq1rpKBnkR5f5vCHR26VeArlTOEF9td4x5IjICksRQ==", + "requires": { + "cookie": "0.4.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~2.0.0", + "on-headers": "~1.0.2", + "parseurl": "~1.3.3", + "safe-buffer": "5.2.1", + "uid-safe": "~2.1.5" + }, + "dependencies": { + "cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==" + }, + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + } + } + }, "extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", @@ -2441,9 +2546,9 @@ } }, "on-headers": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.1.tgz", - "integrity": "sha1-ko9dD0cNSTQmUepnlLCFfBAGk/c=" + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==" }, "once": { "version": "1.4.0", @@ -2460,9 +2565,9 @@ "integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==" }, "parseurl": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", - "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=" + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" }, "passport": { "version": "0.5.0", @@ -2719,6 +2824,11 @@ "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==" }, + "random-bytes": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", + "integrity": "sha1-T2ih3Arli9P7lYSMMDJNt11kNgs=" + }, "range-parser": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", @@ -2971,6 +3081,14 @@ "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=", "optional": true }, + "uid-safe": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz", + "integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==", + "requires": { + "random-bytes": "~1.0.0" + } + }, "unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", diff --git a/package.json b/package.json index c94c979..eb6d387 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "debug": "~2.6.9", "dotenv": "^10.0.0", "express": "~4.16.0", + "express-session": "^1.17.2", "http-errors": "~1.6.2", "morgan": "~1.9.0", "nodemailer": "^6.6.5", From 9c9c106acd1ea730fb3877d49662fc14b456d0cb Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Wed, 24 Nov 2021 15:53:42 -0700 Subject: [PATCH 02/35] Database init script now creates accounts table --- database/init_database.sql | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/database/init_database.sql b/database/init_database.sql index ad4f0e7..ea8decf 100644 --- a/database/init_database.sql +++ b/database/init_database.sql @@ -22,10 +22,7 @@ scores: accounts: users: - *user_id* | email | salt | password_hash | role | approved - - sessions: - *session_id* | ~user_id~ | expiration_date + *user_id* | email | password | admin */ @@ -102,4 +99,13 @@ CREATE TABLE IF NOT EXISTS scores.games( ); +CREATE SCHEMA IF NOT EXISTS accounts; + +CREATE TABLE IF NOT EXISTS accounts.users( + user_id BIGINT GENERATED ALWAYS AS IDENTITY, + email TEXT UNIQUE NOT NULL, + password TEXT NOT NULL, + admin BOOLEAN NOT NULL DEFAULT FALSE + ); + COMMIT; \ No newline at end of file From fbfa500b8a513d198683f436ec02c384b70c448d Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Wed, 24 Nov 2021 15:55:44 -0700 Subject: [PATCH 03/35] Install bcrypt for managing passwords --- package-lock.json | 857 ++++++++++++++++++++++++++++++++++++++++++++-- package.json | 1 + 2 files changed, 831 insertions(+), 27 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9ec6365..b41b189 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "0.0.0", "dependencies": { "async": "^3.2.2", + "bcrypt": "^5.0.1", "cookie-parser": "~1.4.3", "debug": "~2.6.9", "dotenv": "^10.0.0", @@ -26,6 +27,25 @@ "supertest": "^3.0.0" } }, + "node_modules/@mapbox/node-pre-gyp": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.7.tgz", + "integrity": "sha512-PplSvl4pJ5N3BkVjAdDzpPhVUPdC73JgttkR+LnBx2OORC1GCQsBjUeEuipf9uOaAM1SbxcdZFfR3KDTKm2S0A==", + "dependencies": { + "detect-libc": "^1.0.3", + "https-proxy-agent": "^5.0.0", + "make-dir": "^3.1.0", + "node-fetch": "^2.6.5", + "nopt": "^5.0.0", + "npmlog": "^6.0.0", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.11" + }, + "bin": { + "node-pre-gyp": "bin/node-pre-gyp" + } + }, "node_modules/@types/babel-types": { "version": "7.0.4", "resolved": "https://registry.npmjs.org/@types/babel-types/-/babel-types-7.0.4.tgz", @@ -39,6 +59,11 @@ "@types/babel-types": "*" } }, + "node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + }, "node_modules/accepts": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz", @@ -81,6 +106,38 @@ "node": ">=0.4.0" } }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/agent-base/node_modules/debug": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/agent-base/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, "node_modules/align-text": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", @@ -102,6 +159,44 @@ "node": ">=0.4.2" } }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/aproba": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==" + }, + "node_modules/are-we-there-yet": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", + "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/are-we-there-yet/node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", @@ -154,8 +249,7 @@ "node_modules/balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" }, "node_modules/basic-auth": { "version": "2.0.0", @@ -168,6 +262,19 @@ "node": ">= 0.8" } }, + "node_modules/bcrypt": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-5.0.1.tgz", + "integrity": "sha512-9BTgmrhZM2t1bNuDtrtIMVSmmxZBrJ71n8Wg+YgdjHuIWYF7SjjmCPZFB+/5i/o/PIeRpwVJR3P+NrpIItUjqw==", + "hasInstallScript": true, + "dependencies": { + "@mapbox/node-pre-gyp": "^1.0.0", + "node-addon-api": "^3.1.0" + }, + "engines": { + "node": ">= 10.0.0" + } + }, "node_modules/body-parser": { "version": "1.18.2", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.2.tgz", @@ -192,7 +299,6 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -248,6 +354,14 @@ "is-regex": "^1.0.3" } }, + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "engines": { + "node": ">=10" + } + }, "node_modules/clean-css": { "version": "3.4.28", "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-3.4.28.tgz", @@ -273,6 +387,14 @@ "wordwrap": "0.0.2" } }, + "node_modules/color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "bin": { + "color-support": "bin.js" + } + }, "node_modules/combined-stream": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz", @@ -305,8 +427,12 @@ "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "node_modules/console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" }, "node_modules/constantinople": { "version": "3.1.2", @@ -403,6 +529,11 @@ "node": ">=0.4.0" } }, + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" + }, "node_modules/depd": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", @@ -416,6 +547,17 @@ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" }, + "node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", + "bin": { + "detect-libc": "bin/detect-libc.js" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/diff": { "version": "3.5.0", "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", @@ -443,6 +585,11 @@ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, "node_modules/encodeurl": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", @@ -634,17 +781,46 @@ "node": ">= 0.6" } }, + "node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, "node_modules/function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, + "node_modules/gauge": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.0.tgz", + "integrity": "sha512-F8sU45yQpjQjxKkm1UOAhf0U/O0aFt//Fl7hsrNVto+patMHjs7dPI9mFOGUKbhrgKm0S3EjW3scMFuQmWSROw==", + "dependencies": { + "ansi-regex": "^5.0.1", + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.2", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.1", + "signal-exit": "^3.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.2" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16" + } + }, "node_modules/glob": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", @@ -696,6 +872,11 @@ "node": ">=4" } }, + "node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" + }, "node_modules/he": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", @@ -719,6 +900,39 @@ "node": ">= 0.6" } }, + "node_modules/https-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", + "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/https-proxy-agent/node_modules/debug": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/https-proxy-agent/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, "node_modules/iconv-lite": { "version": "0.4.19", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", @@ -731,7 +945,6 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -775,6 +988,14 @@ "node": ">=0.4.0" } }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "engines": { + "node": ">=8" + } + }, "node_modules/is-promise": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", @@ -843,6 +1064,39 @@ "node": ">=0.10.0" } }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -895,7 +1149,6 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -909,6 +1162,29 @@ "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", "dev": true }, + "node_modules/minipass": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.5.tgz", + "integrity": "sha512-+8NzxD82XQoNKNrl1d/FSi+X8wAEWR+sbYAfIvub4Nz0d22plFG72CEVVaufV8PNf4qSslFTD8VMOxNVhHCjTw==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/mkdirp": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", @@ -991,6 +1267,22 @@ "node": ">= 0.6" } }, + "node_modules/node-addon-api": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz", + "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==" + }, + "node_modules/node-fetch": { + "version": "2.6.6", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.6.tgz", + "integrity": "sha512-Z8/6vRlTUChSdIgMa51jxQ4lrw/Jy5SOW10ObaA47/RElsAN2c5Pn8bTgFGWn/ibwzXTE8qwr1Yzx28vsecXEA==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + } + }, "node_modules/nodemailer": { "version": "6.6.5", "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.6.5.tgz", @@ -999,6 +1291,34 @@ "node": ">=6.0.0" } }, + "node_modules/nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/npmlog": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.0.tgz", + "integrity": "sha512-03ppFRGlsyUaQFbGC2C8QWJN/C/K7PsfyD9aQdhVKAQIH4sQBc8WASqFBP7O+Ut4d2oo5LoeoboB3cGdBZSp6Q==", + "dependencies": { + "are-we-there-yet": "^2.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^4.0.0", + "set-blocking": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16" + } + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -1030,7 +1350,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, "dependencies": { "wrappy": "1" } @@ -1076,7 +1395,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -1457,11 +1775,58 @@ "node": ">=0.10.0" } }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dependencies": { + "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" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/safe-buffer": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" }, + "node_modules/semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/send": { "version": "0.16.2", "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", @@ -1499,11 +1864,21 @@ "node": ">= 0.8.0" } }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" + }, "node_modules/setprototypeof": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" }, + "node_modules/signal-exit": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.6.tgz", + "integrity": "sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ==" + }, "node_modules/source-map": { "version": "0.4.4", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", @@ -1552,6 +1927,30 @@ "safe-buffer": "~5.1.0" } }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/superagent": { "version": "3.8.2", "resolved": "https://registry.npmjs.org/superagent/-/superagent-3.8.2.tgz", @@ -1607,6 +2006,33 @@ "node": ">=4" } }, + "node_modules/tar": { + "version": "6.1.11", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz", + "integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^3.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/tar/node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/to-fast-properties": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", @@ -1620,6 +2046,11 @@ "resolved": "https://registry.npmjs.org/token-stream/-/token-stream-0.0.1.tgz", "integrity": "sha1-zu78cXp2xDFvEm0LnbqlXX598Bo=" }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" + }, "node_modules/type-is": { "version": "1.6.16", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz", @@ -1712,6 +2143,28 @@ "node": ">=0.10.0" } }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, "node_modules/window-size": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", @@ -1740,8 +2193,7 @@ "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, "node_modules/xtend": { "version": "4.0.2", @@ -1751,6 +2203,11 @@ "node": ">=0.4" } }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, "node_modules/yargs": { "version": "3.10.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", @@ -1764,6 +2221,22 @@ } }, "dependencies": { + "@mapbox/node-pre-gyp": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.7.tgz", + "integrity": "sha512-PplSvl4pJ5N3BkVjAdDzpPhVUPdC73JgttkR+LnBx2OORC1GCQsBjUeEuipf9uOaAM1SbxcdZFfR3KDTKm2S0A==", + "requires": { + "detect-libc": "^1.0.3", + "https-proxy-agent": "^5.0.0", + "make-dir": "^3.1.0", + "node-fetch": "^2.6.5", + "nopt": "^5.0.0", + "npmlog": "^6.0.0", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.11" + } + }, "@types/babel-types": { "version": "7.0.4", "resolved": "https://registry.npmjs.org/@types/babel-types/-/babel-types-7.0.4.tgz", @@ -1777,6 +2250,11 @@ "@types/babel-types": "*" } }, + "abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + }, "accepts": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz", @@ -1806,6 +2284,29 @@ } } }, + "agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "requires": { + "debug": "4" + }, + "dependencies": { + "debug": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, "align-text": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", @@ -1821,6 +2322,37 @@ "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=" }, + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" + }, + "aproba": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==" + }, + "are-we-there-yet": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", + "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, "array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", @@ -1870,8 +2402,7 @@ "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" }, "basic-auth": { "version": "2.0.0", @@ -1881,6 +2412,15 @@ "safe-buffer": "5.1.1" } }, + "bcrypt": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-5.0.1.tgz", + "integrity": "sha512-9BTgmrhZM2t1bNuDtrtIMVSmmxZBrJ71n8Wg+YgdjHuIWYF7SjjmCPZFB+/5i/o/PIeRpwVJR3P+NrpIItUjqw==", + "requires": { + "@mapbox/node-pre-gyp": "^1.0.0", + "node-addon-api": "^3.1.0" + } + }, "body-parser": { "version": "1.18.2", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.2.tgz", @@ -1902,7 +2442,6 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -1946,6 +2485,11 @@ "is-regex": "^1.0.3" } }, + "chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==" + }, "clean-css": { "version": "3.4.28", "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-3.4.28.tgz", @@ -1965,6 +2509,11 @@ "wordwrap": "0.0.2" } }, + "color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==" + }, "combined-stream": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz", @@ -1991,8 +2540,12 @@ "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" }, "constantinople": { "version": "3.1.2", @@ -2070,6 +2623,11 @@ "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", "dev": true }, + "delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" + }, "depd": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", @@ -2080,6 +2638,11 @@ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" }, + "detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=" + }, "diff": { "version": "3.5.0", "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", @@ -2101,6 +2664,11 @@ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, "encodeurl": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", @@ -2243,17 +2811,40 @@ "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" }, + "fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "requires": { + "minipass": "^3.0.0" + } + }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, + "gauge": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.0.tgz", + "integrity": "sha512-F8sU45yQpjQjxKkm1UOAhf0U/O0aFt//Fl7hsrNVto+patMHjs7dPI9mFOGUKbhrgKm0S3EjW3scMFuQmWSROw==", + "requires": { + "ansi-regex": "^5.0.1", + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.2", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.1", + "signal-exit": "^3.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.2" + } + }, "glob": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", @@ -2293,6 +2884,11 @@ "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", "dev": true }, + "has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" + }, "he": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", @@ -2310,6 +2906,30 @@ "statuses": ">= 1.4.0 < 2" } }, + "https-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", + "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", + "requires": { + "agent-base": "6", + "debug": "4" + }, + "dependencies": { + "debug": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, "iconv-lite": { "version": "0.4.19", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", @@ -2319,7 +2939,6 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, "requires": { "once": "^1.3.0", "wrappy": "1" @@ -2356,6 +2975,11 @@ } } }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" + }, "is-promise": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", @@ -2412,6 +3036,29 @@ "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=" }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "requires": { + "yallist": "^4.0.0" + } + }, + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "requires": { + "semver": "^6.0.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + } + } + }, "media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -2449,7 +3096,6 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, "requires": { "brace-expansion": "^1.1.7" } @@ -2460,6 +3106,23 @@ "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", "dev": true }, + "minipass": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.5.tgz", + "integrity": "sha512-+8NzxD82XQoNKNrl1d/FSi+X8wAEWR+sbYAfIvub4Nz0d22plFG72CEVVaufV8PNf4qSslFTD8VMOxNVhHCjTw==", + "requires": { + "yallist": "^4.0.0" + } + }, + "minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "requires": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + } + }, "mkdirp": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", @@ -2527,11 +3190,43 @@ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=" }, + "node-addon-api": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz", + "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==" + }, + "node-fetch": { + "version": "2.6.6", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.6.tgz", + "integrity": "sha512-Z8/6vRlTUChSdIgMa51jxQ4lrw/Jy5SOW10ObaA47/RElsAN2c5Pn8bTgFGWn/ibwzXTE8qwr1Yzx28vsecXEA==", + "requires": { + "whatwg-url": "^5.0.0" + } + }, "nodemailer": { "version": "6.6.5", "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.6.5.tgz", "integrity": "sha512-C/v856DBijUzHcHIgGpQoTrfsH3suKIRAGliIzCstatM2cAa+MYX3LuyCrABiO/cdJTxgBBHXxV1ztiqUwst5A==" }, + "nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "requires": { + "abbrev": "1" + } + }, + "npmlog": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.0.tgz", + "integrity": "sha512-03ppFRGlsyUaQFbGC2C8QWJN/C/K7PsfyD9aQdhVKAQIH4sQBc8WASqFBP7O+Ut4d2oo5LoeoboB3cGdBZSp6Q==", + "requires": { + "are-we-there-yet": "^2.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^4.0.0", + "set-blocking": "^2.0.0" + } + }, "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -2554,7 +3249,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, "requires": { "wrappy": "1" } @@ -2586,8 +3280,7 @@ "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" }, "path-parse": { "version": "1.0.5", @@ -2909,11 +3602,42 @@ "align-text": "^0.1.1" } }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "requires": { + "glob": "^7.1.3" + }, + "dependencies": { + "glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "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" + } + } + } + }, "safe-buffer": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "requires": { + "lru-cache": "^6.0.0" + } + }, "send": { "version": "0.16.2", "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", @@ -2945,11 +3669,21 @@ "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=" + }, "setprototypeof": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" }, + "signal-exit": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.6.tgz", + "integrity": "sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ==" + }, "source-map": { "version": "0.4.4", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", @@ -2991,6 +3725,24 @@ "safe-buffer": "~5.1.0" } }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "requires": { + "ansi-regex": "^5.0.1" + } + }, "superagent": { "version": "3.8.2", "resolved": "https://registry.npmjs.org/superagent/-/superagent-3.8.2.tgz", @@ -3039,6 +3791,26 @@ "has-flag": "^3.0.0" } }, + "tar": { + "version": "6.1.11", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz", + "integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==", + "requires": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^3.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "dependencies": { + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" + } + } + }, "to-fast-properties": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", @@ -3049,6 +3821,11 @@ "resolved": "https://registry.npmjs.org/token-stream/-/token-stream-0.0.1.tgz", "integrity": "sha1-zu78cXp2xDFvEm0LnbqlXX598Bo=" }, + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" + }, "type-is": { "version": "1.6.16", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz", @@ -3114,6 +3891,28 @@ "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", "integrity": "sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=" }, + "webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=" + }, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "requires": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, "window-size": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", @@ -3136,14 +3935,18 @@ "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, "xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, "yargs": { "version": "3.10.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", diff --git a/package.json b/package.json index eb6d387..79eeb84 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ }, "dependencies": { "async": "^3.2.2", + "bcrypt": "^5.0.1", "cookie-parser": "~1.4.3", "debug": "~2.6.9", "dotenv": "^10.0.0", From 7a62885e8f908970c29d85b078db026bdf9a93c4 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Wed, 24 Nov 2021 17:16:46 -0700 Subject: [PATCH 04/35] Install passport-local --- package-lock.json | 20 ++++++++++++++++++++ package.json | 1 + 2 files changed, 21 insertions(+) diff --git a/package-lock.json b/package-lock.json index b41b189..c402529 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,6 +19,7 @@ "morgan": "~1.9.0", "nodemailer": "^6.6.5", "passport": "^0.5.0", + "passport-local": "^1.0.0", "pg": "^8.7.1", "pug": "2.0.0-beta11" }, @@ -1383,6 +1384,17 @@ "url": "https://github.com/sponsors/jaredhanson" } }, + "node_modules/passport-local": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/passport-local/-/passport-local-1.0.0.tgz", + "integrity": "sha1-H+YyaMkudWBmJkN+O5BmYsFbpu4=", + "dependencies": { + "passport-strategy": "1.x.x" + }, + "engines": { + "node": ">= 0.4.0" + } + }, "node_modules/passport-strategy": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz", @@ -3272,6 +3284,14 @@ "pause": "0.0.1" } }, + "passport-local": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/passport-local/-/passport-local-1.0.0.tgz", + "integrity": "sha1-H+YyaMkudWBmJkN+O5BmYsFbpu4=", + "requires": { + "passport-strategy": "1.x.x" + } + }, "passport-strategy": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz", diff --git a/package.json b/package.json index 79eeb84..2959fbd 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "morgan": "~1.9.0", "nodemailer": "^6.6.5", "passport": "^0.5.0", + "passport-local": "^1.0.0", "pg": "^8.7.1", "pug": "2.0.0-beta11" }, From 33617a82ecea0c55286986d4093b5f082cdddcab Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Wed, 24 Nov 2021 17:56:04 -0700 Subject: [PATCH 05/35] Add passport functions --- app.js | 22 +++++++++++++++++++++ database/accounts/accounts.js | 37 +++++++++++++++++++++++++++++++++++ database/accounts/random.js | 12 ++++++++++++ routes/accounts.js | 11 +++++++++++ 4 files changed, 82 insertions(+) create mode 100644 database/accounts/accounts.js create mode 100644 database/accounts/random.js create mode 100644 routes/accounts.js diff --git a/app.js b/app.js index c773280..0ad96f4 100644 --- a/app.js +++ b/app.js @@ -3,6 +3,11 @@ var express = require('express'); var path = require('path'); var cookieParser = require('cookie-parser'); var logger = require('morgan'); +var random = require('./database/accounts/random'); +const passport = require('passport'); +const session = require('express-session'); +const accounts = require('./database/accounts/accounts'); + var indexRouter = require('./routes/index'); var usersRouter = require('./routes/users'); @@ -11,6 +16,23 @@ var manageRouter = require('./routes/manage'); var app = express(); +// session setup +app.use( + session({ + secret: random.makeid(20), + resave: false, + saveUninitialized: true, + }) +); + +// passport setup +app.use(passport.initialize()); +app.use(passport.session()); + +//passport.use(accounts.createStrategy()); +//passport.serializeUser(accounts.serializeUser()); +//passport.deserializeUser(accounts.deserializeUser()); + // view engine setup app.set('views', path.join(__dirname, 'views')); app.set('view engine', 'pug'); diff --git a/database/accounts/accounts.js b/database/accounts/accounts.js new file mode 100644 index 0000000..67d277a --- /dev/null +++ b/database/accounts/accounts.js @@ -0,0 +1,37 @@ +const database = require('./../database'); +const passport = require('passport'); +const passportLocal = require('passport-local'); + +passport.use(new passportLocal.Strategy((email, password, cb) => { + query = `SELECT id, email, password, admin + FROM accounts.users + WHERE email = $1`; + const result = database.executeQuery(query, [email]); + if(result.length > 0) { + const first = result[0]; + bcrypt.compare(password, first[2], function(err, res) { + if(res) { + cb(null, { id: first[0], email: first[1], admin: first[3] }) + } + else + { + cb(null, false) + } + }) + } else { + cb(null, false) + } +})); + +passport.serializeUser((user, done) => { + done(null, user.id) +}) + +passport.deserializeUser((id, cb) => { + query = `SELECT id, email, admin + FROM accounts.users + WHERE id = $1`; + const result = database.executeQuery(query, [parseInt(id, 10)]); + + cb(null, result[0]); +}); \ No newline at end of file diff --git a/database/accounts/random.js b/database/accounts/random.js new file mode 100644 index 0000000..6d6a57d --- /dev/null +++ b/database/accounts/random.js @@ -0,0 +1,12 @@ +function makeid(length) { + var result = ''; + var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + var charactersLength = characters.length; + for ( var i = 0; i < length; i++ ) { + result += characters.charAt(Math.floor(Math.random() * + charactersLength)); + } + return result; +} + +exports.makeid = makeid; \ No newline at end of file diff --git a/routes/accounts.js b/routes/accounts.js new file mode 100644 index 0000000..5a72c2f --- /dev/null +++ b/routes/accounts.js @@ -0,0 +1,11 @@ +const passport = require('passport'); +var router = express.Router(); +const app = require('../app'); + +router.post('/login', passport.authenticate('local'), (req, res, next) => { + const { user } = req; + + res.json(user); +}); + +module.exports = router; \ No newline at end of file From c5f05eebff4da9a8eecad0d955f6e266d32514d8 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Wed, 24 Nov 2021 17:58:28 -0700 Subject: [PATCH 06/35] Add auth router to app.js --- app.js | 2 ++ routes/{accounts.js => auth.js} | 0 2 files changed, 2 insertions(+) rename routes/{accounts.js => auth.js} (100%) diff --git a/app.js b/app.js index 0ad96f4..f657970 100644 --- a/app.js +++ b/app.js @@ -13,6 +13,7 @@ var indexRouter = require('./routes/index'); var usersRouter = require('./routes/users'); var dataRouter = require('./routes/data'); var manageRouter = require('./routes/manage'); +var authRouter = require('./routes/auth'); var app = express(); @@ -47,6 +48,7 @@ app.use('/', indexRouter); app.use('/users', usersRouter); app.use('/data', dataRouter); app.use('/manage', manageRouter); +app.user('/auth', authRouter); // catch 404 and forward to error handler diff --git a/routes/accounts.js b/routes/auth.js similarity index 100% rename from routes/accounts.js rename to routes/auth.js From 7520580d66e5a136203ade7e7f928b33d7696764 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Wed, 24 Nov 2021 18:13:33 -0700 Subject: [PATCH 07/35] Add function to create user --- app.js | 2 +- database/accounts/accounts.js | 52 ++++++++++++++++++++++------------- database/database.js | 1 - 3 files changed, 34 insertions(+), 21 deletions(-) diff --git a/app.js b/app.js index f657970..f8cb47b 100644 --- a/app.js +++ b/app.js @@ -48,7 +48,7 @@ app.use('/', indexRouter); app.use('/users', usersRouter); app.use('/data', dataRouter); app.use('/manage', manageRouter); -app.user('/auth', authRouter); +app.use('/auth', authRouter); // catch 404 and forward to error handler diff --git a/database/accounts/accounts.js b/database/accounts/accounts.js index 67d277a..d65de3c 100644 --- a/database/accounts/accounts.js +++ b/database/accounts/accounts.js @@ -1,26 +1,28 @@ const database = require('./../database'); const passport = require('passport'); const passportLocal = require('passport-local'); +const bcrypt = require('bcrypt'); passport.use(new passportLocal.Strategy((email, password, cb) => { - query = `SELECT id, email, password, admin + query = `SELECT user_id, email, password, admin FROM accounts.users WHERE email = $1`; - const result = database.executeQuery(query, [email]); - if(result.length > 0) { - const first = result[0]; - bcrypt.compare(password, first[2], function(err, res) { - if(res) { - cb(null, { id: first[0], email: first[1], admin: first[3] }) - } - else - { + database.executeQuery(query, [email]) + .then(result => { + if(result.length > 0) { + const first = result[0]; + const matches = bcrypt.compareSync(password, first[2]); + if(matches) { + cb(null, { id: first[0], email: first[1], admin: first[3] }) + } + else + { + cb(null, false) + } + } else { cb(null, false) } - }) - } else { - cb(null, false) - } + }); })); passport.serializeUser((user, done) => { @@ -28,10 +30,22 @@ passport.serializeUser((user, done) => { }) passport.deserializeUser((id, cb) => { - query = `SELECT id, email, admin + query = `SELECT user_id, email, admin FROM accounts.users WHERE id = $1`; - const result = database.executeQuery(query, [parseInt(id, 10)]); - - cb(null, result[0]); -}); \ No newline at end of file + database.executeQuery(query, [parseInt(id, 10)]) + .then(result => { + cb(null, result[0]); + }); +}); + + + +async function createUser(email, password) { + const salt = bcrypt.genSaltSync(); + const hash = bcrypt.hashSync(password, salt); + + const query = `INSERT INTO accounts.users(email, password) + VALUES($1, $2)`; + await database.executeQuery(query, [email, hash]); +} \ No newline at end of file diff --git a/database/database.js b/database/database.js index f166980..1d5bc56 100644 --- a/database/database.js +++ b/database/database.js @@ -27,7 +27,6 @@ async function Initialize() { - async function checkForDatabaseInitialization() { const query = `SELECT schema_name FROM information_schema.schemata WHERE schema_name = 'scores'`; let result = await executeQuery(query); From 48c2ee67bd9b5cc9e7292c382bb172606e226c40 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Wed, 24 Nov 2021 18:25:27 -0700 Subject: [PATCH 08/35] Add page to create user --- app.js | 2 ++ database/accounts/accounts.js | 4 +++- routes/admin.js | 12 ++++++++++++ routes/auth.js | 9 ++++++++- views/accounts/createuser.pug | 20 ++++++++++++++++++++ 5 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 routes/admin.js create mode 100644 views/accounts/createuser.pug diff --git a/app.js b/app.js index f8cb47b..3efd585 100644 --- a/app.js +++ b/app.js @@ -14,6 +14,7 @@ var usersRouter = require('./routes/users'); var dataRouter = require('./routes/data'); var manageRouter = require('./routes/manage'); var authRouter = require('./routes/auth'); +var adminRouter = require('./routes/admin'); var app = express(); @@ -49,6 +50,7 @@ app.use('/users', usersRouter); app.use('/data', dataRouter); app.use('/manage', manageRouter); app.use('/auth', authRouter); +app.use('/admin', adminRouter); // catch 404 and forward to error handler diff --git a/database/accounts/accounts.js b/database/accounts/accounts.js index d65de3c..5ae6cc9 100644 --- a/database/accounts/accounts.js +++ b/database/accounts/accounts.js @@ -48,4 +48,6 @@ async function createUser(email, password) { const query = `INSERT INTO accounts.users(email, password) VALUES($1, $2)`; await database.executeQuery(query, [email, hash]); -} \ No newline at end of file +} + +exports.createUser = createUser; \ No newline at end of file diff --git a/routes/admin.js b/routes/admin.js new file mode 100644 index 0000000..d032cd1 --- /dev/null +++ b/routes/admin.js @@ -0,0 +1,12 @@ +var express = require('express'); +var router = express.Router(); +const passport = require('passport'); +const app = require('../app'); +const accounts = require('./../database/accounts/accounts'); + + +router.get('/createuser', (req, res, next) => { + res.render('accounts/createuser', { title: 'Create user' }); +}); + +module.exports = router; \ No newline at end of file diff --git a/routes/auth.js b/routes/auth.js index 5a72c2f..38426ea 100644 --- a/routes/auth.js +++ b/routes/auth.js @@ -1,5 +1,7 @@ -const passport = require('passport'); +var express = require('express'); var router = express.Router(); +const passport = require('passport'); +const accounts = require('./../database/accounts/accounts'); const app = require('../app'); router.post('/login', passport.authenticate('local'), (req, res, next) => { @@ -8,4 +10,9 @@ router.post('/login', passport.authenticate('local'), (req, res, next) => { res.json(user); }); +router.post('/register', (req, res, next) => { + accounts.createUser(req.body.email, req.body.password) + .then(res.redirect('/')); +}); + module.exports = router; \ No newline at end of file diff --git a/views/accounts/createuser.pug b/views/accounts/createuser.pug new file mode 100644 index 0000000..c5630c8 --- /dev/null +++ b/views/accounts/createuser.pug @@ -0,0 +1,20 @@ +extends ../layout + +block stylesheets + link(rel='stylesheet', href='/stylesheets/submit.css') + link(rel='stylesheet', href='/stylesheets/form.css') + +block content + div#mobile-view + h1 #{title} + form(action='/auth/register', method='POST') + span(class='form-section') + label Email + span(class='form-section-input') + input(type="email", name="email") + span(class='form-section') + label Password + span(class='form-section-input') + input(type="password", name="password") + span(class='form-section') + button#submit-button(type="submit") Submit \ No newline at end of file From 0a6c56e103d94f45a9260f334e537f5125c24901 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Wed, 24 Nov 2021 20:36:00 -0700 Subject: [PATCH 09/35] Fix issues involving editing and submitting games --- public/scripts/manage/game.js | 2 ++ routes/manage.js | 2 +- views/manage/addgame.pug | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/public/scripts/manage/game.js b/public/scripts/manage/game.js index 0cc134b..82d4cb8 100644 --- a/public/scripts/manage/game.js +++ b/public/scripts/manage/game.js @@ -25,6 +25,8 @@ async function initializeForm() { const game = await Data.getGame(gameID); + Form.addHiddenValue('game', gameID, submissionForm); + Form.populateSeasons(seasonDropdown, game.seasonID); Data.getDivision(game.divisionID) .then(data => { diff --git a/routes/manage.js b/routes/manage.js index 9757e69..16f8ca4 100644 --- a/routes/manage.js +++ b/routes/manage.js @@ -35,7 +35,7 @@ router.post('/game', function(req, res, next) { if(remove) games.remove(id) .then(res.redirect("/manage")); - else if(id) games.edit(id, divisionId, seasonID, date, team1ID, team2ID, team1Score, team2Score) + else if(id) games.edit(id, divisionID, seasonID, date, team1ID, team2ID, team1Score, team2Score) .then(res.redirect('/manage')); else games.add(divisionID, seasonID, date, team1ID, team2ID, team1Score, team2Score) .then(res.redirect("/manage")); diff --git a/views/manage/addgame.pug b/views/manage/addgame.pug index 039bfd1..7981148 100644 --- a/views/manage/addgame.pug +++ b/views/manage/addgame.pug @@ -7,7 +7,7 @@ block stylesheets block content div#mobile-view h1 #{title} - form#submission-form(action='./submitgame', method='POST') + form#submission-form(action='./game', method='POST') span(class='form-section') label Year span(class='form-section-input') From 9c7241e7e6c0b58e0eb9748b42059755241879ca Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Wed, 24 Nov 2021 20:46:16 -0700 Subject: [PATCH 10/35] Remove console.log from teams.js --- database/scores/teams.js | 1 - 1 file changed, 1 deletion(-) diff --git a/database/scores/teams.js b/database/scores/teams.js index 592796f..20034ef 100644 --- a/database/scores/teams.js +++ b/database/scores/teams.js @@ -69,7 +69,6 @@ async function getFromID(id) { FROM scores.teams WHERE team_id = $1;`; const row = (await database.executeQuery(query, [id]))[0]; - console.log(row); return new Team(id, row[0], row[1]); } From a6fe6d6c7280d639ed9284c381b05c88e1102a1d Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Wed, 24 Nov 2021 20:58:49 -0700 Subject: [PATCH 11/35] Create login page --- database/accounts/accounts.js | 21 +++++++++++++-------- routes/auth.js | 14 ++++++++++---- views/accounts/login.pug | 20 ++++++++++++++++++++ 3 files changed, 43 insertions(+), 12 deletions(-) create mode 100644 views/accounts/login.pug diff --git a/database/accounts/accounts.js b/database/accounts/accounts.js index 5ae6cc9..6f000c9 100644 --- a/database/accounts/accounts.js +++ b/database/accounts/accounts.js @@ -1,26 +1,30 @@ const database = require('./../database'); const passport = require('passport'); -const passportLocal = require('passport-local'); +const localStrategy = require('passport-local').Strategy; const bcrypt = require('bcrypt'); -passport.use(new passportLocal.Strategy((email, password, cb) => { + +passport.use(new localStrategy({ + usernameField: 'email', + passwordField: 'password'}, + (username, password, cb) => { query = `SELECT user_id, email, password, admin FROM accounts.users WHERE email = $1`; - database.executeQuery(query, [email]) + database.executeQuery(query, [username]) .then(result => { if(result.length > 0) { const first = result[0]; const matches = bcrypt.compareSync(password, first[2]); if(matches) { - cb(null, { id: first[0], email: first[1], admin: first[3] }) + return cb(null, { id: first[0], email: first[1], admin: first[3] }) } else { - cb(null, false) + return cb(null, false) } } else { - cb(null, false) + return cb(null, false) } }); })); @@ -32,7 +36,7 @@ passport.serializeUser((user, done) => { passport.deserializeUser((id, cb) => { query = `SELECT user_id, email, admin FROM accounts.users - WHERE id = $1`; + WHERE user_id = $1`; database.executeQuery(query, [parseInt(id, 10)]) .then(result => { cb(null, result[0]); @@ -50,4 +54,5 @@ async function createUser(email, password) { await database.executeQuery(query, [email, hash]); } -exports.createUser = createUser; \ No newline at end of file +exports.createUser = createUser; +exports.passport = passport; \ No newline at end of file diff --git a/routes/auth.js b/routes/auth.js index 38426ea..026728c 100644 --- a/routes/auth.js +++ b/routes/auth.js @@ -1,13 +1,19 @@ var express = require('express'); var router = express.Router(); -const passport = require('passport'); const accounts = require('./../database/accounts/accounts'); const app = require('../app'); -router.post('/login', passport.authenticate('local'), (req, res, next) => { - const { user } = req; +router.get('/login', (req, res, next) => { + res.render('accounts/login', { title : "Login" }); +}); - res.json(user); +router.post('/login', + accounts.passport.authenticate('local', { + failureRedirect: '/fail', + successRedirect: '/success', + }), + (req, res, next) => { + console.log(req.user); }); router.post('/register', (req, res, next) => { diff --git a/views/accounts/login.pug b/views/accounts/login.pug new file mode 100644 index 0000000..fb58202 --- /dev/null +++ b/views/accounts/login.pug @@ -0,0 +1,20 @@ +extends ../layout + +block stylesheets + link(rel='stylesheet', href='/stylesheets/submit.css') + link(rel='stylesheet', href='/stylesheets/form.css') + +block content + div#mobile-view + h1 #{title} + form(action='/auth/login', method='POST') + span(class='form-section') + label Email + span(class='form-section-input') + input(type="email", name="email") + span(class='form-section') + label Password + span(class='form-section-input') + input(type="password", name="password") + span(class='form-section') + button#submit-button(type="submit") Submit \ No newline at end of file From 8e8f0e3e7ced5b82b3926e3c8472a682386cd423 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Wed, 24 Nov 2021 21:05:33 -0700 Subject: [PATCH 12/35] Change passport require --- routes/auth.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/routes/auth.js b/routes/auth.js index 026728c..53242d5 100644 --- a/routes/auth.js +++ b/routes/auth.js @@ -1,5 +1,6 @@ var express = require('express'); var router = express.Router(); +const passport = require('passport'); const accounts = require('./../database/accounts/accounts'); const app = require('../app'); @@ -8,7 +9,7 @@ router.get('/login', (req, res, next) => { }); router.post('/login', - accounts.passport.authenticate('local', { + passport.authenticate('local', { failureRedirect: '/fail', successRedirect: '/success', }), From ebef80e061a11777fd48d304dc5a2a5d37003fa3 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Wed, 24 Nov 2021 21:07:13 -0700 Subject: [PATCH 13/35] Add logout function --- routes/auth.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/routes/auth.js b/routes/auth.js index 53242d5..9b400e5 100644 --- a/routes/auth.js +++ b/routes/auth.js @@ -8,6 +8,11 @@ router.get('/login', (req, res, next) => { res.render('accounts/login', { title : "Login" }); }); +router.get('/logout', (req, res, next) => { + req.logout(); + res.redirect("/"); +}); + router.post('/login', passport.authenticate('local', { failureRedirect: '/fail', From 24197d9bffb9b9dd8881b8ca110b12851175f6cb Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Wed, 24 Nov 2021 21:22:17 -0700 Subject: [PATCH 14/35] Add invalid email or password message --- app.js | 4 ++++ package-lock.json | 14 ++++++++++++++ package.json | 1 + routes/auth.js | 7 ++++--- views/accounts/login.pug | 1 + 5 files changed, 24 insertions(+), 3 deletions(-) diff --git a/app.js b/app.js index 3efd585..a379166 100644 --- a/app.js +++ b/app.js @@ -7,6 +7,7 @@ var random = require('./database/accounts/random'); const passport = require('passport'); const session = require('express-session'); const accounts = require('./database/accounts/accounts'); +var flash = require('connect-flash'); var indexRouter = require('./routes/index'); @@ -18,6 +19,9 @@ var adminRouter = require('./routes/admin'); var app = express(); +// flash setup +app.use(flash()); + // session setup app.use( session({ diff --git a/package-lock.json b/package-lock.json index c402529..1903fa7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "dependencies": { "async": "^3.2.2", "bcrypt": "^5.0.1", + "connect-flash": "^0.1.1", "cookie-parser": "~1.4.3", "debug": "~2.6.9", "dotenv": "^10.0.0", @@ -430,6 +431,14 @@ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, + "node_modules/connect-flash": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/connect-flash/-/connect-flash-0.1.1.tgz", + "integrity": "sha1-2GMPJtlaf4UfmVax6MxnMvO2qjA=", + "engines": { + "node": ">= 0.4.0" + } + }, "node_modules/console-control-strings": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", @@ -2554,6 +2563,11 @@ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, + "connect-flash": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/connect-flash/-/connect-flash-0.1.1.tgz", + "integrity": "sha1-2GMPJtlaf4UfmVax6MxnMvO2qjA=" + }, "console-control-strings": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", diff --git a/package.json b/package.json index 2959fbd..c3c687d 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "dependencies": { "async": "^3.2.2", "bcrypt": "^5.0.1", + "connect-flash": "^0.1.1", "cookie-parser": "~1.4.3", "debug": "~2.6.9", "dotenv": "^10.0.0", diff --git a/routes/auth.js b/routes/auth.js index 9b400e5..5d2e910 100644 --- a/routes/auth.js +++ b/routes/auth.js @@ -5,7 +5,7 @@ const accounts = require('./../database/accounts/accounts'); const app = require('../app'); router.get('/login', (req, res, next) => { - res.render('accounts/login', { title : "Login" }); + res.render('accounts/login', { title : "Login", message: req.flash('error') }); }); router.get('/logout', (req, res, next) => { @@ -15,8 +15,9 @@ router.get('/logout', (req, res, next) => { router.post('/login', passport.authenticate('local', { - failureRedirect: '/fail', - successRedirect: '/success', + failureRedirect: '/auth/login', + successRedirect: '/', + failureFlash: "Invalid email or password.", }), (req, res, next) => { console.log(req.user); diff --git a/views/accounts/login.pug b/views/accounts/login.pug index fb58202..13f707e 100644 --- a/views/accounts/login.pug +++ b/views/accounts/login.pug @@ -16,5 +16,6 @@ block content label Password span(class='form-section-input') input(type="password", name="password") + .error #{message} span(class='form-section') button#submit-button(type="submit") Submit \ No newline at end of file From 4828ef2c7c77fa27542e83bff753bd093a285ea1 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Wed, 24 Nov 2021 21:40:33 -0700 Subject: [PATCH 15/35] Require admin for certain pages --- database/accounts/accounts.js | 8 +++---- routes/manage.js | 40 +++++++++++++++++++++++++---------- 2 files changed, 33 insertions(+), 15 deletions(-) diff --git a/database/accounts/accounts.js b/database/accounts/accounts.js index 6f000c9..6fc0d62 100644 --- a/database/accounts/accounts.js +++ b/database/accounts/accounts.js @@ -45,13 +45,13 @@ passport.deserializeUser((id, cb) => { -async function createUser(email, password) { +async function createUser(email, password, isAdmin) { const salt = bcrypt.genSaltSync(); const hash = bcrypt.hashSync(password, salt); - const query = `INSERT INTO accounts.users(email, password) - VALUES($1, $2)`; - await database.executeQuery(query, [email, hash]); + const query = `INSERT INTO accounts.users(email, password, admin) + VALUES($1, $2, $3)`; + await database.executeQuery(query, [email, hash, isAdmin]); } exports.createUser = createUser; diff --git a/routes/manage.js b/routes/manage.js index 16f8ca4..7fbd517 100644 --- a/routes/manage.js +++ b/routes/manage.js @@ -8,18 +8,36 @@ var divisions = require('../database/scores/divisions'); var genders = require('../database/scores/genders'); var teams = require('../database/scores/teams'); +function userLoggedIn(req, res, next) { + if (req.user) { + next(); + } + else { + res.redirect('/auth/login'); + } +} -router.get('/', function(req, res, next) { +function adminLoggedIn(req, res, next) { + if (req.user && req.user[2]) { + next(); + } + else { + res.send('UNAUTHORIZED'); + } +} + + +router.get('/' ,userLoggedIn, function(req, res, next) { res.render('manage', { title: 'Score Management' }); }); -router.get('/game', function(req, res, next) { +router.get('/game', userLoggedIn, function(req, res, next) { let title = req.query.game ? 'Edit Game' : 'Submit Score' res.render('manage/addgame', { title }); }); -router.post('/game', function(req, res, next) { +router.post('/game', userLoggedIn, function(req, res, next) { const seasonID = req.body['year']; const sportID = req.body['sport']; const gender = (req.body['gender'] == "female") ? genders.FEMALE : genders.MALE; @@ -41,11 +59,11 @@ router.post('/game', function(req, res, next) { .then(res.redirect("/manage")); }); -router.get('/season', function(req, res, next) { +router.get('/season', adminLoggedIn, function(req, res, next) { res.render('manage/addseason', { title: 'Add Season', currentYear : (new Date()).getFullYear() }); }); -router.post('/season', function(req, res, next) { +router.post('/season', adminLoggedIn, function(req, res, next) { const year = req.body['year']; const seasonID = req.body['season']; @@ -55,11 +73,11 @@ router.post('/season', function(req, res, next) { else seasons.add(year).then(res.redirect("/manage")); }); -router.get('/sport', function(req, res, next) { +router.get('/sport', adminLoggedIn, function(req, res, next) { res.render('manage/addsport', { title: 'Add Sport' }); }); -router.post('/sport', function(req, res, next) { +router.post('/sport', adminLoggedIn, function(req, res, next) { const name = req.body['name']; const id = req.body['sport']; const remove = req.body['remove']; @@ -69,13 +87,13 @@ router.post('/sport', function(req, res, next) { else sports.add(name).then(res.redirect('/manage')); }); -router.get('/division', function(req, res, next) { +router.get('/division', adminLoggedIn, function(req, res, next) { let title = req.query.division ? 'Edit Division' : 'Add Division' res.render('manage/adddivision', { title }); }); -router.post('/division', function(req, res, next) { +router.post('/division', adminLoggedIn, function(req, res, next) { const name = req.body['name']; const sport = req.body['sport']; const genderName = req.body['gender']; @@ -100,13 +118,13 @@ router.post('/division', function(req, res, next) { } }); -router.get('/team', function(req, res, next) { +router.get('/team', adminLoggedIn, function(req, res, next) { let title = req.query.team ? 'Edit Team' : 'Add Team' res.render('manage/addteam', { title }); }); -router.post('/team', function(req, res, next) { +router.post('/team', adminLoggedIn, function(req, res, next) { const name = req.body['name']; const sport = req.body['sport']; From 173c075aa335d00e11432b60d9c2b4b0b58f1904 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Wed, 24 Nov 2021 22:29:29 -0700 Subject: [PATCH 16/35] Add ability to create user from admin panel --- database/accounts/accounts.js | 22 ++++++++++++++++++ public/scripts/data.js | 6 +++++ public/scripts/manage.js | 43 +++++++++++++++++++++++++++++++++++ public/stylesheets/form.css | 9 ++++++++ routes/admin.js | 16 +++++++++++-- routes/auth.js | 17 +++++++++++--- routes/data.js | 15 ++++++++++++ routes/manage.js | 7 +++++- views/accounts/createuser.pug | 4 ++++ views/manage.pug | 1 + 10 files changed, 134 insertions(+), 6 deletions(-) diff --git a/database/accounts/accounts.js b/database/accounts/accounts.js index 6fc0d62..147cadd 100644 --- a/database/accounts/accounts.js +++ b/database/accounts/accounts.js @@ -3,6 +3,14 @@ const passport = require('passport'); const localStrategy = require('passport-local').Strategy; const bcrypt = require('bcrypt'); +class User { + constructor(id, email, isAdmin) { + this.id = id; + this.email = email; + this.isAdmin = isAdmin; + } +} + passport.use(new localStrategy({ usernameField: 'email', @@ -54,5 +62,19 @@ async function createUser(email, password, isAdmin) { await database.executeQuery(query, [email, hash, isAdmin]); } +async function retrieveAll() { + const query = `SELECT user_id, email, admin + FROM accounts.users + ORDER BY email;` + const table = await database.executeQuery(query); + + const accountsList = []; + table.forEach((row) => { + accountsList.push(new User(row[0], row[1], row[2])); + }); + return accountsList; +} + exports.createUser = createUser; +exports.retrieveAll = retrieveAll; exports.passport = passport; \ No newline at end of file diff --git a/public/scripts/data.js b/public/scripts/data.js index 873d4d6..2360aed 100644 --- a/public/scripts/data.js +++ b/public/scripts/data.js @@ -69,4 +69,10 @@ export async function getGame(gameID) { const response = await fetch(`/data/game?game=${gameID}`); const game = await response.json(); return game; +} + +export async function getAccounts() { + const response = await fetch(`/data/accounts`); + const accounts = await response.json(); + return accounts; } \ No newline at end of file diff --git a/public/scripts/manage.js b/public/scripts/manage.js index 8c89bfd..de4a6e2 100644 --- a/public/scripts/manage.js +++ b/public/scripts/manage.js @@ -294,6 +294,49 @@ CATEGORIES.push(new Category( } )); +CATEGORIES.push(new Category( + "accounts", + async function getAccounts() { + return await Data.getAccounts(); + }, + async function listAccountHeaders() { + const headerRow = document.createElement('tr'); + + const emailHeader = document.createElement('th'); + emailHeader.textContent = "Email"; + headerRow.appendChild(emailHeader); + + const spacerHeader = document.createElement('th'); + spacerHeader.classList.add('spacer-column'); + headerRow.appendChild(spacerHeader); + + const adminHeader = document.createElement('th'); + adminHeader.textContent = "Admin?"; + headerRow.appendChild(adminHeader); + + itemsListTable.appendChild(headerRow); + }, + function listAccount(account, row) { + const emailCell = document.createElement('td'); + emailCell.textContent = account.email; + row.appendChild(emailCell); + + const spacerCell = document.createElement('td'); + row.appendChild(spacerCell); + + const adminCell = document.createElement('td'); + adminCell.textContent = account.isAdmin; + row.appendChild(adminCell); + }, + async function addAccount() { + window.location.href = "/manage/account"; + }, + async function editAccount(id) { + window.location.href = `/manage/account?account=${id}`; + } +)); + + async function listItems(category) { diff --git a/public/stylesheets/form.css b/public/stylesheets/form.css index 21abe10..32123e7 100644 --- a/public/stylesheets/form.css +++ b/public/stylesheets/form.css @@ -35,4 +35,13 @@ form { #delete-button { visibility: hidden; + } + + .form-section-checkbox { + flex-direction: row; + align-items: center; + } + + #admin { + width: auto; } \ No newline at end of file diff --git a/routes/admin.js b/routes/admin.js index d032cd1..9e9cfc9 100644 --- a/routes/admin.js +++ b/routes/admin.js @@ -5,8 +5,20 @@ const app = require('../app'); const accounts = require('./../database/accounts/accounts'); -router.get('/createuser', (req, res, next) => { - res.render('accounts/createuser', { title: 'Create user' }); +function adminLoggedIn(req, res, next) { + if (req.user && req.user[2]) { + next(); + } + else { + req.flash('error', 'An admin account is required to access this page.'); + res.redirect('/auth/login'); + } + } + +router.get('/', adminLoggedIn, (req, res, next) => { + res.render }); + + module.exports = router; \ No newline at end of file diff --git a/routes/auth.js b/routes/auth.js index 5d2e910..47a9b09 100644 --- a/routes/auth.js +++ b/routes/auth.js @@ -4,6 +4,17 @@ const passport = require('passport'); const accounts = require('./../database/accounts/accounts'); const app = require('../app'); + +function adminLoggedIn(req, res, next) { + if (req.user && req.user[2]) { + next(); + } + else { + req.flash('error', 'An admin account is required to access this page.'); + res.redirect('/auth/login'); + } + } + router.get('/login', (req, res, next) => { res.render('accounts/login', { title : "Login", message: req.flash('error') }); }); @@ -23,9 +34,9 @@ router.post('/login', console.log(req.user); }); -router.post('/register', (req, res, next) => { - accounts.createUser(req.body.email, req.body.password) - .then(res.redirect('/')); +router.post('/register', adminLoggedIn, (req, res, next) => { + accounts.createUser(req.body.email, req.body.password, !!req.body.admin) + .then(res.redirect('/manage')); }); module.exports = router; \ No newline at end of file diff --git a/routes/data.js b/routes/data.js index 6ad845a..fcb4920 100644 --- a/routes/data.js +++ b/routes/data.js @@ -6,7 +6,17 @@ var genders = require('../database/scores/genders'); var divisions = require('../database/scores/divisions'); var teams = require('../database/scores/teams'); var games = require('../database/scores/games'); +var accounts = require('../database/accounts/accounts'); +function adminLoggedIn(req, res, next) { + if (req.user && req.user[2]) { + next(); + } + else { + req.flash('error', 'An admin account is required to access this page.'); + res.redirect('/auth/login'); + } + } router.get('/sports', function(req, res, next) { sports.retrieveAll() @@ -61,4 +71,9 @@ router.get('/game', function(req, res, next) { .then(data => res.json(data)); }) +router.get('/accounts', adminLoggedIn, function(req, res, next) { + accounts.retrieveAll() + .then(data => res.json(data)); +}) + module.exports = router; \ No newline at end of file diff --git a/routes/manage.js b/routes/manage.js index 7fbd517..756c58d 100644 --- a/routes/manage.js +++ b/routes/manage.js @@ -22,7 +22,8 @@ function adminLoggedIn(req, res, next) { next(); } else { - res.send('UNAUTHORIZED'); + req.flash('error', 'An admin account is required to access this page.'); + res.redirect('/auth/login'); } } @@ -136,4 +137,8 @@ router.post('/team', adminLoggedIn, function(req, res, next) { else teams.add(name, sport).then(res.redirect("/manage")); }); +router.get('/account', adminLoggedIn, (req, res, next) => { + res.render('accounts/createuser', { title: 'Create user' }); +}); + module.exports = router; diff --git a/views/accounts/createuser.pug b/views/accounts/createuser.pug index c5630c8..02560ec 100644 --- a/views/accounts/createuser.pug +++ b/views/accounts/createuser.pug @@ -16,5 +16,9 @@ block content label Password span(class='form-section-input') input(type="password", name="password") + span(class='form-section') + span(class='form-section-checkbox') + input#admin(type="checkbox", name="admin") + label(for="admin") Grant admin privileges span(class='form-section') button#submit-button(type="submit") Submit \ No newline at end of file diff --git a/views/manage.pug b/views/manage.pug index c43dcf9..454d1ed 100644 --- a/views/manage.pug +++ b/views/manage.pug @@ -17,6 +17,7 @@ block content option(value="divisions") Divisions option(value="teams") Teams option(value="games") Games + option(value="accounts") Accounts div h2#table-header table#items-list From 4d3b6989e4905f317b0bb293cd7467139f4c0efa Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Thu, 25 Nov 2021 12:04:18 -0700 Subject: [PATCH 17/35] Add function to get user information --- database/accounts/accounts.js | 10 ++++++++++ public/scripts/data.js | 6 ++++++ routes/data.js | 5 +++++ 3 files changed, 21 insertions(+) diff --git a/database/accounts/accounts.js b/database/accounts/accounts.js index 147cadd..ef0d040 100644 --- a/database/accounts/accounts.js +++ b/database/accounts/accounts.js @@ -75,6 +75,16 @@ async function retrieveAll() { return accountsList; } +async function getFromID(id) { + const query = `SELECT user_id, email, admin + FROM accounts.users + WHERE user_id = $1;`; + const row = (await database.executeQuery(query, [id]))[0]; + + return new User(id, row[1], row[2]); +} + exports.createUser = createUser; exports.retrieveAll = retrieveAll; +exports.getFromID = getFromID; exports.passport = passport; \ No newline at end of file diff --git a/public/scripts/data.js b/public/scripts/data.js index 2360aed..ae8dc7f 100644 --- a/public/scripts/data.js +++ b/public/scripts/data.js @@ -75,4 +75,10 @@ export async function getAccounts() { const response = await fetch(`/data/accounts`); const accounts = await response.json(); return accounts; +} + +export async function getAccount(accountID) { + const response = await fetch(`/data/account?account=${accountID}`); + const account = await response.json(); + return account; } \ No newline at end of file diff --git a/routes/data.js b/routes/data.js index fcb4920..d010bd1 100644 --- a/routes/data.js +++ b/routes/data.js @@ -76,4 +76,9 @@ router.get('/accounts', adminLoggedIn, function(req, res, next) { .then(data => res.json(data)); }) +router.get('/account', adminLoggedIn, function(req, res, next) { + accounts.getFromID(req.query.account) + .then(data => res.json(data)); +}) + module.exports = router; \ No newline at end of file From d43aba3da1e7cd270b75af4c1febaecc7e947597 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Thu, 25 Nov 2021 12:13:46 -0700 Subject: [PATCH 18/35] Remove console.log from teams.js --- public/scripts/manage/team.js | 1 - 1 file changed, 1 deletion(-) diff --git a/public/scripts/manage/team.js b/public/scripts/manage/team.js index d5d8291..10cd386 100644 --- a/public/scripts/manage/team.js +++ b/public/scripts/manage/team.js @@ -21,7 +21,6 @@ async function initializeForm() { Form.populateSports(sportDropdown, team.sportID); - console.log(team.sportID); Form.addHiddenValue('team', teamID, submissionForm); } else { From 1901f33851f1e37682a3e8d26028f09b7ca5fc7f Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Thu, 25 Nov 2021 12:17:53 -0700 Subject: [PATCH 19/35] Create client layout for managing users --- public/scripts/manage/account.js | 33 ++++++++++++++++++++++++++++++++ public/stylesheets/form.css | 2 +- routes/manage.js | 4 +++- views/accounts/createuser.pug | 19 +++++++++++------- 4 files changed, 49 insertions(+), 9 deletions(-) create mode 100644 public/scripts/manage/account.js diff --git a/public/scripts/manage/account.js b/public/scripts/manage/account.js new file mode 100644 index 0000000..6b51c2f --- /dev/null +++ b/public/scripts/manage/account.js @@ -0,0 +1,33 @@ +import * as Data from "../data.js"; +import * as Form from "../form.js"; + +const submissionForm = document.getElementById('submission-form'); +const emailTextbox = document.getElementById('email-textbox'); +const passwordTextbox = document.getElementById('password-textbox'); +const adminCheckbox = document.getElementById('admin-checkbox'); +const submitButton = document.getElementById('submit-button'); +const deleteButton = document.getElementById('delete-button'); + +async function Initialize() { + let params = new URLSearchParams(location.search); + let accountID = params.get('account'); + if(accountID) { + const account = await Data.getAccount(accountID); + console.log(account); + + emailTextbox.value = account.email; + + passwordTextbox.placeholder = "leave unchanged"; + + adminCheckbox.checked = account.isAdmin; + + Form.addHiddenValue('account', accountID, submissionForm); + + deleteButton.style.visibility = "visible"; + deleteButton.disabled = false; + } + emailTextbox.disabled = false; + passwordTextbox.disabled = false; + adminCheckbox.disabled = false; +} +Initialize(); \ No newline at end of file diff --git a/public/stylesheets/form.css b/public/stylesheets/form.css index 32123e7..389028c 100644 --- a/public/stylesheets/form.css +++ b/public/stylesheets/form.css @@ -42,6 +42,6 @@ form { align-items: center; } - #admin { + #admin-checkbox { width: auto; } \ No newline at end of file diff --git a/routes/manage.js b/routes/manage.js index 756c58d..e9d4377 100644 --- a/routes/manage.js +++ b/routes/manage.js @@ -138,7 +138,9 @@ router.post('/team', adminLoggedIn, function(req, res, next) { }); router.get('/account', adminLoggedIn, (req, res, next) => { - res.render('accounts/createuser', { title: 'Create user' }); + let title = req.query.account ? 'Manage User' : 'Create User' + + res.render('accounts/createuser', { title }); }); module.exports = router; diff --git a/views/accounts/createuser.pug b/views/accounts/createuser.pug index 02560ec..cc5324d 100644 --- a/views/accounts/createuser.pug +++ b/views/accounts/createuser.pug @@ -7,18 +7,23 @@ block stylesheets block content div#mobile-view h1 #{title} - form(action='/auth/register', method='POST') + form#submission-form(action='/auth/register', method='POST') span(class='form-section') label Email span(class='form-section-input') - input(type="email", name="email") + input#email-textbox(type="email", name="email" disabled) span(class='form-section') label Password - span(class='form-section-input') - input(type="password", name="password") + span(class='form-section-input' ) + input#password-textbox(type="password" name="password" disabled) span(class='form-section') span(class='form-section-checkbox') - input#admin(type="checkbox", name="admin") - label(for="admin") Grant admin privileges + input#admin-checkbox(type="checkbox" name="admin" disabled) + label(for="admin-checkbox") Grant admin privileges span(class='form-section') - button#submit-button(type="submit") Submit \ No newline at end of file + button#submit-button(type="submit" disabled) Submit + span(class='form-section') + button#delete-button(type="delete" disabled) Delete + +block scripts + script(src='/scripts/manage/account.js' type="module") \ No newline at end of file From 06765455f62ccba8c4743287d07fe0f7502e445c Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Thu, 25 Nov 2021 12:40:19 -0700 Subject: [PATCH 20/35] Add functionality to edit accounts --- database/accounts/accounts.js | 30 +++++++++++++++++++++++++++--- public/scripts/manage/account.js | 16 +++++++++++++++- routes/manage.js | 12 ++++++++++++ views/accounts/createuser.pug | 2 +- 4 files changed, 55 insertions(+), 5 deletions(-) diff --git a/database/accounts/accounts.js b/database/accounts/accounts.js index ef0d040..ce0be9f 100644 --- a/database/accounts/accounts.js +++ b/database/accounts/accounts.js @@ -52,16 +52,39 @@ passport.deserializeUser((id, cb) => { }); +async function generateHash(password) { + return bcrypt.hashSync(password, salt); +} -async function createUser(email, password, isAdmin) { +async function create(email, password, isAdmin) { const salt = bcrypt.genSaltSync(); - const hash = bcrypt.hashSync(password, salt); + const hash = await generateHash(password); const query = `INSERT INTO accounts.users(email, password, admin) VALUES($1, $2, $3)`; await database.executeQuery(query, [email, hash, isAdmin]); } +async function edit(id, email, password, isAdmin) { + if(password) { + const hash = await generateHash(password); + + const query = `UPDATE accounts.users + SET email = $2, + password = $3, + admin = $4 + WHERE user_id = $1;`; + await database.executeQuery(query, [id, email, hash, isAdmin]); + } else { + const query = `UPDATE accounts.users + SET email = $2, + admin = $3 + WHERE user_id = $1;`; + await database.executeQuery(query, [id, email, isAdmin]); + } + return new User(id, email, isAdmin); +} + async function retrieveAll() { const query = `SELECT user_id, email, admin FROM accounts.users @@ -84,7 +107,8 @@ async function getFromID(id) { return new User(id, row[1], row[2]); } -exports.createUser = createUser; +exports.create = create; +exports.edit = edit; exports.retrieveAll = retrieveAll; exports.getFromID = getFromID; exports.passport = passport; \ No newline at end of file diff --git a/public/scripts/manage/account.js b/public/scripts/manage/account.js index 6b51c2f..65c9f4e 100644 --- a/public/scripts/manage/account.js +++ b/public/scripts/manage/account.js @@ -27,7 +27,21 @@ async function Initialize() { deleteButton.disabled = false; } emailTextbox.disabled = false; + emailTextbox.addEventListener('keyup', checkDataValidity); passwordTextbox.disabled = false; + passwordTextbox.addEventListener('keyup', checkDataValidity); adminCheckbox.disabled = false; + checkDataValidity(); } -Initialize(); \ No newline at end of file +Initialize(); + +async function checkDataValidity() { + let dataIsValid = true; + + if(!passwordTextbox.value && !passwordTextbox.placeholder) dataIsValid = false; + if(!emailTextbox.value) dataIsValid = false; + + if(dataIsValid) submitButton.disabled = false; + else submitButton.disabled = true; +} + diff --git a/routes/manage.js b/routes/manage.js index e9d4377..9e038f0 100644 --- a/routes/manage.js +++ b/routes/manage.js @@ -7,6 +7,7 @@ var sports = require('../database/scores/sports'); var divisions = require('../database/scores/divisions'); var genders = require('../database/scores/genders'); var teams = require('../database/scores/teams'); +var accounts = require('../database/accounts/accounts'); function userLoggedIn(req, res, next) { if (req.user) { @@ -143,4 +144,15 @@ router.get('/account', adminLoggedIn, (req, res, next) => { res.render('accounts/createuser', { title }); }); +router.post('/account', adminLoggedIn, (req, res, next) => { + const email = req.body.email; + const password = req.body.password; + const isAdmin = !!req.body.admin; + + const accountID = req.body.account; + + if(accountID) accounts.edit(accountID, email, password, isAdmin).then(res.redirect('/manage')); + else accounts.create(req.body.email, req.body.password, !!req.body.admin).then(res.redirect('/manage')); +}); + module.exports = router; diff --git a/views/accounts/createuser.pug b/views/accounts/createuser.pug index cc5324d..36e0acb 100644 --- a/views/accounts/createuser.pug +++ b/views/accounts/createuser.pug @@ -7,7 +7,7 @@ block stylesheets block content div#mobile-view h1 #{title} - form#submission-form(action='/auth/register', method='POST') + form#submission-form(action='/manage/account', method='POST') span(class='form-section') label Email span(class='form-section-input') From 23e85b7af3109f5c7b9ef31a204626f390a4c901 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Thu, 25 Nov 2021 12:41:22 -0700 Subject: [PATCH 21/35] Fix bug in accounts.js --- database/accounts/accounts.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/database/accounts/accounts.js b/database/accounts/accounts.js index ce0be9f..869810e 100644 --- a/database/accounts/accounts.js +++ b/database/accounts/accounts.js @@ -53,11 +53,11 @@ passport.deserializeUser((id, cb) => { async function generateHash(password) { + const salt = bcrypt.genSaltSync(); return bcrypt.hashSync(password, salt); } async function create(email, password, isAdmin) { - const salt = bcrypt.genSaltSync(); const hash = await generateHash(password); const query = `INSERT INTO accounts.users(email, password, admin) From 08545bfb5cefb941e93caa051fb352ac927937ef Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Thu, 25 Nov 2021 12:49:31 -0700 Subject: [PATCH 22/35] Add functions to remove account --- database/accounts/accounts.js | 9 +++++++++ public/scripts/manage/account.js | 1 + routes/manage.js | 2 ++ 3 files changed, 12 insertions(+) diff --git a/database/accounts/accounts.js b/database/accounts/accounts.js index 869810e..a8bab35 100644 --- a/database/accounts/accounts.js +++ b/database/accounts/accounts.js @@ -85,6 +85,14 @@ async function edit(id, email, password, isAdmin) { return new User(id, email, isAdmin); } +async function remove(id) { + const query = `DELETE FROM accounts.users + WHERE user_id = $1 + RETURNING email, admin;`; + const row = (await database.executeQuery(query, [id]))[0]; + return new User(id, row[0], row[1]); +} + async function retrieveAll() { const query = `SELECT user_id, email, admin FROM accounts.users @@ -109,6 +117,7 @@ async function getFromID(id) { exports.create = create; exports.edit = edit; +exports.remove = remove; exports.retrieveAll = retrieveAll; exports.getFromID = getFromID; exports.passport = passport; \ No newline at end of file diff --git a/public/scripts/manage/account.js b/public/scripts/manage/account.js index 65c9f4e..0ab23a3 100644 --- a/public/scripts/manage/account.js +++ b/public/scripts/manage/account.js @@ -45,3 +45,4 @@ async function checkDataValidity() { else submitButton.disabled = true; } +Form.addRemoveFunction(deleteButton, submissionForm, "account"); \ No newline at end of file diff --git a/routes/manage.js b/routes/manage.js index 9e038f0..fe6f204 100644 --- a/routes/manage.js +++ b/routes/manage.js @@ -150,7 +150,9 @@ router.post('/account', adminLoggedIn, (req, res, next) => { const isAdmin = !!req.body.admin; const accountID = req.body.account; + const remove = req.body.remove; + if(remove) accounts.remove(accountID).then(res.redirect('/manage')); if(accountID) accounts.edit(accountID, email, password, isAdmin).then(res.redirect('/manage')); else accounts.create(req.body.email, req.body.password, !!req.body.admin).then(res.redirect('/manage')); }); From d5be13955a0194400eea33f77b2e4100d9fb876a Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Thu, 25 Nov 2021 12:55:54 -0700 Subject: [PATCH 23/35] Fix bug regarding deleting games --- routes/manage.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routes/manage.js b/routes/manage.js index fe6f204..c3202f1 100644 --- a/routes/manage.js +++ b/routes/manage.js @@ -51,7 +51,7 @@ router.post('/game', userLoggedIn, function(req, res, next) { const team2Score = req.body['team2-score']; const id = req.body['game']; - const remove = req.body['delete']; + const remove = req.body['remove']; if(remove) games.remove(id) .then(res.redirect("/manage")); From b7f032754bb27f718ef1933e0edc298bcc2f7c1d Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Thu, 25 Nov 2021 13:03:54 -0700 Subject: [PATCH 24/35] Fix bug in games.js where dropdowns are not being updated --- public/scripts/manage/game.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/public/scripts/manage/game.js b/public/scripts/manage/game.js index 82d4cb8..8338d4c 100644 --- a/public/scripts/manage/game.js +++ b/public/scripts/manage/game.js @@ -67,6 +67,19 @@ async function initializeForm() { team1ScoreTextbox.disabled = false; team2ScoreTextbox.disabled = false; submitButton.disabled = false; + + sportDropdown.onchange = () => { + Form.populateGenders(genderDropdown, sportDropdown.value) + .then(() => { + Form.populateDivisions(divisionDropdown, sportDropdown.value, genderDropdown.value); + }); + Form.populateTeams(team1Dropdown, sportDropdown.value); + Form.populateTeams(team2Dropdown, sportDropdown.value); + }; + + genderDropdown.onchange = () => { + Form.populateDivisions(divisionDropdown, sportDropdown.value, genderDropdown.value); + }; } initializeForm(); From 4a108f606b46832c61289aaea9a967e645d24abb Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Thu, 25 Nov 2021 15:41:10 -0700 Subject: [PATCH 25/35] Move register route from auth to manage --- routes/auth.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/routes/auth.js b/routes/auth.js index 47a9b09..25159ab 100644 --- a/routes/auth.js +++ b/routes/auth.js @@ -34,9 +34,6 @@ router.post('/login', console.log(req.user); }); -router.post('/register', adminLoggedIn, (req, res, next) => { - accounts.createUser(req.body.email, req.body.password, !!req.body.admin) - .then(res.redirect('/manage')); -}); + module.exports = router; \ No newline at end of file From d35188537540e7040cbbb1ac0f3ea9998038d7d1 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Thu, 25 Nov 2021 18:29:18 -0700 Subject: [PATCH 26/35] Add page with only scores for non-admins --- public/scripts/manage/manage-nonadmin.js | 127 +++++++++++++++++++++++ routes/manage.js | 3 +- views/manage/manage-nonadmin.pug | 16 +++ 3 files changed, 145 insertions(+), 1 deletion(-) create mode 100644 public/scripts/manage/manage-nonadmin.js create mode 100644 views/manage/manage-nonadmin.pug diff --git a/public/scripts/manage/manage-nonadmin.js b/public/scripts/manage/manage-nonadmin.js new file mode 100644 index 0000000..4fc4b02 --- /dev/null +++ b/public/scripts/manage/manage-nonadmin.js @@ -0,0 +1,127 @@ +import * as Data from "./../data.js"; + +const gamesListTable = document.getElementById('games-list'); +const addNewButton = document.getElementById('add-new-button'); + + +function getGenderLetter(genderName) { + return genderName == "female" ? "F" : "M"; +} + +async function listGameHeaders() { + const headerRow = document.createElement('tr'); + + const teamsHeader = document.createElement('th'); + teamsHeader.textContent = "Teams"; + headerRow.appendChild(teamsHeader); + + const scoreHeader = document.createElement('th'); + headerRow.appendChild(scoreHeader); + + const spacerHeader = document.createElement('th'); + spacerHeader.classList.add('spacer-column'); + headerRow.appendChild(spacerHeader); + + const sportNameHeader = document.createElement('th'); + sportNameHeader.textContent = "Sport"; + headerRow.appendChild(sportNameHeader); + + const dateHeader = document.createElement('th'); + dateHeader.textContent = "Date"; + headerRow.appendChild(dateHeader); + + gamesListTable.appendChild(headerRow); +} + + +function listGame(game, row) { + const teamsCell = document.createElement('td'); + const team1NameSpan = document.createElement('span'); + Data.getTeam(game.team1ID) + .then(data => team1NameSpan.textContent = data.name); + teamsCell.appendChild(team1NameSpan); + const team2NameSpan = document.createElement('span'); + Data.getTeam(game.team2ID) + .then(data => team2NameSpan.textContent = data.name); + teamsCell.appendChild(team2NameSpan); + row.appendChild(teamsCell); + + const scoresCell = document.createElement('td'); + const team1ScoreSpan = document.createElement('span'); + team1ScoreSpan.textContent = game.team1Score; + scoresCell.appendChild(team1ScoreSpan); + const team2ScoreSpan = document.createElement('span'); + team2ScoreSpan.textContent = game.team2Score; + scoresCell.appendChild(team2ScoreSpan); + row.appendChild(scoresCell); + + const spacerCell = document.createElement('td'); + row.appendChild(spacerCell); + + const sportCell = document.createElement('td'); + const sportSpan = document.createElement('span'); + const divisionSpan = document.createElement('span'); + divisionSpan.classList.add('flat-content'); + const divisionNameSpan = document.createElement('span'); + const divisionGenderSpan = document.createElement('span'); + divisionSpan.appendChild(divisionNameSpan); + divisionSpan.appendChild(divisionGenderSpan); + Data.getDivision(game.divisionID) + .then(data => { + Data.getSportName(data.sportID) + .then(data => sportSpan.textContent = data); + divisionNameSpan.textContent = data.name; + divisionGenderSpan.textContent = getGenderLetter(data.gender.name); + }); + sportCell.appendChild(sportSpan); + sportCell.appendChild(divisionSpan); + row.appendChild(sportCell); + + const dateCell = document.createElement('td'); + const yearSpan = document.createElement('span'); + yearSpan.textContent = game.date.slice(0,4); + dateCell.appendChild(yearSpan); + const dateSpan = document.createElement('span'); + dateSpan.textContent = game.date.slice(5); + dateCell.appendChild(dateSpan); + row.appendChild(dateCell); +} + +async function addGame() { + window.location.href = "/manage/game"; +} + +async function editGame(id) { + window.location.href = `/manage/game?game=${id}`; +} + + +async function listItems() { + const gamesList = await Data.getGames(); + + await listGameHeaders(); + + gamesList.forEach(game => { + const row = document.createElement('tr'); + + listGame(game, row); + + const manageCell = document.createElement('td'); + + const editSpan = document.createElement('span'); + const editButton = document.createElement('button'); + editButton.textContent = "E"; + editButton.addEventListener('click', () => { + editGame(game.id); + }); + editSpan.appendChild(editButton); + manageCell.appendChild(editSpan); + + row.appendChild(manageCell); + + gamesListTable.appendChild(row); + }); +} +listItems(); + +addNewButton.addEventListener('click', () => addGame()); \ No newline at end of file diff --git a/routes/manage.js b/routes/manage.js index c3202f1..aece4fc 100644 --- a/routes/manage.js +++ b/routes/manage.js @@ -30,7 +30,8 @@ function adminLoggedIn(req, res, next) { router.get('/' ,userLoggedIn, function(req, res, next) { - res.render('manage', { title: 'Score Management' }); + if(req.user[2]) res.render('manage', { title: 'Score Management' }); + else res.render('manage/manage-nonadmin', { title: "Manage Games" }); }); router.get('/game', userLoggedIn, function(req, res, next) { diff --git a/views/manage/manage-nonadmin.pug b/views/manage/manage-nonadmin.pug new file mode 100644 index 0000000..f3bb4e6 --- /dev/null +++ b/views/manage/manage-nonadmin.pug @@ -0,0 +1,16 @@ +extends layout + +block stylesheets + link(rel='stylesheet', href='/stylesheets/manage.css') + link(rel='stylesheet', href='/stylesheets/form.css') + +block content + div#mobile-view + h1 #{title} + div + h2#table-header + table#games-list + button#add-new-button Add new... + +block scripts + script(src='/scripts/manage/manage-nonadmin.js' type="module") \ No newline at end of file From 3515be836d0a4e429cdf438c66dbb8abd0c06b8b Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Thu, 25 Nov 2021 18:49:31 -0700 Subject: [PATCH 27/35] Add submitter_id column to init_database.sql script --- database/database.js | 1 - database/init_database.sql | 30 ++++++++++++++++-------------- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/database/database.js b/database/database.js index 1d5bc56..2f9277e 100644 --- a/database/database.js +++ b/database/database.js @@ -26,7 +26,6 @@ async function Initialize() { } - async function checkForDatabaseInitialization() { const query = `SELECT schema_name FROM information_schema.schemata WHERE schema_name = 'scores'`; let result = await executeQuery(query); diff --git a/database/init_database.sql b/database/init_database.sql index ea8decf..c7de265 100644 --- a/database/init_database.sql +++ b/database/init_database.sql @@ -30,6 +30,18 @@ accounts: BEGIN; +CREATE SCHEMA IF NOT EXISTS accounts; + +CREATE TABLE IF NOT EXISTS accounts.users( + user_id BIGINT GENERATED ALWAYS AS IDENTITY, + email TEXT UNIQUE NOT NULL, + password TEXT NOT NULL, + admin BOOLEAN NOT NULL DEFAULT FALSE, + PRIMARY KEY(user_id) + ); + + + CREATE SCHEMA IF NOT EXISTS scores; @@ -78,7 +90,7 @@ CREATE TABLE IF NOT EXISTS scores.games( team2_id BIGINT, team1_score INTEGER, team2_score INTEGER, -/* submitter_id BIGINT,*/ + submitter_id BIGINT, updated_timestamp TIMESTAMP WITH TIME ZONE DEFAULT now(), PRIMARY KEY(game_id), CONSTRAINT fk_division @@ -92,20 +104,10 @@ CREATE TABLE IF NOT EXISTS scores.games( REFERENCES scores.teams(team_id), CONSTRAINT fk_team2 FOREIGN KEY(team2_id) - REFERENCES scores.teams(team_id) -/* CONSTRAINT fk_submitter + REFERENCES scores.teams(team_id), + CONSTRAINT fk_submitter FOREIGN KEY(submitter_id) - REFERENCES accounts.users(user_id)*/ - ); - - -CREATE SCHEMA IF NOT EXISTS accounts; - -CREATE TABLE IF NOT EXISTS accounts.users( - user_id BIGINT GENERATED ALWAYS AS IDENTITY, - email TEXT UNIQUE NOT NULL, - password TEXT NOT NULL, - admin BOOLEAN NOT NULL DEFAULT FALSE + REFERENCES accounts.users(user_id) ); COMMIT; \ No newline at end of file From bd5b393a278154bde1f6a6f9b0210073b38c4cd6 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Thu, 25 Nov 2021 19:21:21 -0700 Subject: [PATCH 28/35] Add panel for non-admins to edit their own games --- database/accounts/accounts.js | 18 ++++++++++++++++++ database/database.js | 6 +++--- database/scores/games.js | 23 +++++++++++++++++++---- public/scripts/data.js | 7 +++++++ public/scripts/manage/manage-nonadmin.js | 2 +- routes/data.js | 5 +++-- routes/manage.js | 5 +++-- 7 files changed, 54 insertions(+), 12 deletions(-) diff --git a/database/accounts/accounts.js b/database/accounts/accounts.js index a8bab35..84c7a35 100644 --- a/database/accounts/accounts.js +++ b/database/accounts/accounts.js @@ -12,6 +12,24 @@ class User { } +async function checkForAdminAccount() { + + const adminUsersQuery = `SELECT * + FROM accounts.users + WHERE admin = true;`; + const adminUsers = await database.executeQuery(adminUsersQuery); + + if(adminUsers.length == 0) { + const passwordHash = await generateHash('admin'); + const createTempAdminQuery = `INSERT INTO accounts.users(email, password, admin) + VALUES('admin@example.com', $1, true);`; + database.executeQuery(createTempAdminQuery, [passwordHash]); + console.log("Created temp admin account 'admin@example.com' with password 'admin'."); + } +} +checkForAdminAccount(); + + passport.use(new localStrategy({ usernameField: 'email', passwordField: 'password'}, diff --git a/database/database.js b/database/database.js index 2f9277e..349e2bc 100644 --- a/database/database.js +++ b/database/database.js @@ -27,13 +27,13 @@ async function Initialize() { async function checkForDatabaseInitialization() { - const query = `SELECT schema_name FROM information_schema.schemata WHERE schema_name = 'scores'`; - let result = await executeQuery(query); + const scoresSchemaExistsQuery = `SELECT schema_name FROM information_schema.schemata WHERE schema_name = 'scores'`; + let result = await executeQuery(scoresSchemaExistsQuery); const scoresSchemaExists = result.length !== 0; if(!scoresSchemaExists) { - Initialize(); + await Initialize(); } } checkForDatabaseInitialization(); diff --git a/database/scores/games.js b/database/scores/games.js index 270941b..3506356 100644 --- a/database/scores/games.js +++ b/database/scores/games.js @@ -19,12 +19,12 @@ class Game { -async function add(divisionID, seasonID, date, team1ID, team2ID, team1Score, team2Score) { - const query = `INSERT INTO scores.games(division_id, season_id, game_date, team1_id, team2_id, team1_score, team2_score) - VALUES($1, $2, $3, $4, $5, $6, $7) +async function add(divisionID, seasonID, date, team1ID, team2ID, team1Score, team2Score, userID) { + const query = `INSERT INTO scores.games(division_id, season_id, game_date, team1_id, team2_id, team1_score, team2_score, submitter_id) + VALUES($1, $2, $3, $4, $5, $6, $7, $8) RETURNING game_id;`; - const id = (await database.executeQuery(query, [divisionID, seasonID, date, team1ID, team2ID, team1Score, team2Score]))[0][0]; + const id = (await database.executeQuery(query, [divisionID, seasonID, date, team1ID, team2ID, team1Score, team2Score, userID]))[0][0]; return new Game(id, date, team1ID, team2ID, team1Score, team2Score); } @@ -71,6 +71,20 @@ async function retrieve(teamID, divisionID, seasonID) { return gamesList; } +async function retrieveByUser(userID) { + const query = `SELECT game_id, division_id, season_id, game_date, team1_id, team2_id, team1_score, team2_score + FROM scores.games + WHERE submitter_id = $1 + ORDER BY game_date DESC;`; + const table = await database.executeQuery(query, [userID]); + + const gamesList = []; + table.forEach((row) => { + gamesList.push(new Game(row[0], row[3].toISOString().slice(0,10), row[4], row[5], row[6], row[7], row[1], row[2])); + }); + return gamesList; +} + async function edit(gameID, divisionID, seasonID, date, team1ID, team2ID, team1Score, team2Score) { const query = `UPDATE scores.games SET division_id = $2, @@ -100,5 +114,6 @@ async function getFromID(gameID) { exports.add = add; exports.remove = remove; exports.retrieve = retrieve; +exports.retrieveByUser = retrieveByUser; exports.edit = edit; exports.getFromID = getFromID; \ No newline at end of file diff --git a/public/scripts/data.js b/public/scripts/data.js index ae8dc7f..e8c5898 100644 --- a/public/scripts/data.js +++ b/public/scripts/data.js @@ -65,6 +65,13 @@ export async function getGames(teamID = undefined, divisionID = undefined, seaso return gamesList; } +export async function getGamesByUser() { + let URL = '/data/games?user=1'; + const response = await fetch(URL); + const gamesList = await response.json(); + return gamesList; +} + export async function getGame(gameID) { const response = await fetch(`/data/game?game=${gameID}`); const game = await response.json(); diff --git a/public/scripts/manage/manage-nonadmin.js b/public/scripts/manage/manage-nonadmin.js index 4fc4b02..d549030 100644 --- a/public/scripts/manage/manage-nonadmin.js +++ b/public/scripts/manage/manage-nonadmin.js @@ -97,7 +97,7 @@ async function editGame(id) { async function listItems() { - const gamesList = await Data.getGames(); + const gamesList = await Data.getGamesByUser(); await listGameHeaders(); diff --git a/routes/data.js b/routes/data.js index d010bd1..3d71892 100644 --- a/routes/data.js +++ b/routes/data.js @@ -62,8 +62,9 @@ router.get('/team', function(req, res, next) { }) router.get('/games', function(req, res, next) { - games.retrieve(req.query.team, req.query.division, req.query.season) - .then(data => res.json(data)); + const userID = req.user[0]; + if(req.query.user) games.retrieveByUser(userID).then(data => res.json(data)); + else games.retrieve(req.query.team, req.query.division, req.query.season).then(data => res.json(data)); }) router.get('/game', function(req, res, next) { diff --git a/routes/manage.js b/routes/manage.js index aece4fc..b01b477 100644 --- a/routes/manage.js +++ b/routes/manage.js @@ -31,7 +31,7 @@ function adminLoggedIn(req, res, next) { router.get('/' ,userLoggedIn, function(req, res, next) { if(req.user[2]) res.render('manage', { title: 'Score Management' }); - else res.render('manage/manage-nonadmin', { title: "Manage Games" }); + else res.render('manage/manage-nonadmin', { title: "My Games" }); }); router.get('/game', userLoggedIn, function(req, res, next) { @@ -50,6 +50,7 @@ router.post('/game', userLoggedIn, function(req, res, next) { const team1Score = req.body['team1-score']; const team2ID = req.body['team2']; const team2Score = req.body['team2-score']; + const userID = req.user[0]; const id = req.body['game']; const remove = req.body['remove']; @@ -58,7 +59,7 @@ router.post('/game', userLoggedIn, function(req, res, next) { .then(res.redirect("/manage")); else if(id) games.edit(id, divisionID, seasonID, date, team1ID, team2ID, team1Score, team2Score) .then(res.redirect('/manage')); - else games.add(divisionID, seasonID, date, team1ID, team2ID, team1Score, team2Score) + else games.add(divisionID, seasonID, date, team1ID, team2ID, team1Score, team2Score, userID) .then(res.redirect("/manage")); }); From 79123c14bd0b4944bc653118a0b7e40efaaebbcc Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Thu, 25 Nov 2021 19:33:53 -0700 Subject: [PATCH 29/35] Only allow non-admin users to edit their own games --- database/scores/games.js | 7 ++++--- routes/manage.js | 21 +++++++++++++++------ 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/database/scores/games.js b/database/scores/games.js index 3506356..8792b2e 100644 --- a/database/scores/games.js +++ b/database/scores/games.js @@ -5,7 +5,7 @@ const database = require('./../database'); class Game { - constructor(id, date, team1ID, team2ID, team1Score, team2Score, divisionID, seasonID) { + constructor(id, date, team1ID, team2ID, team1Score, team2Score, divisionID, seasonID, submitterID) { this.id = id; this.date = date; this.team1ID = team1ID; @@ -14,6 +14,7 @@ class Game { this.team2Score = team2Score; this.divisionID = divisionID; this.seasonID = seasonID; + this.submitterID = submitterID; } } @@ -100,11 +101,11 @@ async function edit(gameID, divisionID, seasonID, date, team1ID, team2ID, team1S } async function getFromID(gameID) { - const query = `SELECT game_id, division_id, season_id, game_date, team1_id, team2_id, team1_score, team2_score + const query = `SELECT game_id, division_id, season_id, game_date, team1_id, team2_id, team1_score, team2_score, submitter_id FROM scores.games WHERE game_id = $1;`; const row = (await database.executeQuery(query, [gameID]))[0]; - return new Game(row[0], row[3].toISOString().slice(0,10), row[4], row[5], row[6], row[7], row[1], row[2]); + return new Game(row[0], row[3].toISOString().slice(0,10), row[4], row[5], row[6], row[7], row[1], row[2], row[8]); } diff --git a/routes/manage.js b/routes/manage.js index b01b477..0a9c16e 100644 --- a/routes/manage.js +++ b/routes/manage.js @@ -55,12 +55,21 @@ router.post('/game', userLoggedIn, function(req, res, next) { const id = req.body['game']; const remove = req.body['remove']; - if(remove) games.remove(id) - .then(res.redirect("/manage")); - else if(id) games.edit(id, divisionID, seasonID, date, team1ID, team2ID, team1Score, team2Score) - .then(res.redirect('/manage')); - else games.add(divisionID, seasonID, date, team1ID, team2ID, team1Score, team2Score, userID) - .then(res.redirect("/manage")); + const loggedInUserID = req.user[0]; + const loggedInUserIsAdmin = req.user[2]; + + games.getFromID(id) + .then(game => { + if(!loggedInUserIsAdmin && loggedInUserID != game.submitterID) { + res.status(403).send("ACCESS DENIED"); + } + else if(remove) games.remove(id) + .then(res.redirect("/manage")); + else if(id) games.edit(id, divisionID, seasonID, date, team1ID, team2ID, team1Score, team2Score) + .then(res.redirect('/manage')); + else games.add(divisionID, seasonID, date, team1ID, team2ID, team1Score, team2Score, userID) + .then(res.redirect("/manage")); + }); }); router.get('/season', adminLoggedIn, function(req, res, next) { From 54978d3c35dba2a22fd2403df5d236342aca46ca Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Thu, 25 Nov 2021 19:46:29 -0700 Subject: [PATCH 30/35] Add manage account button for non-admin manage page --- public/stylesheets/style.css | 5 +++++ views/manage/manage-nonadmin.pug | 5 ++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/public/stylesheets/style.css b/public/stylesheets/style.css index ec6953e..06b919e 100644 --- a/public/stylesheets/style.css +++ b/public/stylesheets/style.css @@ -17,4 +17,9 @@ a { .flat-content { flex-direction: row; + justify-content: space-between; +} + +.send-to-right { + margin-left: auto; } \ No newline at end of file diff --git a/views/manage/manage-nonadmin.pug b/views/manage/manage-nonadmin.pug index f3bb4e6..4f9df3a 100644 --- a/views/manage/manage-nonadmin.pug +++ b/views/manage/manage-nonadmin.pug @@ -7,10 +7,13 @@ block stylesheets block content div#mobile-view h1 #{title} + span(class="flat-content") + button#add-new-button Add new... + button#manage-account-button(class="send-to-right") Manage account... + div h2#table-header table#games-list - button#add-new-button Add new... block scripts script(src='/scripts/manage/manage-nonadmin.js' type="module") \ No newline at end of file From e277107b10fe0d01f72686ff876c044d26044f0a Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Fri, 26 Nov 2021 12:08:45 -0700 Subject: [PATCH 31/35] Add ability for non-admin users to manage their account --- public/scripts/manage/account.js | 16 ++++++++-- public/scripts/manage/manage-nonadmin.js | 7 ++++- public/stylesheets/submit.css | 4 +++ routes/data.js | 27 +++++++++++++--- routes/manage.js | 39 +++++++++++++++++++----- views/accounts/createuser.pug | 4 ++- 6 files changed, 79 insertions(+), 18 deletions(-) diff --git a/public/scripts/manage/account.js b/public/scripts/manage/account.js index 0ab23a3..40b252f 100644 --- a/public/scripts/manage/account.js +++ b/public/scripts/manage/account.js @@ -4,13 +4,14 @@ import * as Form from "../form.js"; const submissionForm = document.getElementById('submission-form'); const emailTextbox = document.getElementById('email-textbox'); const passwordTextbox = document.getElementById('password-textbox'); +const adminCheckboxSection = document.getElementById('admin-checkbox-section'); const adminCheckbox = document.getElementById('admin-checkbox'); const submitButton = document.getElementById('submit-button'); const deleteButton = document.getElementById('delete-button'); async function Initialize() { let params = new URLSearchParams(location.search); - let accountID = params.get('account'); + let accountID = params.get('account') || (document.getElementById('account-id') ? document.getElementById('account-id').value : null); if(accountID) { const account = await Data.getAccount(accountID); console.log(account); @@ -21,16 +22,25 @@ async function Initialize() { adminCheckbox.checked = account.isAdmin; - Form.addHiddenValue('account', accountID, submissionForm); + if(!document.getElementById('account-id')) { + adminCheckboxSection.style.visibility = "visible"; + adminCheckbox.disabled = false; + + Form.addHiddenValue('account', accountID, submissionForm); + } deleteButton.style.visibility = "visible"; deleteButton.disabled = false; } + else + { + adminCheckboxSection.style.visibility = "visible"; + adminCheckbox.disabled = false; + } emailTextbox.disabled = false; emailTextbox.addEventListener('keyup', checkDataValidity); passwordTextbox.disabled = false; passwordTextbox.addEventListener('keyup', checkDataValidity); - adminCheckbox.disabled = false; checkDataValidity(); } Initialize(); diff --git a/public/scripts/manage/manage-nonadmin.js b/public/scripts/manage/manage-nonadmin.js index d549030..86782aa 100644 --- a/public/scripts/manage/manage-nonadmin.js +++ b/public/scripts/manage/manage-nonadmin.js @@ -2,6 +2,8 @@ import * as Data from "./../data.js"; const gamesListTable = document.getElementById('games-list'); const addNewButton = document.getElementById('add-new-button'); +const manageAccountButton = document.getElementById('manage-account-button'); + function getGenderLetter(genderName) { @@ -124,4 +126,7 @@ async function listItems() { } listItems(); -addNewButton.addEventListener('click', () => addGame()); \ No newline at end of file +addNewButton.addEventListener('click', () => addGame()); +manageAccountButton.addEventListener('click', () => { + window.location.href = '/manage/account'; +}); \ No newline at end of file diff --git a/public/stylesheets/submit.css b/public/stylesheets/submit.css index af4e239..30e64a5 100644 --- a/public/stylesheets/submit.css +++ b/public/stylesheets/submit.css @@ -1,3 +1,7 @@ h1 { text-align: center; +} + +#admin-checkbox-section { + visibility: hidden; } \ No newline at end of file diff --git a/routes/data.js b/routes/data.js index 3d71892..5981784 100644 --- a/routes/data.js +++ b/routes/data.js @@ -10,13 +10,22 @@ var accounts = require('../database/accounts/accounts'); function adminLoggedIn(req, res, next) { if (req.user && req.user[2]) { + next(); + } + else { + req.flash('error', 'An admin account is required to access this page.'); + res.redirect('/auth/login'); + } +} + +function userLoggedIn(req, res, next) { + if (req.user) { next(); } else { - req.flash('error', 'An admin account is required to access this page.'); res.redirect('/auth/login'); } - } +} router.get('/sports', function(req, res, next) { sports.retrieveAll() @@ -77,9 +86,17 @@ router.get('/accounts', adminLoggedIn, function(req, res, next) { .then(data => res.json(data)); }) -router.get('/account', adminLoggedIn, function(req, res, next) { - accounts.getFromID(req.query.account) - .then(data => res.json(data)); +router.get('/account', userLoggedIn, function(req, res, next) { + const userIsAdmin = req.user[2]; + const loggedInAccountID = req.user[0]; + const requestedAccountID = req.query.account; + + if(!userIsAdmin && loggedInAccountID != requestedAccountID) { + res.status(403).send("ACCESS DENIED"); + } else { + accounts.getFromID(req.query.account) + .then(data => res.json(data)); + } }) module.exports = router; \ No newline at end of file diff --git a/routes/manage.js b/routes/manage.js index 0a9c16e..73bdfeb 100644 --- a/routes/manage.js +++ b/routes/manage.js @@ -149,23 +149,46 @@ router.post('/team', adminLoggedIn, function(req, res, next) { else teams.add(name, sport).then(res.redirect("/manage")); }); -router.get('/account', adminLoggedIn, (req, res, next) => { - let title = req.query.account ? 'Manage User' : 'Create User' +router.get('/account', userLoggedIn, (req, res, next) => { + const userIsAdmin = req.user[2]; + const accountID = req.user[0]; + + if(userIsAdmin) { + let title = req.query.account ? 'Manage User' : 'Create User' - res.render('accounts/createuser', { title }); + res.render('accounts/createuser', { title }); + } + else { + let title = 'Manage Account'; + + res.render('accounts/createuser', { title, accountID }); + } }); -router.post('/account', adminLoggedIn, (req, res, next) => { +router.post('/account', userLoggedIn, (req, res, next) => { const email = req.body.email; const password = req.body.password; - const isAdmin = !!req.body.admin; const accountID = req.body.account; const remove = req.body.remove; - if(remove) accounts.remove(accountID).then(res.redirect('/manage')); - if(accountID) accounts.edit(accountID, email, password, isAdmin).then(res.redirect('/manage')); - else accounts.create(req.body.email, req.body.password, !!req.body.admin).then(res.redirect('/manage')); + const loggedInAccountIsAdmin = req.user[2]; + const loggedInAccountID = req.user[0]; + + console.log(accountID); + console.log(loggedInAccountID); + + + if(!loggedInAccountIsAdmin && accountID != loggedInAccountID) { + res.status(403).send("ACCESS DENIED"); + } + else { + const isAdmin = loggedInAccountIsAdmin ? !!req.body.admin : false; + + if(remove) accounts.remove(accountID).then(res.redirect('/manage')); + if(accountID) accounts.edit(accountID, email, password, isAdmin).then(res.redirect('/manage')); + else accounts.create(req.body.email, req.body.password, !!req.body.admin).then(res.redirect('/manage')); + } }); module.exports = router; diff --git a/views/accounts/createuser.pug b/views/accounts/createuser.pug index 36e0acb..94a968b 100644 --- a/views/accounts/createuser.pug +++ b/views/accounts/createuser.pug @@ -8,6 +8,8 @@ block content div#mobile-view h1 #{title} form#submission-form(action='/manage/account', method='POST') + if accountID + input#account-id(type="hidden" name="account" value=accountID) span(class='form-section') label Email span(class='form-section-input') @@ -16,7 +18,7 @@ block content label Password span(class='form-section-input' ) input#password-textbox(type="password" name="password" disabled) - span(class='form-section') + span#admin-checkbox-section(class='form-section') span(class='form-section-checkbox') input#admin-checkbox(type="checkbox" name="admin" disabled) label(for="admin-checkbox") Grant admin privileges From 872397d05ed09b0a641d3aa07f5007bad5133ce1 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Fri, 26 Nov 2021 12:37:09 -0700 Subject: [PATCH 32/35] Add login/logout buttons --- public/scripts/main.js | 14 ++++++++++++++ public/stylesheets/index.css | 5 ----- public/stylesheets/style.css | 5 +++++ routes/index.js | 2 +- routes/manage.js | 18 +++++++++--------- views/accounts/createuser.pug | 2 -- views/accounts/login.pug | 26 ++++++++++++-------------- views/index.pug | 12 ++++++------ views/layout.pug | 13 +++++++++++-- views/manage.pug | 2 -- views/manage/adddivision.pug | 4 +--- views/manage/addgame.pug | 4 +--- views/manage/addseason.pug | 4 +--- views/manage/addsport.pug | 4 +--- views/manage/addteam.pug | 4 +--- views/manage/manage-nonadmin.pug | 4 +--- 16 files changed, 64 insertions(+), 59 deletions(-) create mode 100644 public/scripts/main.js diff --git a/public/scripts/main.js b/public/scripts/main.js new file mode 100644 index 0000000..ced1e07 --- /dev/null +++ b/public/scripts/main.js @@ -0,0 +1,14 @@ +const logInButton = document.getElementById('login-button'); +const logOutButton = document.getElementById('logout-button'); + +if(logInButton) { + logInButton.addEventListener('click', () => { + window.location.href = "/auth/login"; + }); +} + +if(logOutButton) { + logOutButton.addEventListener('click', () => { + window.location.href = "/auth/logout"; + }); +} \ No newline at end of file diff --git a/public/stylesheets/index.css b/public/stylesheets/index.css index 7a60f19..9bf14cb 100644 --- a/public/stylesheets/index.css +++ b/public/stylesheets/index.css @@ -25,8 +25,3 @@ tr { flex-direction: row; } -#actions-div { - display: flex; - flex-direction: column; - margin-left: auto; -} \ No newline at end of file diff --git a/public/stylesheets/style.css b/public/stylesheets/style.css index 06b919e..5e83f29 100644 --- a/public/stylesheets/style.css +++ b/public/stylesheets/style.css @@ -22,4 +22,9 @@ a { .send-to-right { margin-left: auto; +} + +#actions-div { + display: flex; + margin-left: auto; } \ No newline at end of file diff --git a/routes/index.js b/routes/index.js index 3c536d2..47fe5d4 100644 --- a/routes/index.js +++ b/routes/index.js @@ -3,7 +3,7 @@ var router = express.Router(); /* GET home page. */ router.get('/', function(req, res, next) { - res.render('index', { title: 'View Scores' }); + res.render('index', { title: 'View Scores', userLoggedIn: !!req.user }); }); module.exports = router; diff --git a/routes/manage.js b/routes/manage.js index 73bdfeb..b04cf75 100644 --- a/routes/manage.js +++ b/routes/manage.js @@ -30,14 +30,14 @@ function adminLoggedIn(req, res, next) { router.get('/' ,userLoggedIn, function(req, res, next) { - if(req.user[2]) res.render('manage', { title: 'Score Management' }); - else res.render('manage/manage-nonadmin', { title: "My Games" }); + if(req.user[2]) res.render('manage', { title: 'Score Management', userLoggedIn: !!req.user }); + else res.render('manage/manage-nonadmin', { title: "My Games", userLoggedIn: !!req.user }); }); router.get('/game', userLoggedIn, function(req, res, next) { let title = req.query.game ? 'Edit Game' : 'Submit Score' - res.render('manage/addgame', { title }); + res.render('manage/addgame', { title, userLoggedIn: !!req.user }); }); router.post('/game', userLoggedIn, function(req, res, next) { @@ -73,7 +73,7 @@ router.post('/game', userLoggedIn, function(req, res, next) { }); router.get('/season', adminLoggedIn, function(req, res, next) { - res.render('manage/addseason', { title: 'Add Season', currentYear : (new Date()).getFullYear() }); + res.render('manage/addseason', { title: 'Add Season', currentYear : (new Date()).getFullYear(), userLoggedIn: !!req.user }); }); router.post('/season', adminLoggedIn, function(req, res, next) { @@ -87,7 +87,7 @@ router.post('/season', adminLoggedIn, function(req, res, next) { }); router.get('/sport', adminLoggedIn, function(req, res, next) { - res.render('manage/addsport', { title: 'Add Sport' }); + res.render('manage/addsport', { title: 'Add Sport', userLoggedIn: !!req.user }); }); router.post('/sport', adminLoggedIn, function(req, res, next) { @@ -103,7 +103,7 @@ router.post('/sport', adminLoggedIn, function(req, res, next) { router.get('/division', adminLoggedIn, function(req, res, next) { let title = req.query.division ? 'Edit Division' : 'Add Division' - res.render('manage/adddivision', { title }); + res.render('manage/adddivision', { title, userLoggedIn: !!req.user }); }); router.post('/division', adminLoggedIn, function(req, res, next) { @@ -134,7 +134,7 @@ router.post('/division', adminLoggedIn, function(req, res, next) { router.get('/team', adminLoggedIn, function(req, res, next) { let title = req.query.team ? 'Edit Team' : 'Add Team' - res.render('manage/addteam', { title }); + res.render('manage/addteam', { title, userLoggedIn: !!req.user }); }); router.post('/team', adminLoggedIn, function(req, res, next) { @@ -156,12 +156,12 @@ router.get('/account', userLoggedIn, (req, res, next) => { if(userIsAdmin) { let title = req.query.account ? 'Manage User' : 'Create User' - res.render('accounts/createuser', { title }); + res.render('accounts/createuser', { title, userLoggedIn: !!req.user }); } else { let title = 'Manage Account'; - res.render('accounts/createuser', { title, accountID }); + res.render('accounts/createuser', { title, accountID, userLoggedIn: !!req.user }); } }); diff --git a/views/accounts/createuser.pug b/views/accounts/createuser.pug index 94a968b..a87897d 100644 --- a/views/accounts/createuser.pug +++ b/views/accounts/createuser.pug @@ -5,8 +5,6 @@ block stylesheets link(rel='stylesheet', href='/stylesheets/form.css') block content - div#mobile-view - h1 #{title} form#submission-form(action='/manage/account', method='POST') if accountID input#account-id(type="hidden" name="account" value=accountID) diff --git a/views/accounts/login.pug b/views/accounts/login.pug index 13f707e..cbc9044 100644 --- a/views/accounts/login.pug +++ b/views/accounts/login.pug @@ -5,17 +5,15 @@ block stylesheets link(rel='stylesheet', href='/stylesheets/form.css') block content - div#mobile-view - h1 #{title} - form(action='/auth/login', method='POST') - span(class='form-section') - label Email - span(class='form-section-input') - input(type="email", name="email") - span(class='form-section') - label Password - span(class='form-section-input') - input(type="password", name="password") - .error #{message} - span(class='form-section') - button#submit-button(type="submit") Submit \ No newline at end of file + form(action='/auth/login', method='POST') + span(class='form-section') + label Email + span(class='form-section-input') + input(type="email", name="email") + span(class='form-section') + label Password + span(class='form-section-input') + input(type="password", name="password") + .error #{message} + span(class='form-section') + button#submit-button(type="submit") Submit \ No newline at end of file diff --git a/views/index.pug b/views/index.pug index 674d874..3deed78 100644 --- a/views/index.pug +++ b/views/index.pug @@ -4,13 +4,13 @@ block stylesheets link(rel='stylesheet', href='/stylesheets/index.css') link(rel='stylesheet', href='/stylesheets/form.css') +block actions + if userLoggedIn + button#add-score-button Submit score + button#manage-button Manage scores + + block content - div#mobile-view - div#header-div - h1 Score Tracker - div#actions-div - button#add-score-button + - button#manage-button Manage div span(class='form-section') label Year diff --git a/views/layout.pug b/views/layout.pug index e4f85eb..52ad62f 100644 --- a/views/layout.pug +++ b/views/layout.pug @@ -1,10 +1,19 @@ doctype html html head - title= title + title= title + ' - Score Tracker' meta(name='viewport', content='width=device-width, initial-scale=1') link(rel='stylesheet', href='/stylesheets/style.css') block stylesheets body - block content + div#mobile-view + div#actions-div + block actions + if userLoggedIn + button#logout-button Log out + else if userLoggedIn !== undefined + button#login-button Log in + h1 #{title} + block content block scripts + script(src='/scripts/main.js' type="module") diff --git a/views/manage.pug b/views/manage.pug index 454d1ed..63d76ec 100644 --- a/views/manage.pug +++ b/views/manage.pug @@ -5,8 +5,6 @@ block stylesheets link(rel='stylesheet', href='/stylesheets/form.css') block content - div#mobile-view - h1 Management Panel div span(class='form-section') label Category diff --git a/views/manage/adddivision.pug b/views/manage/adddivision.pug index 5db737c..10e7281 100644 --- a/views/manage/adddivision.pug +++ b/views/manage/adddivision.pug @@ -1,12 +1,10 @@ -extends layout +extends ../layout block stylesheets link(rel='stylesheet', href='/stylesheets/submit.css') link(rel='stylesheet', href='/stylesheets/form.css') block content - div#mobile-view - h1 #{title} form#submission-form(action='./division', method='POST') span(class='form-section') label Sport diff --git a/views/manage/addgame.pug b/views/manage/addgame.pug index 7981148..61e7aaa 100644 --- a/views/manage/addgame.pug +++ b/views/manage/addgame.pug @@ -1,12 +1,10 @@ -extends layout +extends ../layout block stylesheets link(rel='stylesheet', href='/stylesheets/submit.css') link(rel='stylesheet', href='/stylesheets/form.css') block content - div#mobile-view - h1 #{title} form#submission-form(action='./game', method='POST') span(class='form-section') label Year diff --git a/views/manage/addseason.pug b/views/manage/addseason.pug index bdb13fd..4050cb2 100644 --- a/views/manage/addseason.pug +++ b/views/manage/addseason.pug @@ -1,12 +1,10 @@ -extends layout +extends ../layout block stylesheets link(rel='stylesheet', href='/stylesheets/submit.css') link(rel='stylesheet', href='/stylesheets/form.css') block content - div#mobile-view - h1 #{title} form(action='./season', method='POST') span(class='form-section') label Ending year diff --git a/views/manage/addsport.pug b/views/manage/addsport.pug index 5251b7e..067b9ac 100644 --- a/views/manage/addsport.pug +++ b/views/manage/addsport.pug @@ -1,12 +1,10 @@ -extends layout +extends ../layout block stylesheets link(rel='stylesheet', href='/stylesheets/submit.css') link(rel='stylesheet', href='/stylesheets/form.css') block content - div#mobile-view - h1#main-header Add Sport form#submission-form(action='./sport', method='POST') span(class='form-section') label Sport name diff --git a/views/manage/addteam.pug b/views/manage/addteam.pug index 3cb6ccb..9f7262c 100644 --- a/views/manage/addteam.pug +++ b/views/manage/addteam.pug @@ -1,12 +1,10 @@ -extends layout +extends ../layout block stylesheets link(rel='stylesheet', href='/stylesheets/submit.css') link(rel='stylesheet', href='/stylesheets/form.css') block content - div#mobile-view - h1 #{title} form#submission-form(action='./team', method='POST') span(class='form-section') label Sport diff --git a/views/manage/manage-nonadmin.pug b/views/manage/manage-nonadmin.pug index 4f9df3a..b639b1a 100644 --- a/views/manage/manage-nonadmin.pug +++ b/views/manage/manage-nonadmin.pug @@ -1,12 +1,10 @@ -extends layout +extends ../layout block stylesheets link(rel='stylesheet', href='/stylesheets/manage.css') link(rel='stylesheet', href='/stylesheets/form.css') block content - div#mobile-view - h1 #{title} span(class="flat-content") button#add-new-button Add new... button#manage-account-button(class="send-to-right") Manage account... From 3201ffb2a9509c52ee536b6421e17b939e78adc3 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Fri, 26 Nov 2021 12:46:16 -0700 Subject: [PATCH 33/35] Add "Home" button for navigation --- public/scripts/main.js | 7 +++++++ public/stylesheets/index.css | 3 +++ public/stylesheets/style.css | 5 ++++- routes/index.js | 2 +- views/layout.pug | 2 ++ 5 files changed, 17 insertions(+), 2 deletions(-) diff --git a/public/scripts/main.js b/public/scripts/main.js index ced1e07..aac4daf 100644 --- a/public/scripts/main.js +++ b/public/scripts/main.js @@ -1,5 +1,6 @@ const logInButton = document.getElementById('login-button'); const logOutButton = document.getElementById('logout-button'); +const homeButton = document.getElementById('home-button'); if(logInButton) { logInButton.addEventListener('click', () => { @@ -11,4 +12,10 @@ if(logOutButton) { logOutButton.addEventListener('click', () => { window.location.href = "/auth/logout"; }); +} + +if(homeButton) { + homeButton.addEventListener('click', () => { + window.location.href = '/'; + }); } \ No newline at end of file diff --git a/public/stylesheets/index.css b/public/stylesheets/index.css index 9bf14cb..7e66419 100644 --- a/public/stylesheets/index.css +++ b/public/stylesheets/index.css @@ -25,3 +25,6 @@ tr { flex-direction: row; } +#actions-div { + margin-left: auto; +} \ No newline at end of file diff --git a/public/stylesheets/style.css b/public/stylesheets/style.css index 5e83f29..4582e1e 100644 --- a/public/stylesheets/style.css +++ b/public/stylesheets/style.css @@ -26,5 +26,8 @@ a { #actions-div { display: flex; - margin-left: auto; +} + +#home-button { + margin-right: auto; } \ No newline at end of file diff --git a/routes/index.js b/routes/index.js index 47fe5d4..42fa2b0 100644 --- a/routes/index.js +++ b/routes/index.js @@ -3,7 +3,7 @@ var router = express.Router(); /* GET home page. */ router.get('/', function(req, res, next) { - res.render('index', { title: 'View Scores', userLoggedIn: !!req.user }); + res.render('index', { title: 'View Scores', userLoggedIn: !!req.user, hideHomeButton: true }); }); module.exports = router; diff --git a/views/layout.pug b/views/layout.pug index 52ad62f..11fb12b 100644 --- a/views/layout.pug +++ b/views/layout.pug @@ -8,6 +8,8 @@ html body div#mobile-view div#actions-div + if !hideHomeButton + button#home-button Home block actions if userLoggedIn button#logout-button Log out From bb335c7acac32d1b99d6faaa4ef131f7b325fdc9 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Fri, 26 Nov 2021 12:53:54 -0700 Subject: [PATCH 34/35] Fix bug where games don't load if user is not logged in --- public/scripts/index.js | 17 ++++++++++------- routes/data.js | 2 +- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/public/scripts/index.js b/public/scripts/index.js index 843ed4b..0beced6 100644 --- a/public/scripts/index.js +++ b/public/scripts/index.js @@ -177,11 +177,14 @@ genderDropdown.onchange = listDivisions; teamDropdown.onchange = listGames; seasonDropdown.onchange = listGames; +if(addScoreButton) { + addScoreButton.addEventListener('click', () => { + window.location.href = '/manage/game'; + }); +} -addScoreButton.addEventListener('click', () => { - window.location.href = '/manage/game'; -}); - -manageButton.addEventListener('click', () => { - window.location.href = '/manage' -}); \ No newline at end of file +if(manageButton) { + manageButton.addEventListener('click', () => { + window.location.href = '/manage' + }); +} diff --git a/routes/data.js b/routes/data.js index 5981784..c875622 100644 --- a/routes/data.js +++ b/routes/data.js @@ -71,7 +71,7 @@ router.get('/team', function(req, res, next) { }) router.get('/games', function(req, res, next) { - const userID = req.user[0]; + const userID = req.user ? req.user[0] : null; if(req.query.user) games.retrieveByUser(userID).then(data => res.json(data)); else games.retrieve(req.query.team, req.query.division, req.query.season).then(data => res.json(data)); }) From ad4533e71296f1a5caf114849c797f7f7e3d8d1b Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Fri, 26 Nov 2021 12:59:48 -0700 Subject: [PATCH 35/35] Adjust layout for non-admin management panel --- views/index.pug | 2 +- views/manage/manage-nonadmin.pug | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/views/index.pug b/views/index.pug index 3deed78..b504698 100644 --- a/views/index.pug +++ b/views/index.pug @@ -7,7 +7,7 @@ block stylesheets block actions if userLoggedIn button#add-score-button Submit score - button#manage-button Manage scores + button#manage-button Manage... block content diff --git a/views/manage/manage-nonadmin.pug b/views/manage/manage-nonadmin.pug index b639b1a..890c739 100644 --- a/views/manage/manage-nonadmin.pug +++ b/views/manage/manage-nonadmin.pug @@ -4,14 +4,14 @@ block stylesheets link(rel='stylesheet', href='/stylesheets/manage.css') link(rel='stylesheet', href='/stylesheets/form.css') -block content - span(class="flat-content") - button#add-new-button Add new... - button#manage-account-button(class="send-to-right") Manage account... +block actions + button#manage-account-button(class="send-to-right") Manage account +block content div - h2#table-header table#games-list + div + button#add-new-button Add new... block scripts script(src='/scripts/manage/manage-nonadmin.js' type="module") \ No newline at end of file