diff --git a/.env.example b/.env.example
index 9ef5217..11d1e69 100644
--- a/.env.example
+++ b/.env.example
@@ -1,5 +1,11 @@
NODE_ENV=development
+PGUSER=dbuser
+PGHOST=database.server.com
+PGPASSWORD=dbuserpassword
+PGDATABASE=mydatabase
+PGPORT=5432
+
MAIL_FROM=fromaddress@example.com
MAIL_HOST=smtp.smtphost.net
MAIL_PORT=465
diff --git a/.node-version b/.node-version
index 2f76972..58a4133 100644
--- a/.node-version
+++ b/.node-version
@@ -1 +1 @@
-8.11.1
+16.13.0
diff --git a/Dockerfile b/Dockerfile
index 41c390d..97abb5d 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,4 +1,4 @@
-FROM node:8.11-alpine
+FROM node:16.13.0-alpine3.12
WORKDIR /usr/src/app
diff --git a/app.js b/app.js
index 3cff1ca..4065b14 100644
--- a/app.js
+++ b/app.js
@@ -3,13 +3,40 @@ 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 submitRouter = require('./routes/submit')
+var dataRouter = require('./routes/data');
+var manageRouter = require('./routes/manage');
+var authRouter = require('./routes/auth');
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');
@@ -21,8 +48,10 @@ app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use('/', indexRouter);
-app.use('/users', usersRouter);
-app.use('/submit', submitRouter);
+app.use('/data', dataRouter);
+app.use('/manage', manageRouter);
+app.use('/auth', authRouter);
+
// catch 404 and forward to error handler
app.use(function(req, res, next) {
diff --git a/database/accounts/accounts.js b/database/accounts/accounts.js
new file mode 100644
index 0000000..d33d11f
--- /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'.");
+ }
+}
+database.initializationStatus.then(() => 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
new file mode 100644
index 0000000..7390426
--- /dev/null
+++ b/database/database.js
@@ -0,0 +1,46 @@
+const app = require('../app');
+const { Client } = require('pg');
+const fs = require('fs');
+
+if (process.env.NODE_ENV !== 'production' || process.env.NODE_ENV !== 'testing') {
+ require('dotenv').config();
+}
+
+const client = new Client();
+client.connect();
+
+async function executeQuery(query, values = []) {
+ const result = await client.query({
+ rowMode: 'array',
+ text: query,
+ values: values
+ });
+ return result.rows;
+}
+
+async function Initialize() {
+ console.log("Initializing database...")
+ const sql = fs.readFileSync('database/init_database.sql').toString();
+ await executeQuery(sql);
+ console.log("Database initialized.")
+}
+
+
+async function checkForDatabaseInitialization() {
+ 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) {
+ await Initialize();
+ }
+}
+const initializationStatus = checkForDatabaseInitialization();
+
+
+
+
+
+exports.executeQuery = executeQuery;
+exports.initializationStatus = initializationStatus;
\ No newline at end of file
diff --git a/database/init_database.sql b/database/init_database.sql
new file mode 100644
index 0000000..404addf
--- /dev/null
+++ b/database/init_database.sql
@@ -0,0 +1,113 @@
+/* SCORE TRACKER DATABASE LAYOUT
+
+scores:
+
+ sports:
+ *sport_id* | sport_name | currently_active
+
+ divisions:
+ *division_id* | division_name | gender | *sport_id* | currently_active
+
+ teams:
+ *team_id* | team_name | ~sport_id~ | currently_active
+
+ seasons:
+ *season_id* | school_year
+
+ games:
+ *game_id* | ~division_id~ | ~season_id~ | game_date | ~team1_id~ | ~team2_id~ | team1_score | team2_score | ~submitter_id~ | updated_timestamp
+
+
+
+accounts:
+
+ users:
+ *user_id* | email | password | admin
+
+*/
+
+
+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;
+
+
+CREATE TABLE IF NOT EXISTS scores.sports(
+ sport_id BIGINT GENERATED ALWAYS AS IDENTITY,
+ sport_name TEXT UNIQUE NOT NULL,
+ currently_active BOOLEAN DEFAULT TRUE,
+ PRIMARY KEY(sport_id)
+ );
+
+CREATE TABLE IF NOT EXISTS scores.divisions(
+ division_id BIGINT GENERATED ALWAYS AS IDENTITY,
+ division_name TEXT NOT NULL,
+ gender VARCHAR(1) NOT NULL CHECK (gender IN ( 'F', 'M' ) ),
+ sport_id BIGINT NOT NULL,
+ currently_active BOOLEAN DEFAULT TRUE,
+ PRIMARY KEY(division_id),
+ CONSTRAINT fk_sport
+ FOREIGN KEY(sport_id)
+ REFERENCES scores.sports(sport_id)
+ );
+
+CREATE TABLE IF NOT EXISTS scores.teams(
+ team_id BIGINT GENERATED ALWAYS AS IDENTITY,
+ team_name TEXT NOT NULL,
+ sport_id BIGINT NOT NULL,
+ currently_active BOOLEAN DEFAULT TRUE,
+ PRIMARY KEY(team_id),
+ CONSTRAINT fk_sport
+ FOREIGN KEY(sport_id)
+ REFERENCES scores.sports(sport_id)
+ );
+
+CREATE TABLE IF NOT EXISTS scores.seasons(
+ season_id BIGINT GENERATED ALWAYS AS IDENTITY,
+ school_year INTEGER NOT NULL,
+ PRIMARY KEY(season_id)
+ );
+
+CREATE TABLE IF NOT EXISTS scores.games(
+ game_id BIGINT GENERATED ALWAYS AS IDENTITY,
+ division_id BIGINT NOT NULL,
+ season_id BIGINT NOT NULL,
+ game_date DATE NOT NULL,
+ team1_id BIGINT NOT NULL,
+ team2_id BIGINT NOT NULL,
+ team1_score INTEGER NOT NULL,
+ team2_score INTEGER NOT NULL,
+ submitter_id BIGINT NOT NULL,
+ updated_timestamp TIMESTAMP WITH TIME ZONE DEFAULT now(),
+ PRIMARY KEY(game_id),
+ CONSTRAINT fk_division
+ FOREIGN KEY(division_id)
+ REFERENCES scores.divisions(division_id),
+ CONSTRAINT fk_season
+ FOREIGN KEY(season_id)
+ REFERENCES scores.seasons(season_id),
+ CONSTRAINT fk_team1
+ FOREIGN KEY(team1_id)
+ REFERENCES scores.teams(team_id),
+ CONSTRAINT fk_team2
+ FOREIGN KEY(team2_id)
+ REFERENCES scores.teams(team_id),
+ CONSTRAINT fk_submitter
+ FOREIGN KEY(submitter_id)
+ REFERENCES accounts.users(user_id)
+ );
+
+COMMIT;
\ No newline at end of file
diff --git a/database/scores/divisions.js b/database/scores/divisions.js
new file mode 100644
index 0000000..7da86dd
--- /dev/null
+++ b/database/scores/divisions.js
@@ -0,0 +1,99 @@
+const database = require('./../database');
+const genders = require('./genders');
+
+
+
+
+
+class Division {
+ constructor(id, name, gender, sportID) {
+ this.id = id;
+ this.name = name;
+ this.gender = gender;
+ this.sportID = sportID;
+ }
+}
+
+
+
+function getGenderID(gender) {
+ return (gender == genders.MALE) ? "M" : "F";
+}
+
+function getGenderFromID(genderID) {
+ return (genderID == "F") ? genders.FEMALE : genders.MALE;
+}
+
+
+
+async function add(name, gender, sportID) {
+ const query = `INSERT INTO scores.divisions(division_name,gender,sport_id)
+ VALUES($1,$2,$3)
+ RETURNING division_id;`;
+ const genderID = getGenderID(gender);
+ const id = (await database.executeQuery(query, [name, genderID, sportID]))[0][0];
+ return new Division(id, name);
+}
+
+async function rename(id, name) {
+ const query = `UPDATE scores.divisions
+ SET division_name = $2
+ WHERE division_id = $1;`;
+ await database.executeQuery(query, [id, name]);
+ return new Division(id, name);
+}
+
+async function remove(id) {
+ const query = `DELETE FROM scores.divisions
+ WHERE division_id = $1
+ RETURNING division_name;`;
+ const name = (await database.executeQuery(query, [id]))[0][0];
+ return new Division(id, name);
+}
+
+async function retrieve(sportID = undefined, gender = undefined) {
+ let table;
+
+ if(sportID && gender) {
+ const query = `SELECT division_id, division_name, gender, sport_id
+ FROM scores.divisions
+ WHERE sport_id = $1 AND gender = $2
+ ORDER BY division_name;`;
+ const genderID = getGenderID(gender);
+ table = await database.executeQuery(query, [sportID, genderID]);
+ }
+ else {
+ const query = `SELECT division_id, division_name, gender, sport_id
+ FROM scores.divisions
+ ORDER BY sport_id,
+ division_name,
+ gender;`;
+ table = await database.executeQuery(query);
+ }
+
+ const divisionsList = [];
+ table.forEach((row) => {
+ divisionsList.push(new Division(row[0], row[1], getGenderFromID(row[2]), row[3]));
+ });
+ return divisionsList;
+}
+
+async function getFromID(id) {
+ const query = `SELECT division_id, division_name, gender, sport_id
+ FROM scores.divisions
+ WHERE division_id = $1;`;
+ const row = (await database.executeQuery(query, [id]))[0];
+
+
+ return new Division(id, row[1], getGenderFromID(row[2]), row[3]);
+}
+
+
+
+
+
+exports.add = add;
+exports.rename = rename;
+exports.remove = remove;
+exports.retrieve = retrieve;
+exports.getFromID = getFromID;
\ No newline at end of file
diff --git a/database/scores/games.js b/database/scores/games.js
new file mode 100644
index 0000000..8792b2e
--- /dev/null
+++ b/database/scores/games.js
@@ -0,0 +1,120 @@
+const database = require('./../database');
+
+
+
+
+
+class Game {
+ constructor(id, date, team1ID, team2ID, team1Score, team2Score, divisionID, seasonID, submitterID) {
+ this.id = id;
+ this.date = date;
+ this.team1ID = team1ID;
+ this.team2ID = team2ID;
+ this.team1Score = team1Score;
+ this.team2Score = team2Score;
+ this.divisionID = divisionID;
+ this.seasonID = seasonID;
+ this.submitterID = submitterID;
+ }
+}
+
+
+
+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, userID]))[0][0];
+ return new Game(id, date, team1ID, team2ID, team1Score, team2Score);
+}
+
+async function remove(id) {
+ const query = `DELETE FROM scores.games
+ WHERE game_id = $1
+ RETURNING * ;`;
+ const row = (await database.executeQuery(query, [id]))[0];
+ return new Game(id, row[3], row[4], row[5], row[6], row[7]);
+}
+
+async function retrieve(teamID, divisionID, seasonID) {
+ let table;
+
+ if(teamID && divisionID && seasonID) {
+ const query = `SELECT game_id, division_id, season_id, game_date, team1_id, team2_id, team1_score, team2_score
+ FROM scores.games
+ WHERE (team1_id = $1 OR team2_id = $1) AND division_id = $2 AND season_id = $3
+ ORDER BY game_date DESC;`;
+ table = await database.executeQuery(query, [teamID,divisionID,seasonID]);
+ }
+ else {
+ const query = `SELECT game_id, division_id, season_id, game_date, team1_id, team2_id, team1_score, team2_score
+ FROM scores.games
+ ORDER BY game_date DESC;`;
+ table = await database.executeQuery(query);
+ }
+
+
+ const gamesList = [];
+ table.forEach((row) => {
+ if(teamID) {
+ const opponentIsTeam2 = teamID != row[5];
+ const opponentID = opponentIsTeam2 ? row[5] : row[4];
+ const teamScore = opponentIsTeam2 ? row[6] : row[7];
+ const opponentScore = opponentIsTeam2 ? row[7] : row[6];
+
+ gamesList.push(new Game(row[0], row[3].toISOString().slice(0,10), teamID, opponentID, teamScore, opponentScore, row[1], row[2]));
+ }
+ else {
+ 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 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,
+ season_id = $3,
+ game_date = $4,
+ team1_id = $5,
+ team2_id = $6,
+ team1_score = $7,
+ team2_score = $8
+ WHERE game_id = $1;`;
+ await database.executeQuery(query, [gameID, divisionID, seasonID, date, team1ID, team2ID, team1Score, team2Score]);
+ return new Game(gameID, date, team1ID, team2ID, team1Score, team2Score, divisionID, seasonID);
+}
+
+async function getFromID(gameID) {
+ 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], row[8]);
+}
+
+
+
+
+
+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/genders.js b/database/scores/genders.js
new file mode 100644
index 0000000..17e3ff2
--- /dev/null
+++ b/database/scores/genders.js
@@ -0,0 +1,48 @@
+const database = require('./../database');
+
+
+
+
+
+class Gender {
+ constructor(name) {
+ this.name = name;
+ }
+}
+const MALE = new Gender("male");
+const FEMALE = new Gender("female");
+
+
+
+async function retrieveBySport(sportID) {
+ const query = `SELECT DISTINCT(gender)
+ FROM scores.divisions
+ WHERE sport_id = $1;`;
+ const table = await database.executeQuery(query, [sportID]);
+
+ const gendersList = [];
+
+ if(table.length == 0) {
+ return gendersList;
+ }
+ if(table.length == 2) {
+ gendersList.push(FEMALE);
+ gendersList.push(MALE);
+ return gendersList;
+ }
+ else if(table[0][0] = "F") {
+ gendersList.push(FEMALE);
+ return gendersList;
+ }
+ else if(table[0][0] = "M") {
+ gendersList.push(MALE);
+ return gendersList;
+ }
+}
+
+
+
+
+exports.MALE = MALE;
+exports.FEMALE = FEMALE;
+exports.retrieveBySport = retrieveBySport;
\ No newline at end of file
diff --git a/database/scores/seasons.js b/database/scores/seasons.js
new file mode 100644
index 0000000..f787ade
--- /dev/null
+++ b/database/scores/seasons.js
@@ -0,0 +1,51 @@
+const database = require('./../database');
+
+
+
+
+
+class Season {
+ constructor(id, year) {
+ this.id = id;
+ this.year = year;
+ }
+}
+
+
+
+async function add(year) {
+ const query = `INSERT INTO scores.seasons(school_year)
+ VALUES($1)
+ RETURNING season_id;`;
+ const id = (await database.executeQuery(query, [year]))[0][0];
+ return new Season(id, year);
+}
+
+async function remove(id) {
+ const query = `DELETE FROM scores.seasons
+ WHERE season_id = $1
+ RETURNING school_year;`;
+ const year = (await database.executeQuery(query, [id]))[0][0];
+ return new Season(id, year);
+}
+
+async function retrieveAll() {
+ const query = `SELECT *
+ FROM scores.seasons
+ ORDER BY school_year DESC;`;
+ const table = await database.executeQuery(query);
+
+ const seasonsList = [];
+ table.forEach((row) => {
+ seasonsList.push(new Season(row[0], row[1]));
+ });
+ return seasonsList;
+}
+
+
+
+
+
+exports.add = add;
+exports.remove = remove;
+exports.retrieveAll = retrieveAll;
\ No newline at end of file
diff --git a/database/scores/sports.js b/database/scores/sports.js
new file mode 100644
index 0000000..134009d
--- /dev/null
+++ b/database/scores/sports.js
@@ -0,0 +1,67 @@
+const database = require('./../database');
+
+
+
+class Sport {
+ constructor(id, name) {
+ this.id = id;
+ this.name = name;
+ }
+}
+
+
+
+async function add(name) {
+ const query = `INSERT INTO scores.sports(sport_name)
+ VALUES($1)
+ RETURNING sport_id;`;
+ const id = (await database.executeQuery(query, [name]))[0][0];
+ return new Sport(id, name);
+}
+
+async function rename(id, name) {
+ const query = `UPDATE scores.sports
+ SET sport_name = $2
+ WHERE sport_id = $1;`;
+ await database.executeQuery(query, [id, name]);
+ return new Sport(id, name);
+}
+
+async function remove(id) {
+ const query = `DELETE FROM scores.sports
+ WHERE sport_id = $1
+ RETURNING sport_name;`;
+ const name = (await database.executeQuery(query, [id]))[0][0];
+ return new Sport(id, name);
+}
+
+async function retrieveAll() {
+ const query = `SELECT *
+ FROM scores.sports
+ ORDER BY sport_name;`;
+ const table = await database.executeQuery(query);
+
+ const sportsList = [];
+ table.forEach((row) => {
+ sportsList.push(new Sport(row[0], row[1]));
+ });
+ return sportsList;
+}
+
+async function getFromID(id) {
+ const query = `SELECT sport_name
+ FROM scores.sports
+ WHERE sport_id = $1;`;
+ const name = (await database.executeQuery(query, [id]))[0][0];
+ return new Sport(id, name);
+}
+
+
+
+
+
+exports.add = add;
+exports.rename = rename;
+exports.remove = remove;
+exports.retrieveAll = retrieveAll;
+exports.getFromID = getFromID;
\ No newline at end of file
diff --git a/database/scores/teams.js b/database/scores/teams.js
new file mode 100644
index 0000000..20034ef
--- /dev/null
+++ b/database/scores/teams.js
@@ -0,0 +1,83 @@
+const database = require('./../database');
+
+
+
+
+
+class Team {
+ constructor(id, name, sportID) {
+ this.id = id;
+ this.name = name;
+ this.sportID = sportID;
+ }
+}
+
+
+
+async function add(name, sportID) {
+ const query = `INSERT INTO scores.teams(team_name, sport_id)
+ VALUES($1, $2)
+ RETURNING team_id;`;
+ const id = (await database.executeQuery(query, [name, sportID]))[0][0];
+ return new Team(id, name);
+}
+
+async function rename(id, name) {
+ const query = `UPDATE scores.teams
+ SET team_name = $2
+ WHERE team_id = $1;`;
+ await database.executeQuery(query, [id, name]);
+ return new Team(id, name);
+}
+
+async function remove(id) {
+ const query = `DELETE FROM scores.teams
+ WHERE team_id = $1
+ RETURNING team_name;`;
+ const name = (await database.executeQuery(query, [id]))[0][0];
+ return new Team(id, name);
+}
+
+async function retrieve(sportID = undefined) {
+ let table;
+
+ if(sportID) {
+ const query = `SELECT team_id, team_name, sport_id
+ FROM scores.teams
+ WHERE sport_id = $1
+ ORDER BY team_name;`;
+ table = await database.executeQuery(query, [sportID]);
+ }
+ else {
+ const query = `SELECT team_id, team_name, sport_id
+ FROM scores.teams
+ ORDER BY
+ sport_id,
+ team_name;`;
+ table = await database.executeQuery(query);
+ }
+
+ const teamsList = [];
+ table.forEach((row) => {
+ teamsList.push(new Team(row[0], row[1], row[2]));
+ });
+ return teamsList;
+}
+
+async function getFromID(id) {
+ const query = `SELECT team_name, sport_id
+ FROM scores.teams
+ WHERE team_id = $1;`;
+ const row = (await database.executeQuery(query, [id]))[0];
+ return new Team(id, row[0], row[1]);
+}
+
+
+
+
+
+exports.add = add;
+exports.rename = rename;
+exports.remove = remove;
+exports.retrieve = retrieve;
+exports.getFromID = getFromID;
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index 006fc12..1903fa7 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -8,13 +8,20 @@
"name": "demo",
"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"
},
"devDependencies": {
@@ -22,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",
@@ -35,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",
@@ -77,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",
@@ -98,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",
@@ -108,6 +209,11 @@
"resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
"integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY="
},
+ "node_modules/async": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/async/-/async-3.2.2.tgz",
+ "integrity": "sha512-H0E+qZaDEfx/FY4t7iLRv1W2fFI6+pyCeTw1uN20AQPiwqwM6ojPxHxdLv4z8hi2DtnW9BOckSspLucW7pIE5g=="
+ },
"node_modules/asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
@@ -145,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",
@@ -159,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",
@@ -183,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"
@@ -195,6 +312,14 @@
"integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==",
"dev": true
},
+ "node_modules/buffer-writer": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz",
+ "integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==",
+ "engines": {
+ "node": ">=4"
+ }
+ },
"node_modules/bytes": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
@@ -231,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",
@@ -256,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",
@@ -288,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",
@@ -386,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",
@@ -399,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",
@@ -426,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",
@@ -504,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",
@@ -564,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",
@@ -626,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",
@@ -649,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",
@@ -661,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"
@@ -705,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",
@@ -773,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",
@@ -825,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"
},
@@ -839,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",
@@ -921,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",
@@ -929,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",
@@ -949,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"
}
@@ -960,24 +1360,62 @@
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
- "dev": true,
"dependencies": {
"wrappy": "1"
}
},
+ "node_modules/packet-reader": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz",
+ "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"
}
},
+ "node_modules/passport": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/passport/-/passport-0.5.0.tgz",
+ "integrity": "sha512-ln+ue5YaNDS+fes6O5PCzXKSseY5u8MYhX9H5Co4s+HfYI5oqvnHKoOORLYDUPh+8tHvrxugF2GFcUA1Q1Gqfg==",
+ "dependencies": {
+ "passport-strategy": "1.x.x",
+ "pause": "0.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4.0"
+ },
+ "funding": {
+ "type": "github",
+ "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",
+ "integrity": "sha1-tVOaqPwiWj0a0XlHbd8ja0QPUuQ=",
+ "engines": {
+ "node": ">= 0.4.0"
+ }
+ },
"node_modules/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,
"engines": {
"node": ">=0.10.0"
}
@@ -992,6 +1430,120 @@
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
"integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
},
+ "node_modules/pause": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz",
+ "integrity": "sha1-HUCLP9t2kjuVQ9lvtMnf1TXZy10="
+ },
+ "node_modules/pg": {
+ "version": "8.7.1",
+ "resolved": "https://registry.npmjs.org/pg/-/pg-8.7.1.tgz",
+ "integrity": "sha512-7bdYcv7V6U3KAtWjpQJJBww0UEsWuh4yQ/EjNf2HeO/NnvKjpvhEIe/A/TleP6wtmSKnUnghs5A9jUoK6iDdkA==",
+ "dependencies": {
+ "buffer-writer": "2.0.0",
+ "packet-reader": "1.0.0",
+ "pg-connection-string": "^2.5.0",
+ "pg-pool": "^3.4.1",
+ "pg-protocol": "^1.5.0",
+ "pg-types": "^2.1.0",
+ "pgpass": "1.x"
+ },
+ "engines": {
+ "node": ">= 8.0.0"
+ },
+ "peerDependencies": {
+ "pg-native": ">=2.0.0"
+ },
+ "peerDependenciesMeta": {
+ "pg-native": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/pg-connection-string": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.5.0.tgz",
+ "integrity": "sha512-r5o/V/ORTA6TmUnyWZR9nCj1klXCO2CEKNRlVuJptZe85QuhFayC7WeMic7ndayT5IRIR0S0xFxFi2ousartlQ=="
+ },
+ "node_modules/pg-int8": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz",
+ "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==",
+ "engines": {
+ "node": ">=4.0.0"
+ }
+ },
+ "node_modules/pg-pool": {
+ "version": "3.4.1",
+ "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.4.1.tgz",
+ "integrity": "sha512-TVHxR/gf3MeJRvchgNHxsYsTCHQ+4wm3VIHSS19z8NC0+gioEhq1okDY1sm/TYbfoP6JLFx01s0ShvZ3puP/iQ==",
+ "peerDependencies": {
+ "pg": ">=8.0"
+ }
+ },
+ "node_modules/pg-protocol": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.5.0.tgz",
+ "integrity": "sha512-muRttij7H8TqRNu/DxrAJQITO4Ac7RmX3Klyr/9mJEOBeIpgnF8f9jAfRz5d3XwQZl5qBjF9gLsUtMPJE0vezQ=="
+ },
+ "node_modules/pg-types": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz",
+ "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==",
+ "dependencies": {
+ "pg-int8": "1.0.1",
+ "postgres-array": "~2.0.0",
+ "postgres-bytea": "~1.0.0",
+ "postgres-date": "~1.0.4",
+ "postgres-interval": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/pgpass": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.4.tgz",
+ "integrity": "sha512-YmuA56alyBq7M59vxVBfPJrGSozru8QAdoNlWuW3cz8l+UX3cWge0vTvjKhsSHSJpo3Bom8/Mm6hf0TR5GY0+w==",
+ "dependencies": {
+ "split2": "^3.1.1"
+ }
+ },
+ "node_modules/postgres-array": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz",
+ "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/postgres-bytea": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz",
+ "integrity": "sha1-AntTPAqokOJtFy1Hz5zOzFIazTU=",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/postgres-date": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz",
+ "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/postgres-interval": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz",
+ "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==",
+ "dependencies": {
+ "xtend": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/process-nextick-args": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz",
@@ -1140,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",
@@ -1236,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",
@@ -1278,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",
@@ -1294,6 +1911,27 @@
"node": ">=0.8.0"
}
},
+ "node_modules/split2": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz",
+ "integrity": "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==",
+ "dependencies": {
+ "readable-stream": "^3.0.0"
+ }
+ },
+ "node_modules/split2/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/statuses": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz",
@@ -1306,11 +1944,34 @@
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
- "dev": true,
"dependencies": {
"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",
@@ -1366,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",
@@ -1379,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",
@@ -1423,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",
@@ -1434,8 +2138,7 @@
"node_modules/util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
- "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
- "dev": true
+ "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
},
"node_modules/utils-merge": {
"version": "1.0.1",
@@ -1461,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",
@@ -1489,8 +2214,20 @@
"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",
+ "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
+ "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
+ "engines": {
+ "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",
@@ -1505,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",
@@ -1518,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",
@@ -1547,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",
@@ -1562,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",
@@ -1572,6 +2384,11 @@
"resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
"integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY="
},
+ "async": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/async/-/async-3.2.2.tgz",
+ "integrity": "sha512-H0E+qZaDEfx/FY4t7iLRv1W2fFI6+pyCeTw1uN20AQPiwqwM6ojPxHxdLv4z8hi2DtnW9BOckSspLucW7pIE5g=="
+ },
"asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
@@ -1606,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",
@@ -1617,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",
@@ -1638,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"
@@ -1650,6 +2474,11 @@
"integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==",
"dev": true
},
+ "buffer-writer": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz",
+ "integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw=="
+ },
"bytes": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
@@ -1677,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",
@@ -1696,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",
@@ -1722,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",
@@ -1801,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",
@@ -1811,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",
@@ -1832,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",
@@ -1895,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",
@@ -1942,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",
@@ -1992,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",
@@ -2009,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",
@@ -2018,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"
@@ -2055,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",
@@ -2111,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",
@@ -2148,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"
}
@@ -2159,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",
@@ -2226,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",
@@ -2245,29 +3267,54 @@
}
},
"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"
}
},
+ "packet-reader": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz",
+ "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",
+ "resolved": "https://registry.npmjs.org/passport/-/passport-0.5.0.tgz",
+ "integrity": "sha512-ln+ue5YaNDS+fes6O5PCzXKSseY5u8MYhX9H5Co4s+HfYI5oqvnHKoOORLYDUPh+8tHvrxugF2GFcUA1Q1Gqfg==",
+ "requires": {
+ "passport-strategy": "1.x.x",
+ "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",
+ "integrity": "sha1-tVOaqPwiWj0a0XlHbd8ja0QPUuQ="
},
"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",
@@ -2279,6 +3326,89 @@
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
"integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
},
+ "pause": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz",
+ "integrity": "sha1-HUCLP9t2kjuVQ9lvtMnf1TXZy10="
+ },
+ "pg": {
+ "version": "8.7.1",
+ "resolved": "https://registry.npmjs.org/pg/-/pg-8.7.1.tgz",
+ "integrity": "sha512-7bdYcv7V6U3KAtWjpQJJBww0UEsWuh4yQ/EjNf2HeO/NnvKjpvhEIe/A/TleP6wtmSKnUnghs5A9jUoK6iDdkA==",
+ "requires": {
+ "buffer-writer": "2.0.0",
+ "packet-reader": "1.0.0",
+ "pg-connection-string": "^2.5.0",
+ "pg-pool": "^3.4.1",
+ "pg-protocol": "^1.5.0",
+ "pg-types": "^2.1.0",
+ "pgpass": "1.x"
+ }
+ },
+ "pg-connection-string": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.5.0.tgz",
+ "integrity": "sha512-r5o/V/ORTA6TmUnyWZR9nCj1klXCO2CEKNRlVuJptZe85QuhFayC7WeMic7ndayT5IRIR0S0xFxFi2ousartlQ=="
+ },
+ "pg-int8": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz",
+ "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw=="
+ },
+ "pg-pool": {
+ "version": "3.4.1",
+ "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.4.1.tgz",
+ "integrity": "sha512-TVHxR/gf3MeJRvchgNHxsYsTCHQ+4wm3VIHSS19z8NC0+gioEhq1okDY1sm/TYbfoP6JLFx01s0ShvZ3puP/iQ==",
+ "requires": {}
+ },
+ "pg-protocol": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.5.0.tgz",
+ "integrity": "sha512-muRttij7H8TqRNu/DxrAJQITO4Ac7RmX3Klyr/9mJEOBeIpgnF8f9jAfRz5d3XwQZl5qBjF9gLsUtMPJE0vezQ=="
+ },
+ "pg-types": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz",
+ "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==",
+ "requires": {
+ "pg-int8": "1.0.1",
+ "postgres-array": "~2.0.0",
+ "postgres-bytea": "~1.0.0",
+ "postgres-date": "~1.0.4",
+ "postgres-interval": "^1.1.0"
+ }
+ },
+ "pgpass": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.4.tgz",
+ "integrity": "sha512-YmuA56alyBq7M59vxVBfPJrGSozru8QAdoNlWuW3cz8l+UX3cWge0vTvjKhsSHSJpo3Bom8/Mm6hf0TR5GY0+w==",
+ "requires": {
+ "split2": "^3.1.1"
+ }
+ },
+ "postgres-array": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz",
+ "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA=="
+ },
+ "postgres-bytea": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz",
+ "integrity": "sha1-AntTPAqokOJtFy1Hz5zOzFIazTU="
+ },
+ "postgres-date": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz",
+ "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q=="
+ },
+ "postgres-interval": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz",
+ "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==",
+ "requires": {
+ "xtend": "^4.0.0"
+ }
+ },
"process-nextick-args": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz",
@@ -2421,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",
@@ -2501,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",
@@ -2537,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",
@@ -2550,6 +3726,26 @@
"amdefine": ">=0.0.4"
}
},
+ "split2": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz",
+ "integrity": "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==",
+ "requires": {
+ "readable-stream": "^3.0.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"
+ }
+ }
+ }
+ },
"statuses": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz",
@@ -2559,11 +3755,28 @@
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
- "dev": true,
"requires": {
"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",
@@ -2612,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",
@@ -2622,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",
@@ -2654,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",
@@ -2662,8 +3908,7 @@
"util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
- "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
- "dev": true
+ "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
},
"utils-merge": {
"version": "1.0.1",
@@ -2680,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",
@@ -2702,8 +3969,17 @@
"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",
diff --git a/package.json b/package.json
index 0a035a8..7b97c2d 100644
--- a/package.json
+++ b/package.json
@@ -1,19 +1,25 @@
{
- "name": "demo",
- "version": "0.0.0",
+ "name": "score-tracker",
+ "version": "1.0.0-pre",
"private": true,
"scripts": {
"start": "node ./bin/www",
- "test": "mocha"
},
"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"
},
"devDependencies": {
diff --git a/public/scripts/data.js b/public/scripts/data.js
new file mode 100644
index 0000000..e8c5898
--- /dev/null
+++ b/public/scripts/data.js
@@ -0,0 +1,91 @@
+export async function getSports() {
+ const response = await fetch(`/data/sports`);
+ const sportsList = await response.json();
+ return sportsList;
+}
+
+export async function getSportName(sportID) {
+ const response = await fetch(`/data/sport?sport=${sportID}`);
+ const sport = await response.json();
+ return sport.name;
+}
+
+export async function getSeasons() {
+ const response = await fetch(`/data/seasons`);
+ const seasonsList = await response.json();
+ return seasonsList;
+}
+
+export async function getGenders(sportID) {
+ const response = await fetch(`/data/genders?sport=${+sportID}`);
+ const gendersList = await response.json();
+ return gendersList;
+}
+
+export async function getDivisions(sportID = undefined, gender = undefined) {
+ let URL = '/data/divisions?';
+ if(sportID) URL += `sport=${+sportID}&`;
+ if(gender) URL += `gender=${gender}&`;
+
+ const response = await fetch(URL);
+ const divisionsList = await response.json();
+ return divisionsList;
+}
+
+export async function getDivision(divisionID) {
+ const response = await fetch(`/data/division?division=${divisionID}`);
+ const division = await response.json();
+ return division;
+}
+
+export async function getTeams(sportID = undefined) {
+ let URL = '/data/teams?';
+ if(sportID) URL += `sport=${+sportID}&`;
+
+ const response = await fetch(URL);
+ const teamsList = await response.json();
+ return teamsList;
+}
+
+export async function getTeam(teamID) {
+ const response = await fetch(`/data/team?team=${+teamID}`);
+ const team = await response.json();
+ return team;
+}
+
+export async function getGames(teamID = undefined, divisionID = undefined, seasonID = undefined) {
+ let URL = '/data/games?';
+ if(teamID) URL += `team=${+teamID}&`;
+ if(divisionID) URL += `division=${+divisionID}&`;
+ if(seasonID) URL += `season=${+seasonID}`;
+
+
+ const response = await fetch(URL);
+ const gamesList = await response.json();
+ 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/form.js b/public/scripts/form.js
new file mode 100644
index 0000000..5132ea9
--- /dev/null
+++ b/public/scripts/form.js
@@ -0,0 +1,126 @@
+import * as Data from "./data.js";
+
+export async function populateSports(sportDropdown, selectedSportID = undefined) {
+ sportDropdown.innerHTML = "";
+
+ const sportsList = await Data.getSports();
+
+ let currentIndex = 0;
+ let selectedSportIndex;
+ sportsList.forEach(sport => {
+ const option = document.createElement('option');
+ option.text = sport.name;
+ option.value = sport.id;
+ sportDropdown.appendChild(option);
+
+ if(sport.id == selectedSportID) selectedSportIndex = currentIndex;
+ currentIndex++;
+ });
+
+ if(selectedSportIndex) sportDropdown.selectedIndex = selectedSportIndex;
+}
+
+export async function populateSeasons(seasonDropdown, selectedSeasonID = undefined) {
+ seasonDropdown.innerHTML = "";
+
+ const seasonsList = await Data.getSeasons();
+
+ let currentIndex = 0;
+ let selectedSeasonIndex;
+ seasonsList.forEach(season => {
+ const option = document.createElement('option');
+ option.text = (season.year - 1) + "-" + season.year;
+ option.value = season.id;
+ seasonDropdown.appendChild(option);
+
+ if(season.id == selectedSeasonID) selectedSeasonIndex = currentIndex;
+ currentIndex++;
+ });
+
+ if(selectedSeasonIndex) seasonDropdown.selectedIndex = selectedSeasonIndex;
+}
+
+export async function populateGenders(genderDropdown, selectedSportID, selectedGender = undefined) {
+ genderDropdown.innerHTML = "";
+
+ const gendersList = await Data.getGenders(selectedSportID);
+
+ if(selectedSportID) {
+ let currentIndex = 0;
+ let selectedGenderIndex;
+ gendersList.forEach(gender => {
+ const option = document.createElement('option');
+ option.text = (gender.name == "female") ? "Female" : (gender.name == "male") ? "Male" : "";
+ option.value = gender.name;
+ genderDropdown.appendChild(option);
+
+ if(gender.name == selectedGender) selectedGenderIndex = currentIndex;
+ currentIndex++;
+ });
+
+ if(selectedGenderIndex) genderDropdown.selectedIndex = selectedGenderIndex;
+ }
+}
+
+export async function populateDivisions (divisionDropdown, selectedSportID, selectedGender, selectedDivisionID = undefined) {
+ divisionDropdown.innerHTML = "";
+
+ if(selectedSportID && selectedGender) {
+ const divisionsList = await Data.getDivisions(selectedSportID, selectedGender);
+
+ let currentIndex = 0;
+ let selectedDivisionIndex;
+ divisionsList.forEach(division => {
+ const option = document.createElement('option');
+ option.text = division.name;
+ option.value = division.id;
+ divisionDropdown.appendChild(option);
+
+ if(division.id == selectedDivisionID) selectedDivisionIndex = currentIndex;
+ currentIndex++;
+ });
+
+ if(selectedDivisionIndex) divisionDropdown.selectedIndex = selectedDivisionIndex;
+ }
+}
+
+export async function populateTeams(teamDropdown, selectedSportID, selectedTeamID) {
+ teamDropdown.innerHTML = "";
+
+ if(selectedSportID) {
+ const teamsList = await Data.getTeams(selectedSportID);
+
+ let currentIndex = 0;
+ let selectedTeamIndex;
+ teamsList.forEach(team => {
+ const option = document.createElement('option');
+ option.text = team.name;
+ option.value = team.id;
+ teamDropdown.appendChild(option);
+
+ if(team.id == selectedTeamID) selectedTeamIndex = currentIndex;
+ currentIndex++;
+ });
+
+ if(selectedTeamIndex) teamDropdown.selectedIndex = selectedTeamIndex;
+ }
+}
+
+export async function addHiddenValue(name, value, form) {
+ const valueInput = document.createElement('input');
+ valueInput.setAttribute('name', name);
+ valueInput.setAttribute('value', value);
+ valueInput.setAttribute('type', 'hidden');
+ form.appendChild(valueInput);
+}
+
+export async function addRemoveFunction(removeButton, form, objectTitle) {
+ removeButton.addEventListener('click', async () => {
+ const verified = confirm(`This ${objectTitle} will be removed.`);
+
+ if(verified) {
+ await addHiddenValue('remove', 1, form);
+ form.submit();
+ }
+ });
+}
\ No newline at end of file
diff --git a/public/scripts/index.js b/public/scripts/index.js
new file mode 100644
index 0000000..0beced6
--- /dev/null
+++ b/public/scripts/index.js
@@ -0,0 +1,190 @@
+import * as Data from "./data.js";
+
+const sportDropdown = document.getElementById('sport-dropdown');
+const seasonDropdown = document.getElementById('year-dropdown');
+const genderDropdown = document.getElementById('gender-dropdown');
+const divisionDropdown = document.getElementById('division-dropdown');
+const teamDropdown = document.getElementById('team-dropdown');
+const gamesTable = document.getElementById('games-table');
+const gamesTableHeader = document.getElementById('games-table-header');
+const noScoresMessage = document.getElementById('no-scores-message');
+const addScoreButton = document.getElementById('add-score-button');
+const manageButton = document.getElementById('manage-button');
+
+
+
+
+
+async function listSeasons() {
+ seasonDropdown.innerHTML = "";
+
+ const seasonsList = await Data.getSeasons();
+
+ seasonsList.forEach(season => {
+ const option = document.createElement('option');
+ option.text = (season.year - 1) + "-" + season.year;
+ option.value = season.id;
+ seasonDropdown.appendChild(option);
+ });
+}
+listSeasons();
+
+async function listSports() {
+ sportDropdown.innerHTML = "";
+
+ const sportsList = await Data.getSports();
+
+ sportsList.forEach(sport => {
+ const option = document.createElement('option');
+ option.text = sport.name;
+ option.value = sport.id;
+ sportDropdown.appendChild(option);
+ });
+
+ listGenders();
+}
+listSports();
+
+async function listGenders() {
+ genderDropdown.innerHTML = "";
+
+ const selectedSportID = sportDropdown.value;
+ const gendersList = await Data.getGenders(selectedSportID);
+
+ if(selectedSportID) {
+ gendersList.forEach(gender => {
+ const option = document.createElement('option');
+ option.text = (gender.name == "female") ? "Female" : (gender.name == "male") ? "Male" : "";
+ option.value = gender.name;
+ genderDropdown.appendChild(option);
+ });
+ }
+
+ listDivisions();
+}
+
+async function listDivisions() {
+ divisionDropdown.innerHTML = "";
+
+ const selectedSportID = sportDropdown.value;
+ const selectedGender = genderDropdown.value;
+
+ if(selectedGender) {
+ const divisionsList = await Data.getDivisions(selectedSportID, selectedGender);
+
+ divisionsList.forEach(division => {
+ const option = document.createElement('option');
+ option.text = division.name;
+ option.value = division.id;
+ divisionDropdown.appendChild(option);
+ });
+ }
+ listTeams();
+}
+
+async function listTeams() {
+ teamDropdown.innerHTML = "";
+
+ const selectedSportID = sportDropdown.value;
+
+ if(selectedSportID) {
+ const teamsList = await Data.getTeams(selectedSportID);
+
+ teamsList.forEach(team => {
+ const option = document.createElement('option');
+ option.text = team.name;
+ option.value = team.id;
+ teamDropdown.appendChild(option);
+ });
+ }
+
+ listGames();
+}
+
+async function listGames() {
+ gamesTable.innerHTML = "";
+ gamesTableHeader.textContent = "";
+ noScoresMessage.textContent = "";
+
+ const selectedTeamID = teamDropdown.value;
+ const selectedDivisionID = divisionDropdown.value;
+ const selectedSeasonID = seasonDropdown.value;
+
+ if(selectedTeamID && selectedDivisionID) {
+ gamesTableHeader.textContent = `Scores for ${teamDropdown.options[teamDropdown.selectedIndex].text}`;
+
+ const gamesList = await Data.getGames(selectedTeamID, selectedDivisionID, selectedSeasonID);
+ if(gamesList.length > 0) {
+ await setupGamesTableHeaders();
+
+ gamesList.forEach((game) => {
+ const row = document.createElement('tr');
+
+ const scoreCell = document.createElement('td');
+ const winLossLine = document.createElement('span');
+ winLossLine.textContent = (game.team1Score > game.team2Score) ? "Win" : (game.team1Score < game.team2Score) ? "Loss" : "Tie";
+ scoreCell.appendChild(winLossLine);
+ const scoreLine = document.createElement('span');
+ scoreLine.textContent = game.team1Score + "-" + game.team2Score;
+ scoreCell.appendChild(scoreLine);
+ row.appendChild(scoreCell);
+
+ const opponentCell = document.createElement('td');
+ Data.getTeam(game.team2ID)
+ .then(data => opponentCell.textContent = data.name);
+ row.appendChild(opponentCell);
+
+ const dateCell = document.createElement('td');
+ dateCell.textContent = game.date;
+ row.appendChild(dateCell);
+
+ gamesTable.appendChild(row);
+ });
+ }
+ else {
+ noScoresMessage.textContent = "No scores available";
+ }
+ }
+}
+async function setupGamesTableHeaders() {
+
+ const row = document.createElement('tr');
+
+ const scoresHeader = document.createElement('th');
+ scoresHeader.textContent = "Score"
+ row.appendChild(scoresHeader);
+
+ const opponentHeader = document.createElement('th');
+ opponentHeader.textContent = "Opponent";
+ row.appendChild(opponentHeader);
+
+ const dateHeader = document.createElement('th');
+ dateHeader.textContent = "Date";
+ row.appendChild(dateHeader);
+
+ gamesTable.appendChild(row);
+}
+
+
+
+
+
+sportDropdown.onchange = (() => {
+ listGenders();
+ listTeams();
+});
+genderDropdown.onchange = listDivisions;
+teamDropdown.onchange = listGames;
+seasonDropdown.onchange = listGames;
+
+if(addScoreButton) {
+ addScoreButton.addEventListener('click', () => {
+ window.location.href = '/manage/game';
+ });
+}
+
+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
new file mode 100644
index 0000000..de4a6e2
--- /dev/null
+++ b/public/scripts/manage.js
@@ -0,0 +1,377 @@
+import * as Data from "./data.js";
+
+const categoryDropdown = document.getElementById('category-dropdown');
+const itemsListTable = document.getElementById('items-list');
+const addNewButton = document.getElementById('add-new-button');
+
+
+function getGenderLetter(genderName) {
+ return genderName == "female" ? "F" : "M";
+}
+
+class Category {
+ constructor(name, getItems, listHeaders, listItem, addItem, editItem) {
+ this.name = name;
+ this.getItems = getItems;
+ this.listHeaders = listHeaders;
+ this.listItem = listItem;
+ this.addItem = addItem;
+ this.editItem = editItem;
+ }
+}
+
+const CATEGORIES = [];
+
+CATEGORIES.push(new Category(
+ "seasons",
+ async function getSeasons() {
+ return await Data.getSeasons();
+ },
+ async function listSeasonHeaders() {
+ const headerRow = document.createElement('tr');
+
+ const yearsHeader = document.createElement('th');
+ yearsHeader.textContent = "Years";
+
+ headerRow.appendChild(yearsHeader);
+
+ const spacerHeader = document.createElement('th');
+ spacerHeader.classList.add('spacer-column');
+ headerRow.appendChild(spacerHeader);
+
+ itemsListTable.appendChild(headerRow);
+ },
+ function listSeason(season, row) {
+ const yearCell = document.createElement('td');
+ yearCell.textContent = (season.year - 1) + "-" + season.year;
+ row.appendChild(yearCell);
+
+ const spacerCell = document.createElement('td');
+ row.appendChild(spacerCell);
+ },
+ async function addSeason() {
+ window.location.href = "/manage/season";
+ },
+ async function editSeason(id) {
+ const verified = confirm(`This season will be removed.`);
+
+ if(verified) {
+ const form = document.createElement('form');
+ form.action = "/manage/season";
+ form.method = "POST";
+ form.style.visibility = "hidden";
+ itemsListTable.appendChild(form);
+
+ const remove = document.createElement('input');
+ remove.setAttribute('name', 'remove');
+ remove.setAttribute('value', 1);
+ form.appendChild(remove);
+
+ const seasonID = document.createElement('input');
+ seasonID.setAttribute('name', 'season');
+ seasonID.setAttribute('value', id);
+ form.appendChild(seasonID);
+
+ form.submit();
+ }
+ }
+));
+
+CATEGORIES.push(new Category(
+ "sports",
+ async function getSports() {
+ return await Data.getSports();
+ },
+ async function listSportHeaders() {
+ const headerRow = document.createElement('tr');
+
+ const nameHeader = document.createElement('th');
+ nameHeader.textContent = "Name";
+ headerRow.appendChild(nameHeader);
+
+ const spacerHeader = document.createElement('th');
+ spacerHeader.classList.add('spacer-column');
+ headerRow.appendChild(spacerHeader);
+
+ itemsListTable.appendChild(headerRow);
+ },
+ function listSport(sport, row) {
+ const nameCell = document.createElement('td');
+ nameCell.textContent = sport.name;
+ row.appendChild(nameCell);
+
+ const spacerCell = document.createElement('td');
+ row.appendChild(spacerCell);
+ },
+ async function addSport() {
+ window.location.href = `/manage/sport`;
+ },
+ async function editSport(id) {
+ window.location.href = `/manage/sport?sport=${id}`
+ }
+));
+
+CATEGORIES.push(new Category(
+ "divisions",
+ async function getDivisions() {
+ return await Data.getDivisions();
+ },
+ async function listDivisionHeaders() {
+ const headerRow = document.createElement('tr');
+
+ const nameHeader = document.createElement('th');
+ nameHeader.textContent = "Name";
+ headerRow.appendChild(nameHeader);
+
+ const genderHeader = document.createElement('th');
+ headerRow.appendChild(genderHeader);
+
+ const spacerHeader = document.createElement('th');
+ spacerHeader.classList.add('spacer-column');
+ headerRow.appendChild(spacerHeader);
+
+ const sportHeader = document.createElement('th');
+ sportHeader.textContent = "Sport";
+ headerRow.appendChild(sportHeader);
+
+ itemsListTable.appendChild(headerRow);
+ },
+ function listDivision(division, row) {
+ const nameCell = document.createElement('td');
+ nameCell.textContent = division.name;
+ row.appendChild(nameCell);
+
+ const genderCell = document.createElement('td');
+ const gender = getGenderLetter(division.gender.name);
+ genderCell.textContent = gender;
+ row.appendChild(genderCell);
+
+ const spacerCell = document.createElement('td');
+ row.appendChild(spacerCell);
+
+ const sportCell = document.createElement('td');
+ Data.getSportName(division.sportID)
+ .then(data => sportCell.textContent = data);
+ row.appendChild(sportCell);
+ },
+ async function addDivision() {
+ window.location.href = "/manage/division";
+ },
+ async function editDivision(id) {
+ window.location.href = `/manage/division?division=${id}`
+ }
+));
+
+CATEGORIES.push(new Category(
+ "teams",
+ async function getTeams() {
+ return await Data.getTeams();
+ },
+ async function listTeamHeaders() {
+ const headerRow = document.createElement('tr');
+
+ const nameHeader = document.createElement('th');
+ nameHeader.textContent = "Name";
+ headerRow.appendChild(nameHeader);
+
+ const spacerHeader = document.createElement('th');
+ spacerHeader.classList.add('spacer-column');
+ headerRow.appendChild(spacerHeader);
+
+ const sportHeader = document.createElement('th');
+ sportHeader.textContent = "Sport";
+ headerRow.appendChild(sportHeader);
+
+ itemsListTable.appendChild(headerRow);
+ },
+ function listTeam(team, row) {
+ const nameCell = document.createElement('td');
+ nameCell.textContent = team.name;
+ row.appendChild(nameCell);
+
+ const spacerCell = document.createElement('td');
+ row.appendChild(spacerCell);
+
+ const sportCell = document.createElement('td');
+ Data.getSportName(team.sportID)
+ .then(data => sportCell.textContent = data);
+ row.appendChild(sportCell);
+ },
+ async function addTeam() {
+ window.location.href = "/manage/team";
+ },
+ async function editTeam(id) {
+ window.location.href = `/manage/team?team=${id}`;
+ }
+));
+
+CATEGORIES.push(new Category(
+ "games",
+ async function getGames() {
+ return await Data.getGames();
+ },
+ 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);
+
+ itemsListTable.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}`;
+ }
+));
+
+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) {
+ itemsListTable.innerHTML = "";
+
+ const itemsList = await category.getItems();
+
+ await category.listHeaders();
+
+ itemsList.forEach(item => {
+ const row = document.createElement('tr');
+
+ category.listItem(item, row);
+
+ const manageCell = document.createElement('td');
+
+ const editSpan = document.createElement('span');
+ const editButton = document.createElement('button');
+ editButton.textContent = "E";
+ editButton.addEventListener('click', () => {
+ CATEGORIES[categoryDropdown.selectedIndex].editItem(item.id);
+ });
+ editSpan.appendChild(editButton);
+ manageCell.appendChild(editSpan);
+
+ row.appendChild(manageCell);
+
+ itemsListTable.appendChild(row);
+ });
+}
+listItems(CATEGORIES[categoryDropdown.selectedIndex]);
+
+
+
+categoryDropdown.onchange = () => {
+ listItems(CATEGORIES[categoryDropdown.selectedIndex]);
+};
+addNewButton.addEventListener('click', () => CATEGORIES[categoryDropdown.selectedIndex].addItem());
\ No newline at end of file
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/division.js b/public/scripts/manage/division.js
new file mode 100644
index 0000000..a542a65
--- /dev/null
+++ b/public/scripts/manage/division.js
@@ -0,0 +1,62 @@
+import * as Data from "../data.js";
+import * as Form from "../form.js";
+
+
+const submissionForm = document.getElementById('submission-form');
+const sportDropdown = document.getElementById('sport-dropdown');
+const genderDropdown = document.getElementById('gender-dropdown');
+const nameTextbox = document.getElementById('name-textbox');
+const submitButton = document.getElementById('submit-button');
+const deleteButton = document.getElementById('delete-button');
+
+
+async function initializeForm() {
+ let params = new URLSearchParams(location.search);
+ let divisionID = params.get('division');
+ if(divisionID) {
+ const division = await Data.getDivision(divisionID);
+
+ nameTextbox.value = division.name;
+
+ deleteButton.style.visibility = "visible";
+ deleteButton.disabled = false;
+
+ const gender = division.gender.name;
+
+ if(gender == 'female') genderDropdown.selectedIndex = 1;
+ else genderDropdown.selectedIndex = 2;
+
+ Form.populateSports(sportDropdown, division.sportID);
+
+ Form.addHiddenValue('division', divisionID, submissionForm);
+ }
+ else {
+ Form.populateSports(sportDropdown);
+
+ genderDropdown.disabled = false;
+
+ sportDropdown.disabled = false;
+ }
+
+ nameTextbox.disabled = false;
+ nameTextbox.addEventListener('keyup', checkDataValidity);
+}
+initializeForm();
+
+async function checkDataValidity() {
+ let dataIsValid = true;
+
+ if(!nameTextbox.value) dataIsValid = false;
+
+ const sportHasContent = sportDropdown.options.length;
+ const genderHasContent = genderDropdown.options.length;
+
+ if(!sportHasContent || !genderHasContent) dataIsValid = false;
+
+
+ if(dataIsValid) submitButton.disabled = false;
+ else submitButton.disabled = true;
+}
+
+Form.addRemoveFunction(deleteButton, submissionForm, "division");
+
diff --git a/public/scripts/manage/game.js b/public/scripts/manage/game.js
new file mode 100644
index 0000000..ce80b97
--- /dev/null
+++ b/public/scripts/manage/game.js
@@ -0,0 +1,106 @@
+import * as Data from "./../data.js";
+import * as Form from "./../form.js";
+
+
+const submissionForm = document.getElementById('submission-form');
+const sportDropdown = document.getElementById('sport-dropdown');
+const seasonDropdown = document.getElementById('year-dropdown');
+const genderDropdown = document.getElementById('gender-dropdown');
+const divisionDropdown = document.getElementById('division-dropdown');
+const dateInput = document.getElementById('date-input');
+const team1Dropdown = document.getElementById('team1-dropdown');
+const team2Dropdown = document.getElementById('team2-dropdown');
+const team1ScoreTextbox = document.getElementById('team1-score-textbox');
+const team2ScoreTextbox = document.getElementById('team2-score-textbox');
+const submitButton = document.getElementById('submit-button');
+const deleteButton = document.getElementById('delete-button');
+
+
+async function initializeForm() {
+ let params = new URLSearchParams(location.search);
+ let gameID = params.get('game');
+ if(gameID) {
+ deleteButton.style.visibility = "visible";
+ deleteButton.disabled = false;
+
+ const game = await Data.getGame(gameID);
+
+ Form.addHiddenValue('game', gameID, submissionForm);
+
+ Form.populateSeasons(seasonDropdown, game.seasonID);
+ const data = await Data.getDivision(game.divisionID)
+ await Form.populateSports(sportDropdown, data.sportID)
+ await Form.populateGenders(genderDropdown, sportDropdown.value, data.gender.name)
+ await Form.populateDivisions(divisionDropdown, sportDropdown.value, genderDropdown.value, game.divisionID);
+ await Form.populateTeams(team1Dropdown, sportDropdown.value, game.team1ID);
+ await Form.populateTeams(team2Dropdown, sportDropdown.value, game.team2ID);
+
+ dateInput.value = game.date;
+ team1ScoreTextbox.value = game.team1Score;
+ team2ScoreTextbox.value = game.team2Score;
+ }
+ else {
+ await Form.populateSeasons(seasonDropdown);
+ await Form.populateSports(sportDropdown)
+ await Form.populateGenders(genderDropdown, sportDropdown.value)
+ await Form.populateDivisions(divisionDropdown, sportDropdown.value, genderDropdown.value);
+ await Form.populateTeams(team1Dropdown, sportDropdown.value);
+ await Form.populateTeams(team2Dropdown, sportDropdown.value);
+
+ dateInput.value = (new Date()).toISOString().slice(0,10);
+ }
+ seasonDropdown.disabled = false;
+ sportDropdown.disabled = false;
+ genderDropdown.disabled = false;
+ divisionDropdown.disabled = false;
+ dateInput.disabled = false;
+ team1Dropdown.disabled = false;
+ team2Dropdown.disabled = false;
+ team1ScoreTextbox.disabled = false;
+ team2ScoreTextbox.disabled = false;
+
+ sportDropdown.onchange = async () => {
+ await Form.populateGenders(genderDropdown, sportDropdown.value)
+ await Form.populateDivisions(divisionDropdown, sportDropdown.value, genderDropdown.value);
+ await Form.populateTeams(team1Dropdown, sportDropdown.value);
+ await Form.populateTeams(team2Dropdown, sportDropdown.value);
+ checkDataValidity();
+ };
+
+ genderDropdown.onchange = async () => {
+ await Form.populateDivisions(divisionDropdown, sportDropdown.value, genderDropdown.value);
+ checkDataValidity();
+ };
+
+ dateInput.addEventListener('keyup', checkDataValidity);
+ team1Dropdown.onchange = checkDataValidity;
+ team1ScoreTextbox.addEventListener('keyup', checkDataValidity);
+ team2Dropdown.onchange = checkDataValidity;
+ team2ScoreTextbox.addEventListener('keyup', checkDataValidity);
+
+ checkDataValidity();
+}
+initializeForm();
+
+async function checkDataValidity() {
+ let dataIsValid = true;
+
+ const seasonHasContent = seasonDropdown.options.length;
+ const sportHasContent = sportDropdown.options.length;
+ const genderHasContent = genderDropdown.options.length;
+ const divisionHasContent = divisionDropdown.options.length;
+ const team1HasContent = team1Dropdown.options.length;
+ const team2HasContent = team2Dropdown.options.length;
+
+ if(!seasonHasContent || !sportHasContent || !genderHasContent || !divisionHasContent || !team1HasContent || !team2HasContent) dataIsValid = false;
+
+ if(team1Dropdown.selectedIndex == team2Dropdown.selectedIndex) dataIsValid = false;
+
+ if(team1ScoreTextbox.value == "" || team2ScoreTextbox.value == "") dataIsValid = false;
+
+ if(dateInput.value == "") dataIsValid = false;
+
+ submitButton.disabled = !dataIsValid;
+}
+
+Form.addRemoveFunction(deleteButton, submissionForm, "game");
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/season.js b/public/scripts/manage/season.js
new file mode 100644
index 0000000..82d0204
--- /dev/null
+++ b/public/scripts/manage/season.js
@@ -0,0 +1,14 @@
+const seasonTextbox = document.getElementById('season-textbox');
+const submitButton = document.getElementById('submit-button');
+
+async function checkDataValidity() {
+ let dataIsValid = true;
+
+ if(seasonTextbox.value == "") dataIsValid = false;
+
+ submitButton.disabled = !dataIsValid;
+}
+checkDataValidity();
+
+seasonTextbox.addEventListener('keyup', checkDataValidity);
+seasonTextbox.addEventListener('change', checkDataValidity);
diff --git a/public/scripts/manage/sport.js b/public/scripts/manage/sport.js
new file mode 100644
index 0000000..20ad2cb
--- /dev/null
+++ b/public/scripts/manage/sport.js
@@ -0,0 +1,55 @@
+import * as Data from "../data.js";
+
+
+const nameTextbox = document.getElementById('name-textbox');
+const submitButton = document.getElementById('submit-button');
+const deleteButton = document.getElementById('delete-button');
+const submissionForm = document.getElementById('submission-form');
+
+
+async function initializeForm() {
+ let params = new URLSearchParams(location.search);
+ let sportID = params.get('sport')
+ if(sportID) {
+ const sportName = await Data.getSportName(sportID);
+
+ nameTextbox.value = sportName;
+ deleteButton.style.visibility = "visible";
+ deleteButton.disabled = false;
+
+ const sportIDInput = document.createElement('input');
+ sportIDInput.setAttribute('name', 'sport');
+ sportIDInput.setAttribute('value', sportID);
+ sportIDInput.setAttribute('type', 'hidden');
+ submissionForm.appendChild(sportIDInput);
+ }
+ nameTextbox.disabled = false;
+
+ nameTextbox.addEventListener('keyup', checkDataValidity);
+}
+initializeForm();
+
+async function checkDataValidity() {
+ let dataIsValid = true;
+
+ if(!nameTextbox.value) dataIsValid = false;
+
+
+ if(dataIsValid) submitButton.disabled = false;
+ else submitButton.disabled = true;
+}
+
+async function removeSport() {
+ const removeInput = document.createElement('input');
+ removeInput.setAttribute('name', 'remove');
+ removeInput.setAttribute('value', 1);
+ removeInput.setAttribute('type', 'hidden');
+ submissionForm.appendChild(removeInput);
+ submissionForm.submit();
+}
+
+deleteButton.addEventListener('click', () => {
+ const verified = confirm("This sport will be removed.");
+
+ if(verified) removeSport();
+});
\ No newline at end of file
diff --git a/public/scripts/manage/team.js b/public/scripts/manage/team.js
new file mode 100644
index 0000000..4bc643e
--- /dev/null
+++ b/public/scripts/manage/team.js
@@ -0,0 +1,48 @@
+import * as Data from "../data.js";
+import * as Form from "../form.js";
+
+
+const submissionForm = document.getElementById('submission-form');
+const sportDropdown = document.getElementById('sport-dropdown');
+const nameTextbox = document.getElementById('name-textbox');
+const submitButton = document.getElementById('submit-button');
+const deleteButton = document.getElementById('delete-button');
+
+async function initializeForm() {
+ let params = new URLSearchParams(location.search);
+ let teamID = params.get('team');
+ if(teamID) {
+ const team = await Data.getTeam(teamID);
+
+ nameTextbox.value = team.name;
+
+ deleteButton.style.visibility = "visible";
+ deleteButton.disabled = false;
+
+ Form.populateSports(sportDropdown, team.sportID);
+
+ Form.addHiddenValue('team', teamID, submissionForm);
+ }
+ else {
+ sportDropdown.disabled = false;
+
+ Form.populateSports(sportDropdown);
+ }
+
+ nameTextbox.disabled = false;
+ nameTextbox.addEventListener('keyup', checkDataValidity);
+}
+initializeForm();
+
+async function checkDataValidity() {
+ let dataIsValid = true;
+
+ if(!nameTextbox.value) dataIsValid = false;
+
+ const sportHasContent = sportDropdown.options.length;
+ if(!sportHasContent) dataIsValid = false;
+
+ submitButton.disabled = !dataIsValid;
+}
+
+Form.addRemoveFunction(deleteButton, submissionForm, "team");
\ No newline at end of file
diff --git a/public/stylesheets/error.css b/public/stylesheets/error.css
new file mode 100644
index 0000000..f1ac617
--- /dev/null
+++ b/public/stylesheets/error.css
@@ -0,0 +1,3 @@
+#mobile-view {
+ max-width: 100%;
+}
\ No newline at end of file
diff --git a/public/stylesheets/form.css b/public/stylesheets/form.css
new file mode 100644
index 0000000..43fee2e
--- /dev/null
+++ b/public/stylesheets/form.css
@@ -0,0 +1,51 @@
+form {
+ display: flex;
+ flex-direction: column;
+ }
+
+ span {
+ display: flex;
+ flex-direction: column;
+ }
+
+
+ .form-main-dropdown {
+ width: 100%;
+ }
+
+ .form-section {
+ margin-bottom: 0.75em;
+ }
+
+ .form-section-input {
+ flex-direction: row;
+ }
+
+ input {
+ width: 100%;
+ }
+
+ .form-score-input{
+ width: 35%;
+ }
+
+ #submit-button {
+ margin-top: 1.5em;
+ }
+
+ #delete-button {
+ visibility: hidden;
+ }
+
+ .form-section-checkbox {
+ flex-direction: row;
+ align-items: center;
+ }
+
+ #admin-checkbox {
+ width: auto;
+ }
+
+ .flat-form-section {
+ flex-direction: row;
+ }
\ No newline at end of file
diff --git a/public/stylesheets/index.css b/public/stylesheets/index.css
new file mode 100644
index 0000000..d08e1e8
--- /dev/null
+++ b/public/stylesheets/index.css
@@ -0,0 +1,34 @@
+h1 {
+ text-align: left;
+}
+
+th {
+ text-align: left;
+}
+
+#score-column {
+ width: 20%;
+}
+#opponent-column{
+ width: 60%;
+}
+#date-column {
+ width: 20%;
+}
+
+tr {
+ height: 3em;
+}
+
+#header-div {
+ display: flex;
+ flex-direction: row;
+}
+
+#add-score-button {
+ margin-right: auto;
+}
+
+#login-button {
+ margin-left: auto;
+}
\ No newline at end of file
diff --git a/public/stylesheets/manage.css b/public/stylesheets/manage.css
new file mode 100644
index 0000000..6a5359f
--- /dev/null
+++ b/public/stylesheets/manage.css
@@ -0,0 +1,11 @@
+th {
+ text-align: left;
+}
+
+td {
+ white-space: nowrap;
+}
+
+.spacer-column {
+ width: 100%;
+}
\ No newline at end of file
diff --git a/public/stylesheets/style.css b/public/stylesheets/style.css
index 6a727df..c4252bd 100644
--- a/public/stylesheets/style.css
+++ b/public/stylesheets/style.css
@@ -7,3 +7,51 @@ a {
color: #00B7FF;
}
+#mobile-view {
+ max-width: 20em;
+ margin-left: auto;
+ margin-right: auto;
+ display: flex;
+ flex-direction: column;
+}
+
+.flat-content {
+ flex-direction: row;
+ justify-content: space-between;
+}
+
+.send-to-right {
+ margin-left: auto;
+}
+
+#actions-div {
+ display: flex;
+}
+
+#home-button {
+ margin-right: auto;
+}
+
+button {
+ padding: 0.25em;
+ margin: 0em 0.1em;
+}
+
+select {
+ padding: 0.25em;
+}
+
+input {
+ padding: 0.25em;
+}
+
+#logout-button {
+ margin-left: 0.5em;
+}
+
+#noscript-message {
+ background-color: lightcoral;
+ padding: 1em;
+ margin-bottom: 3em;
+ border-radius: .25em;
+}
\ No newline at end of file
diff --git a/public/stylesheets/submit.css b/public/stylesheets/submit.css
index 89dd41c..30e64a5 100644
--- a/public/stylesheets/submit.css
+++ b/public/stylesheets/submit.css
@@ -2,40 +2,6 @@ h1 {
text-align: center;
}
-form {
- display: flex;
- flex-direction: column;
- max-width: 20em;
- margin-left: auto;
- margin-right: auto;
-}
-
-span {
- display: flex;
- flex-direction: column;
-}
-
-
-#sport-dropdown {
- width: 100%;
-}
-
-.form-section {
- margin-bottom: 0.75em;
-}
-
-.form-section-input {
- flex-direction: row;
-}
-
-input {
- width: 100%;
-}
-
-.score-input{
- width: 35%;
-}
-
-button {
- margin-top: 1.5em;
+#admin-checkbox-section {
+ visibility: hidden;
}
\ No newline at end of file
diff --git a/routes/auth.js b/routes/auth.js
new file mode 100644
index 0000000..1e1940b
--- /dev/null
+++ b/routes/auth.js
@@ -0,0 +1,27 @@
+var express = require('express');
+var router = express.Router();
+const passport = require('passport');
+const app = require('../app');
+
+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/checkLoginStatus.js b/routes/checkLoginStatus.js
new file mode 100644
index 0000000..d9fd970
--- /dev/null
+++ b/routes/checkLoginStatus.js
@@ -0,0 +1,21 @@
+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');
+ }
+}
+
+exports.admin = adminLoggedIn;
+exports.user = userLoggedIn;
\ No newline at end of file
diff --git a/routes/data.js b/routes/data.js
new file mode 100644
index 0000000..dfe6b8d
--- /dev/null
+++ b/routes/data.js
@@ -0,0 +1,161 @@
+var express = require('express');
+var router = express.Router();
+
+var sports = require('../database/scores/sports');
+var seasons = require('../database/scores/seasons');
+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');
+
+var checkLoginStatus = require('./checkLoginStatus');
+
+router.get('/sports', async function(req, res, next) {
+ try {
+ const data = await sports.retrieveAll();
+ res.json(data);
+ } catch(err) {
+ console.error("ERROR: " + err.message);
+ res.status(500).send("An error has occurred");
+ }
+});
+
+router.get('/sport', async function(req, res, next) {
+ try {
+ const sportID = req.query.sport;
+ const data = await sports.getFromID(sportID);
+ res.json(data);
+ } catch(err) {
+ console.error("ERROR: " + err.message);
+ res.status(500).send("An error has occurred");
+ }
+});
+
+router.get('/seasons', async function(req, res, next) {
+ try {
+ const data = await seasons.retrieveAll();
+ res.json(data);
+ } catch(err) {
+ console.error("ERROR: " + err.message);
+ res.status(500).send("An error has occurred");
+ }
+})
+
+router.get('/genders', async function(req, res, next) {
+ try {
+ const sportID = req.query.sport;
+ const data = await genders.retrieveBySport(sportID);
+ res.json(data);
+ } catch(err) {
+ console.error("ERROR: " + err.message);
+ res.status(500).send("An error has occurred");
+ }
+})
+
+router.get('/divisions', async function(req, res, next) {
+ try{
+ let gender;
+ if(req.query.gender) gender = (req.query.gender == 'female' ? genders.FEMALE : genders.MALE);
+
+ const sportID = req.query.sport;
+ const data = await divisions.retrieve(sportID, gender);
+ res.json(data);
+ } catch(err) {
+ console.error("ERROR: " + err.message);
+ res.status(500).send("An error has occurred");
+ }
+})
+
+router.get('/division', async function(req, res, next) {
+ try {
+ const divisionID = req.query.division;
+ const data = await divisions.getFromID(divisionID);
+ res.json(data);
+ } catch(err) {
+ console.error("ERROR: " + err.message);
+ res.status(500).send("An error has occurred");
+ }
+})
+
+router.get('/teams', async function(req, res, next) {
+ try {
+ const sportID = req.query.sport;
+ const data = await teams.retrieve(sportID);
+ res.json(data);
+ } catch(err) {
+ console.error("ERROR: " + err.message);
+ res.status(500).send("An error has occurred");
+ }
+})
+
+router.get('/team', async function(req, res, next) {
+ try {
+ const teamID = req.query.team;
+ const data = await teams.getFromID(teamID);
+ res.json(data);
+ } catch(err) {
+ console.error("ERROR: " + err.message);
+ res.status(500).send("An error has occurred");
+ }
+})
+
+router.get('/games', async function(req, res, next) {
+ try {
+ const userID = req.user ? req.user[0] : null;
+ if(req.query.user) {
+ const data = await games.retrieveByUser(userID);
+ res.json(data);
+ } else {
+ const seasonID = req.query.season;
+ const divisionID = req.query.division;
+ const teamID = req.query.team;
+ const data = await games.retrieve(teamID, divisionID, seasonID);
+ res.json(data);
+ }
+ } catch(err) {
+ console.error("ERROR: " + err.message);
+ res.status(500).send("An error has occurred");
+ }
+})
+
+router.get('/game', async function(req, res, next) {
+ try {
+ const gameID = req.query.game;
+ const data = await games.getFromID(gameID);
+ res.json(data);
+ } catch(err) {
+ console.error("ERROR: " + err.message);
+ res.status(500).send("An error has occurred");
+ }
+})
+
+router.get('/accounts', checkLoginStatus.admin, async function(req, res, next) {
+ try {
+ const data = await accounts.retrieveAll();
+ res.json(data);
+ } catch(err) {
+ console.error("ERROR: " + err.message);
+ res.status(500).send("An error has occurred");
+ }
+})
+
+router.get('/account', checkLoginStatus.user, async function(req, res, next) {
+ try{
+ 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 {
+ const data = await accounts.getFromID(req.query.account);
+ res.json(data);
+ }
+ } catch(err) {
+ console.error("ERROR: " + err.message);
+ res.status(500).send("An error has occurred");
+ }
+})
+
+module.exports = router;
\ No newline at end of file
diff --git a/routes/index.js b/routes/index.js
index 9399195..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: 'Submit Score' });
+ res.render('index', { title: 'View Scores', userLoggedIn: !!req.user, hideHomeButton: true });
});
module.exports = router;
diff --git a/routes/manage.js b/routes/manage.js
new file mode 100644
index 0000000..8432ff8
--- /dev/null
+++ b/routes/manage.js
@@ -0,0 +1,224 @@
+var express = require('express');
+var router = express.Router();
+
+var genders = require('../database/scores/genders');
+var games = require('../database/scores/games');
+var seasons = require('../database/scores/seasons');
+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');
+
+var checkLoginStatus = require('./checkLoginStatus');
+
+
+router.get('/' ,checkLoginStatus.user, 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', checkLoginStatus.user, function(req, res, next) {
+ let title = req.query.game ? 'Edit Game' : 'Submit Score'
+
+ res.render('manage/addgame', { title, userLoggedIn: !!req.user, message: req.flash('error') });
+});
+
+router.post('/game', checkLoginStatus.user, async function(req, res, next) {
+ const id = req.body['game'];
+ const remove = req.body['remove'];
+
+ try {
+ const seasonID = req.body['year'];
+ const sportID = req.body['sport'];
+ const gender = (req.body['gender'] == "female") ? genders.FEMALE : genders.MALE;
+ const divisionID = req.body['division'];
+ const date = req.body['date'];
+ const team1ID = req.body['team1'];
+ const team1Score = req.body['team1-score'];
+ const team2ID = req.body['team2'];
+ const team2Score = req.body['team2-score'];
+ const userID = req.user[0];
+
+ const loggedInUserID = req.user[0];
+ const loggedInUserIsAdmin = req.user[2];
+
+ const game = id ? await games.getFromID(id) : null;
+
+ if(!loggedInUserIsAdmin && game && loggedInUserID != game.submitterID) {
+ res.status(403).send("ACCESS DENIED");
+ }
+ else if(remove) {
+ await games.remove(id);
+ res.redirect("/manage");
+ }
+ else if(id) {
+ await games.edit(id, divisionID, seasonID, date, team1ID, team2ID, team1Score, team2Score);
+ res.redirect('/manage');
+ }
+ else {
+ await games.add(divisionID, seasonID, date, team1ID, team2ID, team1Score, team2Score, userID);
+ res.redirect('/');
+ }
+ } catch(err) {
+ console.error("ERROR: " + err.message);
+ req.flash("error", "An error has occurred.");
+ res.redirect('/manage/game' + (id ? `?game=${id}` : ''));
+ }
+});
+
+router.get('/season', checkLoginStatus.admin, function(req, res, next) {
+ res.render('manage/addseason', { title: 'Add Season', currentYear : (new Date()).getFullYear(), userLoggedIn: !!req.user, message: req.flash('error') });
+});
+
+router.post('/season', checkLoginStatus.admin, async function(req, res, next) {
+ const seasonID = req.body['season'];
+ const remove = req.body['remove'];
+
+ try {
+ const year = req.body['year'];
+
+ if(remove) await seasons.remove(seasonID);
+ else await seasons.add(year);
+
+ res.redirect('/manage');
+ } catch(err) {
+ console.error("ERROR: " + err.message);
+ req.flash("error", "An error has occurred.");
+ res.redirect('/manage/season' + (seasonID ? `?season=${seasonID}` : ''));
+ }
+});
+
+router.get('/sport', checkLoginStatus.admin, function(req, res, next) {
+ let title = req.query.sport ? 'Edit Sport' : 'Add Sport';
+
+ res.render('manage/addsport', { title, userLoggedIn: !!req.user, message: req.flash('error') });
+});
+
+router.post('/sport', checkLoginStatus.admin, async function(req, res, next) {
+ const id = req.body['sport'];
+ const remove = req.body['remove'];
+
+ try {
+ const name = req.body['name'];
+
+ if(remove) await sports.remove(id);
+ else if(id) await sports.rename(id, name);
+ else await sports.add(name);
+
+ res.redirect('/manage');
+ } catch(err) {
+ console.error("ERROR: " + err.message);
+ req.flash("error", "An error has occurred.");
+ res.redirect('/manage/sport' + (id ? `?sport=${id}` : ''));
+ }
+});
+
+router.get('/division', checkLoginStatus.admin, function(req, res, next) {
+ let title = req.query.division ? 'Edit Division' : 'Add Division'
+
+ res.render('manage/adddivision', { title, userLoggedIn: !!req.user, message: req.flash('error') });
+});
+
+router.post('/division', checkLoginStatus.admin, async function(req, res, next) {
+ const id = req.body['division'];
+ const remove = req.body['remove'];
+
+ try {
+ const name = req.body['name'];
+ const sport = req.body['sport'];
+ const genderName = req.body['gender'];
+
+ if(remove) await divisions.remove(id);
+ else if(id) await divisions.rename(id, name);
+ else {
+ if(genderName == 'both') {
+ await divisions.add(name, genders.FEMALE, sport);
+ await divisions.add(name, genders.MALE, sport);
+ } else {
+ const gender = (genderName == "female") ? genders.FEMALE : genders.MALE;
+ await divisions.add(name, gender, sport);
+ }
+ }
+ res.redirect('/manage');
+ } catch(err) {
+ console.error("ERROR: " + err.message);
+ req.flash("error", "An error has occurred.");
+ res.redirect('/manage/division' + (id ? `?division=${id}` : ''));
+ }
+});
+
+router.get('/team', checkLoginStatus.admin, function(req, res, next) {
+ let title = req.query.team ? 'Edit Team' : 'Add Team'
+
+ res.render('manage/addteam', { title, userLoggedIn: !!req.user, message: req.flash('error') });
+});
+
+router.post('/team', checkLoginStatus.admin, async function(req, res, next) {
+ const id = req.body['team'];
+ const remove = req.body['remove'];
+
+ try {
+ const name = req.body['name'];
+ const sport = req.body['sport'];
+
+ if(remove) teams.remove(id).then(res.redirect('/manage'));
+ else if(id) teams.rename(id, name).then(res.redirect('/manage'));
+ else teams.add(name, sport).then(res.redirect("/manage"));
+ } catch(err) {
+ console.error("ERROR: " + err.message);
+ req.flash("error", "An error has occurred.");
+ res.redirect('/manage/team' + (id ? `?team=${id}` : ''));
+ }
+});
+
+router.get('/account', checkLoginStatus.user, (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, message: req.flash('error') });
+ }
+ else {
+ let title = 'Manage Account';
+
+ res.render('accounts/createuser', { title, accountID, userLoggedIn: !!req.user, message: req.flash('error') });
+ }
+});
+
+router.post('/account', checkLoginStatus.user, async function(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];
+
+ if(!loggedInAccountIsAdmin && accountID != loggedInAccountID) {
+ res.status(403).send("ACCESS DENIED");
+ }
+ else {
+ try {
+ const isAdmin = loggedInAccountIsAdmin ? !!req.body.admin : false;
+
+ if(remove) await accounts.remove(accountID);
+ else if(accountID) await accounts.edit(accountID, email, password, isAdmin);
+ else await accounts.create(req.body.email, req.body.password, !!req.body.admin);
+
+ res.redirect('/manage');
+ }
+ catch (err) {
+ console.error("ERROR: " + err.message);
+ req.flash("error", "An error has occurred.");
+ let URL = '/manage/account';
+ if(loggedInAccountIsAdmin && accountID) URL += `?account=${accountID}`;
+ res.redirect(URL);
+ }
+ }
+});
+
+module.exports = router;
diff --git a/routes/submit.js b/routes/submit.js
deleted file mode 100644
index 30901be..0000000
--- a/routes/submit.js
+++ /dev/null
@@ -1,38 +0,0 @@
-var express = require('express');
-var mail = require('../mail/mail');
-var router = express.Router();
-
-/* GET submit page. */
-router.get('/', function(req, res, next) {
- res.send('Nothing to send');
-});
-
-/* POST submit page. */
-router.post('/', function(req, res, next) {
- let sport = req.body.sport;
- let gender = req.body.gender;
- let division = req.body.division;
- let home = req.body['home-team'];
- let homeScore = req.body['home-team-score'];
- let visiting = req.body['visiting-team'];
- let visitingScore = req.body['visiting-team-score'];
- let submitter = req.body['submitter'];
- let recipient = req.body['email'];
-
- let message = prepMailBody(sport, gender, division, home, homeScore, visiting, visitingScore, submitter);
-
- mail.send(recipient, "Score Report", message);
-
- res.send('Score sent');
-});
-
-var prepMailBody = function(sport, gender, division, home, homeScore, visiting, visitingScore, submitter) {
- return(
- "Score report from " + submitter + "
" +
- "Sport:
" + sport + " - " + gender + " - " + division + "
" +
- "Home team:
" + home + " - " + homeScore + "
" +
- "Visiting team:
" + visiting + " - " + visitingScore + "
");
-};
-
-
-module.exports = router;
diff --git a/routes/users.js b/routes/users.js
deleted file mode 100644
index 623e430..0000000
--- a/routes/users.js
+++ /dev/null
@@ -1,9 +0,0 @@
-var express = require('express');
-var router = express.Router();
-
-/* GET users listing. */
-router.get('/', function(req, res, next) {
- res.send('respond with a resource');
-});
-
-module.exports = router;
diff --git a/test/test.js b/test/test.js
deleted file mode 100644
index 4f2ac60..0000000
--- a/test/test.js
+++ /dev/null
@@ -1,10 +0,0 @@
-const request = require('supertest');
-const app = require('../app');
-
-describe('App', function() {
- it('has the default page', function(done) {
- request(app)
- .get('/')
- .expect(/Welcome to Express/, done);
- });
-});
diff --git a/views/accounts/createuser.pug b/views/accounts/createuser.pug
new file mode 100644
index 0000000..dfc1b13
--- /dev/null
+++ b/views/accounts/createuser.pug
@@ -0,0 +1,30 @@
+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
+ .error #{message}
+ 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/error.pug b/views/error.pug
index 51ec12c..43a7357 100644
--- a/views/error.pug
+++ b/views/error.pug
@@ -1,6 +1,9 @@
extends layout
+block stylesheets
+ link(rel='stylesheet', href='/stylesheets/error.css')
+
block content
h1= message
h2= error.status
- pre #{error.stack}
+ pre #{error.stack}
\ No newline at end of file
diff --git a/views/index.pug b/views/index.pug
index 7f6baf1..a1b7d2d 100644
--- a/views/index.pug
+++ b/views/index.pug
@@ -1,40 +1,48 @@
extends layout
block stylesheets
- link(rel='stylesheet', href='/stylesheets/submit.css')
+ 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
- h1 Submit Score
- form(action='/submit', method='POST')
- span(class='form-section')
- label Sport
- span(class='form-section-input')
- select#sport-dropdown(name="sport")
- option(value="Football" selected) Football
- select#gender-dropdown(name="gender")
- option(value="Male" selected) Male
- option(value="Female") Female
- select#division-dropdown(name="division")
- option(value="Varsity") Varsity
- option(value="JV-A") JV-A
- option(value="JV-B") JV-B
- span(class='form-section')
- label Home Team
- span(class='form-section-input')
- input(type="text", name="home-team")
- input(class="score-input", type="number", name="home-team-score", value="0")
- span(class='form-section')
- label Visiting Team
- span(class='form-section-input')
- input(type="text", name="visiting-team")
- input(class="score-input", type="number", name="visiting-team-score", value="0")
- span(class='form-section')
- label Submitter
- span(class='form-section-input')
- input(type="text", name="submitter")
- span(class='form-section')
- label Send info to
- span(class='form-section-input')
- input(type="email", name="email", placeholder="email@example.com")
- span(class='form-section')
- button(type="submit") Submit
+ div
+ span(class='form-section')
+ label Year
+ span(class='form-section-input')
+ select#year-dropdown(name="year" class="form-main-dropdown")
+ span(class='form-section flat-form-section')
+ span
+ label Sport
+ span(class='form-section-input')
+ select#sport-dropdown(name="sport" class="form-main-dropdown")
+ span
+ label Gender
+ span(class='form-section-input')
+ select#gender-dropdown(name="gender")
+ span
+ label Division
+ span(class='form-section-input')
+ select#division-dropdown(name="division")
+ span(class='form-section')
+ label Team
+ span(class='form-section-input')
+ select#team-dropdown(name="team" class="form-main-dropdown")
+ div
+ h2#games-table-header
+ span#no-scores-message
+ table
+ colgroup
+ col#score-column(span="1")
+ col#opponent-column(span="1")
+ col#date-column(span="1")
+ tbody#games-table
+
+
+block scripts
+ script(src='/scripts/index.js' type="module")
\ No newline at end of file
diff --git a/views/layout.pug b/views/layout.pug
index 69f5a59..80d0238 100644
--- a/views/layout.pug
+++ b/views/layout.pug
@@ -1,9 +1,23 @@
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
+ noscript
+ span#noscript-message Please enable JavaScript to run this app.
+ 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
new file mode 100644
index 0000000..63d76ec
--- /dev/null
+++ b/views/manage.pug
@@ -0,0 +1,25 @@
+extends layout
+
+block stylesheets
+ link(rel='stylesheet', href='/stylesheets/manage.css')
+ link(rel='stylesheet', href='/stylesheets/form.css')
+
+block content
+ div
+ span(class='form-section')
+ label Category
+ span(class='form-section-input')
+ select#category-dropdown(name="category" class="form-main-dropdown")
+ option(value="seasons") Seasons
+ option(value="sports") Sports
+ option(value="divisions") Divisions
+ option(value="teams") Teams
+ option(value="games") Games
+ option(value="accounts") Accounts
+ div
+ h2#table-header
+ table#items-list
+ button#add-new-button Add new...
+
+block scripts
+ script(src='/scripts/manage.js' type="module")
\ No newline at end of file
diff --git a/views/manage/adddivision.pug b/views/manage/adddivision.pug
new file mode 100644
index 0000000..1a17aaf
--- /dev/null
+++ b/views/manage/adddivision.pug
@@ -0,0 +1,31 @@
+extends ../layout
+
+block stylesheets
+ link(rel='stylesheet', href='/stylesheets/submit.css')
+ link(rel='stylesheet', href='/stylesheets/form.css')
+
+block content
+ form#submission-form(action='./division', method='POST')
+ span(class='form-section')
+ label Sport
+ span(class='form-section-input')
+ select#sport-dropdown(name="sport" class="form-main-dropdown" disabled)
+ span(class='form-section')
+ label Gender
+ span(class='form-section-input')
+ select#gender-dropdown(name="gender" class="form-main-dropdown" disabled)
+ option(value="both") Both
+ option(value="female") Female
+ option(value="male") Male
+ span(class='form-section')
+ label Division name
+ span(class='form-section-input')
+ input#name-textbox(type="text", name="name" disabled)
+ .error #{message}
+ span(class='form-section')
+ button#submit-button(type="submit" disabled) Submit
+ span(class='form-section')
+ button#delete-button(disabled) Delete
+
+block scripts
+ script(src='/scripts/manage/division.js' type="module")
\ No newline at end of file
diff --git a/views/manage/addgame.pug b/views/manage/addgame.pug
new file mode 100644
index 0000000..3a44834
--- /dev/null
+++ b/views/manage/addgame.pug
@@ -0,0 +1,55 @@
+extends ../layout
+
+block stylesheets
+ link(rel='stylesheet', href='/stylesheets/submit.css')
+ link(rel='stylesheet', href='/stylesheets/form.css')
+
+block content
+ form#submission-form(action='./game', method='POST')
+ span(class='form-section')
+ label Year
+ span(class='form-section-input')
+ select#year-dropdown(name="year" class="form-main-dropdown" disabled)
+ span(class='form-section flat-form-section')
+ span
+ label Sport
+ span(class='form-section-input')
+ select#sport-dropdown(name="sport" class="form-main-dropdown")
+ span
+ label Gender
+ span(class='form-section-input')
+ select#gender-dropdown(name="gender")
+ span
+ label Division
+ span(class='form-section-input')
+ select#division-dropdown(name="division")
+ span(class='form-section')
+ label Date of match
+ span(class='form-section-input')
+ input#date-input(type="date", name="date" value=date disabled)
+ span(class='form-section flat-form-section')
+ span
+ label Your team
+ span(class='form-section-input')
+ select#team1-dropdown(name="team1" class="form-main-dropdown" disabled)
+ span(class='form-score-input')
+ label Score
+ span(class='form-section-input')
+ input#team1-score-textbox(type="number", name="team1-score", value="0" disabled)
+ span(class='form-section flat-form-section')
+ span
+ label Opponent
+ span(class='form-section-input')
+ select#team2-dropdown(name="team2" class="form-main-dropdown" disabled)
+ span(class='form-score-input')
+ label Score
+ span(class='form-section-input')
+ input#team2-score-textbox(type="number", name="team2-score", value="0" disabled)
+ .error #{message}
+ span(class='form-section')
+ button#submit-button(type="submit" disabled) Submit
+ span(class='form-section')
+ button#delete-button(disabled) Delete
+
+block scripts
+ script(src='/scripts/manage/game.js' type="module")
\ No newline at end of file
diff --git a/views/manage/addseason.pug b/views/manage/addseason.pug
new file mode 100644
index 0000000..ba43698
--- /dev/null
+++ b/views/manage/addseason.pug
@@ -0,0 +1,18 @@
+extends ../layout
+
+block stylesheets
+ link(rel='stylesheet', href='/stylesheets/submit.css')
+ link(rel='stylesheet', href='/stylesheets/form.css')
+
+block content
+ form(action='./season', method='POST')
+ span(class='form-section')
+ label Ending year
+ span(class='form-section-input')
+ input#season-textbox(type="number", name="year", value=currentYear)
+ .error #{message}
+ span(class='form-section')
+ button#submit-button(type="submit" disabled) Submit
+
+block scripts
+ script(src='/scripts/manage/season.js' type="module")
\ No newline at end of file
diff --git a/views/manage/addsport.pug b/views/manage/addsport.pug
new file mode 100644
index 0000000..b4caa50
--- /dev/null
+++ b/views/manage/addsport.pug
@@ -0,0 +1,21 @@
+extends ../layout
+
+block stylesheets
+ link(rel='stylesheet', href='/stylesheets/submit.css')
+ link(rel='stylesheet', href='/stylesheets/form.css')
+
+block content
+ form#submission-form(action='./sport', method='POST')
+ span(class='form-section')
+ label Sport name
+ span(class='form-section-input')
+ input#name-textbox(type="text" name="name" disabled)
+ .error #{message}
+ span(class='form-section')
+ button#submit-button(type="submit" disabled) Submit
+ span(class='form-section')
+ button#delete-button(disabled) Delete
+
+
+block scripts
+ script(src='/scripts/manage/sport.js' type="module")
\ No newline at end of file
diff --git a/views/manage/addteam.pug b/views/manage/addteam.pug
new file mode 100644
index 0000000..78746a6
--- /dev/null
+++ b/views/manage/addteam.pug
@@ -0,0 +1,24 @@
+extends ../layout
+
+block stylesheets
+ link(rel='stylesheet', href='/stylesheets/submit.css')
+ link(rel='stylesheet', href='/stylesheets/form.css')
+
+block content
+ form#submission-form(action='./team', method='POST')
+ span(class='form-section')
+ label Sport
+ span(class='form-section-input')
+ select#sport-dropdown(name="sport" class="form-main-dropdown" disabled)
+ span(class='form-section')
+ label Team name
+ span(class='form-section-input')
+ input#name-textbox(type="text", name="name" disabled)
+ .error #{message}
+ span(class='form-section')
+ button#submit-button(type="submit" disabled) Submit
+ span(class='form-section')
+ button#delete-button(disabled) Delete
+
+block scripts
+ script(src='/scripts/manage/team.js' type="module")
\ No newline at end of file
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
diff --git a/views/submit.pug b/views/submit.pug
deleted file mode 100644
index e69de29..0000000