diff --git a/app.js b/app.js index c773280..a379166 100644 --- a/app.js +++ b/app.js @@ -3,14 +3,42 @@ 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 flash = require('connect-flash'); + 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 adminRouter = require('./routes/admin'); var app = express(); +// flash setup +app.use(flash()); + +// 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'); @@ -25,6 +53,8 @@ app.use('/', indexRouter); 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 new file mode 100644 index 0000000..84c7a35 --- /dev/null +++ b/database/accounts/accounts.js @@ -0,0 +1,141 @@ +const database = require('./../database'); +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; + } +} + + +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'}, + (username, password, cb) => { + query = `SELECT user_id, email, password, admin + FROM accounts.users + WHERE email = $1`; + database.executeQuery(query, [username]) + .then(result => { + if(result.length > 0) { + const first = result[0]; + const matches = bcrypt.compareSync(password, first[2]); + if(matches) { + return cb(null, { id: first[0], email: first[1], admin: first[3] }) + } + else + { + return cb(null, false) + } + } else { + return cb(null, false) + } + }); +})); + +passport.serializeUser((user, done) => { + done(null, user.id) +}) + +passport.deserializeUser((id, cb) => { + query = `SELECT user_id, email, admin + FROM accounts.users + WHERE user_id = $1`; + database.executeQuery(query, [parseInt(id, 10)]) + .then(result => { + cb(null, result[0]); + }); +}); + + +async function generateHash(password) { + const salt = bcrypt.genSaltSync(); + return bcrypt.hashSync(password, salt); +} + +async function create(email, password, isAdmin) { + 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 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 + 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; +} + +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.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/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/database/database.js b/database/database.js index f166980..349e2bc 100644 --- a/database/database.js +++ b/database/database.js @@ -26,16 +26,14 @@ 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/init_database.sql b/database/init_database.sql index ad4f0e7..c7de265 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 */ @@ -33,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; @@ -81,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 @@ -95,11 +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)*/ + REFERENCES accounts.users(user_id) ); - COMMIT; \ No newline at end of file diff --git a/database/scores/games.js b/database/scores/games.js index 270941b..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,17 +14,18 @@ class Game { this.team2Score = team2Score; this.divisionID = divisionID; this.seasonID = seasonID; + this.submitterID = submitterID; } } -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 +72,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, @@ -86,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]); } @@ -100,5 +115,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/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]); } diff --git a/package-lock.json b/package-lock.json index 225db7b..1903fa7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,14 +9,18 @@ "version": "0.0.0", "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", "express": "~4.16.0", + "express-session": "^1.17.2", "http-errors": "~1.6.2", "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" }, @@ -25,6 +29,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", @@ -38,6 +61,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", @@ -80,6 +108,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", @@ -101,6 +161,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", @@ -153,8 +251,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", @@ -167,6 +264,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", @@ -191,7 +301,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" @@ -247,6 +356,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", @@ -272,6 +389,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", @@ -304,8 +429,20 @@ "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/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", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" }, "node_modules/constantinople": { "version": "3.1.2", @@ -402,6 +539,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", @@ -415,6 +557,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", @@ -442,6 +595,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", @@ -520,6 +678,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", @@ -580,17 +791,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", @@ -642,6 +882,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", @@ -665,6 +910,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", @@ -677,7 +955,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" @@ -721,6 +998,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", @@ -789,6 +1074,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", @@ -841,7 +1159,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" }, @@ -855,6 +1172,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", @@ -937,6 +1277,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", @@ -945,6 +1301,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", @@ -965,9 +1349,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" } @@ -976,7 +1360,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, "dependencies": { "wrappy": "1" } @@ -987,9 +1370,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" } @@ -1010,6 +1393,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", @@ -1022,7 +1416,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" } @@ -1299,6 +1692,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", @@ -1395,11 +1796,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", @@ -1437,11 +1885,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", @@ -1490,6 +1948,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", @@ -1545,6 +2027,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", @@ -1558,6 +2067,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", @@ -1602,6 +2116,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", @@ -1639,6 +2164,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", @@ -1667,8 +2214,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", @@ -1678,6 +2224,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", @@ -1691,6 +2242,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", @@ -1704,6 +2271,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", @@ -1733,6 +2305,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", @@ -1748,6 +2343,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", @@ -1797,8 +2423,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", @@ -1808,6 +2433,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", @@ -1829,7 +2463,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" @@ -1873,6 +2506,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", @@ -1892,6 +2530,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", @@ -1918,8 +2561,17 @@ "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=" + }, + "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", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" }, "constantinople": { "version": "3.1.2", @@ -1997,6 +2649,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", @@ -2007,6 +2664,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", @@ -2028,6 +2690,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", @@ -2091,6 +2758,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", @@ -2138,17 +2837,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", @@ -2188,6 +2910,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", @@ -2205,6 +2932,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", @@ -2214,7 +2965,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" @@ -2251,6 +3001,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", @@ -2307,6 +3062,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", @@ -2344,7 +3122,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" } @@ -2355,6 +3132,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", @@ -2422,11 +3216,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", @@ -2441,15 +3267,14 @@ } }, "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", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, "requires": { "wrappy": "1" } @@ -2460,9 +3285,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", @@ -2473,6 +3298,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", @@ -2481,8 +3314,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", @@ -2719,6 +3551,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", @@ -2799,11 +3636,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", @@ -2835,11 +3703,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", @@ -2881,6 +3759,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", @@ -2929,6 +3825,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", @@ -2939,6 +3855,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", @@ -2971,6 +3892,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", @@ -2996,6 +3925,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", @@ -3018,14 +3969,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 c94c979..c3c687d 100644 --- a/package.json +++ b/package.json @@ -8,14 +8,18 @@ }, "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", "express": "~4.16.0", + "express-session": "^1.17.2", "http-errors": "~1.6.2", "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" }, diff --git a/public/scripts/data.js b/public/scripts/data.js index 873d4d6..e8c5898 100644 --- a/public/scripts/data.js +++ b/public/scripts/data.js @@ -65,8 +65,27 @@ 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(); return game; +} + +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/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/public/scripts/main.js b/public/scripts/main.js new file mode 100644 index 0000000..aac4daf --- /dev/null +++ b/public/scripts/main.js @@ -0,0 +1,21 @@ +const logInButton = document.getElementById('login-button'); +const logOutButton = document.getElementById('logout-button'); +const homeButton = document.getElementById('home-button'); + +if(logInButton) { + logInButton.addEventListener('click', () => { + window.location.href = "/auth/login"; + }); +} + +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/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/scripts/manage/account.js b/public/scripts/manage/account.js new file mode 100644 index 0000000..40b252f --- /dev/null +++ b/public/scripts/manage/account.js @@ -0,0 +1,58 @@ +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 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') || (document.getElementById('account-id') ? document.getElementById('account-id').value : null); + if(accountID) { + const account = await Data.getAccount(accountID); + console.log(account); + + emailTextbox.value = account.email; + + passwordTextbox.placeholder = "leave unchanged"; + + adminCheckbox.checked = account.isAdmin; + + 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); + checkDataValidity(); +} +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; +} + +Form.addRemoveFunction(deleteButton, submissionForm, "account"); \ No newline at end of file diff --git a/public/scripts/manage/game.js b/public/scripts/manage/game.js index 0cc134b..8338d4c 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 => { @@ -65,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(); diff --git a/public/scripts/manage/manage-nonadmin.js b/public/scripts/manage/manage-nonadmin.js new file mode 100644 index 0000000..86782aa --- /dev/null +++ b/public/scripts/manage/manage-nonadmin.js @@ -0,0 +1,132 @@ +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) { + 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.getGamesByUser(); + + 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()); +manageAccountButton.addEventListener('click', () => { + window.location.href = '/manage/account'; +}); \ No newline at end of file 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 { diff --git a/public/stylesheets/form.css b/public/stylesheets/form.css index 21abe10..389028c 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-checkbox { + width: auto; } \ No newline at end of file diff --git a/public/stylesheets/index.css b/public/stylesheets/index.css index 7a60f19..7e66419 100644 --- a/public/stylesheets/index.css +++ b/public/stylesheets/index.css @@ -26,7 +26,5 @@ tr { } #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 ec6953e..4582e1e 100644 --- a/public/stylesheets/style.css +++ b/public/stylesheets/style.css @@ -17,4 +17,17 @@ a { .flat-content { flex-direction: row; + justify-content: space-between; +} + +.send-to-right { + margin-left: auto; +} + +#actions-div { + display: flex; +} + +#home-button { + margin-right: auto; } \ 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/admin.js b/routes/admin.js new file mode 100644 index 0000000..9e9cfc9 --- /dev/null +++ b/routes/admin.js @@ -0,0 +1,24 @@ +var express = require('express'); +var router = express.Router(); +const passport = require('passport'); +const app = require('../app'); +const 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('/', 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 new file mode 100644 index 0000000..25159ab --- /dev/null +++ b/routes/auth.js @@ -0,0 +1,39 @@ +var express = require('express'); +var router = express.Router(); +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') }); +}); + +router.get('/logout', (req, res, next) => { + req.logout(); + res.redirect("/"); +}); + +router.post('/login', + passport.authenticate('local', { + failureRedirect: '/auth/login', + successRedirect: '/', + failureFlash: "Invalid email or password.", + }), + (req, res, next) => { + console.log(req.user); +}); + + + +module.exports = router; \ No newline at end of file diff --git a/routes/data.js b/routes/data.js index 6ad845a..c875622 100644 --- a/routes/data.js +++ b/routes/data.js @@ -6,7 +6,26 @@ 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'); + } +} + +function userLoggedIn(req, res, next) { + if (req.user) { + next(); + } + else { + res.redirect('/auth/login'); + } +} router.get('/sports', function(req, res, next) { sports.retrieveAll() @@ -52,8 +71,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 ? 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)); }) router.get('/game', function(req, res, next) { @@ -61,4 +81,22 @@ 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)); +}) + +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/index.js b/routes/index.js index 3c536d2..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' }); + res.render('index', { title: 'View Scores', userLoggedIn: !!req.user, hideHomeButton: true }); }); module.exports = router; diff --git a/routes/manage.js b/routes/manage.js index 9757e69..b04cf75 100644 --- a/routes/manage.js +++ b/routes/manage.js @@ -7,19 +7,40 @@ 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) { + next(); + } + else { + res.redirect('/auth/login'); + } +} + +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('/', function(req, res, next) { - res.render('manage', { title: 'Score Management' }); +router.get('/' ,userLoggedIn, function(req, res, next) { + 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', 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 }); + res.render('manage/addgame', { title, userLoggedIn: !!req.user }); }); -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; @@ -29,23 +50,33 @@ router.post('/game', 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['delete']; + 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) - .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', function(req, res, next) { - res.render('manage/addseason', { title: 'Add Season', currentYear : (new Date()).getFullYear() }); +router.get('/season', adminLoggedIn, function(req, res, next) { + res.render('manage/addseason', { title: 'Add Season', currentYear : (new Date()).getFullYear(), userLoggedIn: !!req.user }); }); -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 +86,11 @@ router.post('/season', function(req, res, next) { else seasons.add(year).then(res.redirect("/manage")); }); -router.get('/sport', function(req, res, next) { - res.render('manage/addsport', { title: 'Add Sport' }); +router.get('/sport', adminLoggedIn, function(req, res, next) { + res.render('manage/addsport', { title: 'Add Sport', userLoggedIn: !!req.user }); }); -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 +100,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 }); + res.render('manage/adddivision', { title, userLoggedIn: !!req.user }); }); -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 +131,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 }); + res.render('manage/addteam', { title, userLoggedIn: !!req.user }); }); -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']; @@ -118,4 +149,46 @@ router.post('/team', function(req, res, next) { else teams.add(name, sport).then(res.redirect("/manage")); }); +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, userLoggedIn: !!req.user }); + } + else { + let title = 'Manage Account'; + + res.render('accounts/createuser', { title, accountID, userLoggedIn: !!req.user }); + } +}); + +router.post('/account', userLoggedIn, (req, res, next) => { + const email = req.body.email; + const password = req.body.password; + + const accountID = req.body.account; + const remove = req.body.remove; + + 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 new file mode 100644 index 0000000..a87897d --- /dev/null +++ b/views/accounts/createuser.pug @@ -0,0 +1,29 @@ +extends ../layout + +block stylesheets + link(rel='stylesheet', href='/stylesheets/submit.css') + link(rel='stylesheet', href='/stylesheets/form.css') + +block content + 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') + input#email-textbox(type="email", name="email" disabled) + span(class='form-section') + label Password + span(class='form-section-input' ) + input#password-textbox(type="password" name="password" disabled) + 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 + span(class='form-section') + 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 diff --git a/views/accounts/login.pug b/views/accounts/login.pug new file mode 100644 index 0000000..cbc9044 --- /dev/null +++ b/views/accounts/login.pug @@ -0,0 +1,19 @@ +extends ../layout + +block stylesheets + link(rel='stylesheet', href='/stylesheets/submit.css') + link(rel='stylesheet', href='/stylesheets/form.css') + +block content + 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..b504698 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... + + 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..11fb12b 100644 --- a/views/layout.pug +++ b/views/layout.pug @@ -1,10 +1,21 @@ 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 + if !hideHomeButton + button#home-button Home + 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 c43dcf9..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 @@ -17,6 +15,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 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 039bfd1..61e7aaa 100644 --- a/views/manage/addgame.pug +++ b/views/manage/addgame.pug @@ -1,13 +1,11 @@ -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='./submitgame', method='POST') + form#submission-form(action='./game', method='POST') span(class='form-section') label Year span(class='form-section-input') 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 new file mode 100644 index 0000000..890c739 --- /dev/null +++ b/views/manage/manage-nonadmin.pug @@ -0,0 +1,17 @@ +extends ../layout + +block stylesheets + link(rel='stylesheet', href='/stylesheets/manage.css') + link(rel='stylesheet', href='/stylesheets/form.css') + +block actions + button#manage-account-button(class="send-to-right") Manage account + +block content + div + 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