asura-tmdb/src/server/tmdb.js

556 lines
17 KiB
JavaScript

// TMDB - Tournament Manager DataBase
// Handles all the database operations for the Tournament Manager
// Exports the following functions:
module.exports = {
getMatchesByTournamentId: getMatchesByTournamentId,
getTournaments: getTournaments,
getTournament, getTournament,
getTeam: getTeam,
createTeam: createTeam,
editTeam: editTeam,
deleteTeam: deleteTeam,
getMatch: getMatch,
setMatchWinner: setMatchWinner,
unsetContestantAndWinner: unsetContestantAndWinner,
createTournament: createTournament,
deleteTournament: deleteTournament,
editTournament: editTournament,
getTeamsByTournamentId: getTeamsByTournamentId,
getUsers: getUsers,
getUserByEmail: getUserByEmail,
getUserByGoogleId: getUserByGoogleId,
createUserBlank: createUserBlank,
editUser: editUser,
}
const mysql = require("mysql");
// #region Database setup
let db_config = {
host: process.env.DB_HOST,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_DATABASE
};
let connection
// https://stackoverflow.com/a/20211143
function handleDisconnect() {
connection = mysql.createConnection(db_config); // Recreate the connection
connection.connect(function(err) {
if(err) {
console.log('error when connecting to db:', err);
setTimeout(handleDisconnect, 2000); // We introduce a delay before attempting to reconnect,
} // to avoid a hot loop, and to allow our node script to
}); // process asynchronous requests in the meantime.
// If you're also serving http, display a 503 error.
connection.on('error', function(err) {
console.log('db error', err);
if(err.code === 'PROTOCOL_CONNECTION_LOST') { // Connection to the MySQL server is usually
handleDisconnect(); // lost due to either server restart, or a
} else { // connnection idle timeout (the wait_timeout
throw err; // server variable configures this)
}
});
}
handleDisconnect(); //Start the auto-restarting connection
function escapeString(str) {
// return mysql.escape(str);
return str;
}
// #endregion
// #region match
// Returns the match of the exact given id.
function getMatch(matchId) {
return new Promise(function(resolve, reject) {
connection.query("SELECT * FROM matches WHERE id = ?", [escapeString(matchId)], (err, matches) => {
if (err) {
reject(err);
} else {
if (matches.length == 0) {
reject("No such match exists");
}
let match = matches[0];
resolve(match);
}
});
});
}
// Removes a given team from a given match. This is done by setting the teamId-property containing the given team to null.
async function unsetContestant(matchId, teamId) {
let match = await getMatch(matchId);
return new Promise(function(resolve, reject) {
if (match.team1Id == teamId) {
connection.query("UPDATE matches SET team1Id = NULL WHERE id = ?", [escapeString(matchId)], (err, result) => {
if (err) { console.log(err); reject(err); }
resolve();
});
} else if (match.team2Id == teamId) {
connection.query("UPDATE matches SET team2Id = NULL WHERE id = ?", [escapeString(matchId)], (err, result) => {
if (err) { console.log(err); reject(err); }
resolve();
});
} else {
console.log("Error: Team not found in match");
reject("Error: Team not found in match");
}
});
}
async function insertContestant(matchId, teamId, prevMatchId) {
let match = await getMatch(matchId);
connection.query("SELECT * FROM matches WHERE parentMatchId = ?", [escapeString(matchId)], (err, childMatches) => {
if (err) { console.log(err); }
let isFirst = prevMatchId == childMatches[0].id;
if (isFirst) {
if (match.team1Id != null) { return; }
connection.query("UPDATE matches SET team1Id = ? WHERE id = ?",
[escapeString(teamId), escapeString(matchId)], (err, sets) => {
if (err) { console.log(err); }
});
} else {
if (match.team2Id != null) { return; }
connection.query("UPDATE matches SET team2Id = ? WHERE id = ?",
[escapeString(teamId), escapeString(matchId)], (err, sets) => {
if (err) { console.log(err); }
});
}
});
}
async function setMatchWinner(matchId, winnerId) {
return new Promise(async function(resolve, reject) {
let match = await getMatch(matchId);
if (winnerId != match.team1Id && winnerId != match.team2Id && winnerId != null) {
reject("Winner id must be one of the teams in the match, or null");
return;
}
let oldWinnerId = match.winnerId;
connection.query("UPDATE matches SET winnerId = ? WHERE id = ?",[escapeString(winnerId), escapeString(matchId)], async (err, sets) => {
if (err) {
reject(err);
return;
}
// Remove the old winner from the next match
if (oldWinnerId != null && match.parentMatchId != null) {
let parentMatch = await getMatch(match.parentMatchId);
// Do not undo the match if the parent match is played and finished
if (parentMatch.winnerId != null) {
connection.query("UPDATE matches SET winnerId = ? WHERE id = ?", [escapeString(oldWinnerId), escapeString(match.parentMatchId)], (err, sets) => {});
reject("The next match is already played");
return;
}
await unsetContestant(match.parentMatchId, oldWinnerId);
}
if (match.parentMatchId != null && winnerId != null) {
insertContestant(match.parentMatchId, winnerId, matchId);
}
resolve(getMatch(matchId));
});
});
}
async function unsetContestantAndWinner(matchId, teamId) {
let match = await getMatch(matchId);
return new Promise(function(resolve, reject) {
// Find what the child match that supplied the team
connection.query("SELECT * FROM matches WHERE parentMatchId = ? AND winnerId = ?", [matchId, teamId], (err, childMatches) => {
if (err) { console.log(err); reject(err); }
if (childMatches.length != 1) {
reject("Error: Could not find the correct child match");
return;
}
let childMatch = childMatches[0];
// Remove the winner from the child match
setMatchWinner(childMatch.id, null)
.then(() => { resolve(); })
.catch(err => { reject(err); });
});
});
}
// #endregion
// #region tournament
function getTournaments() {
return new Promise(function(resolve, reject) {
// 1. Get the list of tournament IDs
// 2. getTournament() for each ID
// 3. Return list of tournaments
let tournamentList = [];
connection.query("SELECT id FROM tournaments", async (err, tournamentIds) => {
if (err) {
console.log(err);
reject(err);
} else {
let tournaments = await Promise.all(tournamentIds.map(async function(tournament){
return await getTournament(tournament.id);
}));
resolve(tournaments);
}
});
});
}
function getTournament(tournamentId) {
// 1. Get the tournament
// 2. Get all teams associated with the tournament
// 3. Associate the teams with the tournament
// 4. Return the tournament
return new Promise(function(resolve, reject) {
connection.query("SELECT * FROM tournaments WHERE id = ?", [escapeString(tournamentId)], (err, tournaments) => {
if (err) {
console.log(err);
reject(err);
} else {
if (tournaments.length == 0) {
reject("No such tournament exists");
return
}
getTeamsByTournamentId(tournamentId)
.catch(err => reject(err))
.then(teams => {
let tournament = tournaments[0];
tournament.teamCount = teams.length;
resolve(tournament);
});
}
});
});
}
function deleteTournament(tournamentId) {
return new Promise(function(resolve, reject) {
connection.query("DELETE FROM tournaments WHERE id = ?", [escapeString(tournamentId)], (err, result) => {
if (err) {
reject(err);
} else {
resolve();
}
});
});
}
function getMatchesByTournamentId(tournamentId) {
return new Promise(function(resolve, reject) {
connection.query("SELECT * FROM matches WHERE tournamentId = ?", [escapeString(tournamentId)], (err, matches) => {
if (err) {
console.log(err);
reject(err);
} else {
resolve(matches);
}
});
});
}
function createMatch(tournamentId, parentMatchId, tier) {
//Returns Promise<int> witht the inserted ID.
return new Promise(function(resolve, reject) {
connection.query("INSERT INTO matches (tournamentId, parentMatchId, tier) VALUES (?, ?, ?)",
[escapeString(tournamentId), escapeString(parentMatchId), escapeString(tier)], (err, result) => {
if (err) {
console.log(err);
reject(err);
} else {
resolve(result.insertId);
}
});
});
}
function createTournament(name, description, prize, startDate, endDate, teamLimit) {
startDate = startDate.toISOString().slice(0, 19).replace('T', ' ');
endDate = endDate.toISOString().slice(0, 19).replace('T', ' ');
return new Promise(function(resolve, reject) {
connection.query("INSERT INTO tournaments (name, description, prize, startTime, endTime, teamLimit) VALUES (?, ?, ?, ?, ?, ?)",
[escapeString(name), escapeString(description), escapeString(prize), startDate, endDate, teamLimit], async (err, sets) => {
if (err) {
console.log(err);
reject(err);
} else {
// Create the matches for the tournament
let matchIds = [];
let tournamentId = sets.insertId;
let tiers = Math.log2(teamLimit);
for (let tier = 0; tier < tiers; tier++) {
let matchCount = Math.pow(2, tier);
for (let i = 0; i < matchCount; i++) {
let parentMatchId = null;
if (tier > 0) {
let parentMatchIndex = Math.pow(2, tier - 1) + Math.floor((i - (i % 2)) / 2) - 1;
parentMatchId = matchIds[parentMatchIndex];
}
let newMatchId = await createMatch(tournamentId, parentMatchId, tier);
matchIds.push(newMatchId);
}
}
resolve({message: "Tournament created", tournamentId: sets.insertId});
}
});
});
}
function editTournament(tournamentId, name, description, prize, startDate, endDate) {
startDate = startDate.toISOString().slice(0, 19).replace('T', ' ');
endDate = endDate.toISOString().slice(0, 19).replace('T', ' ');
return new Promise(function(resolve, reject) {
connection.query("UPDATE tournaments SET name = ?, description = ?, prize = ?, startTime = ?, endTime = ? WHERE id = ?",
[escapeString(name), escapeString(description), escapeString(prize), startDate, endDate, escapeString(tournamentId)], (err, sets) => {
if (err) {
console.log(err);
reject(err);
} else {
resolve("Tournament updated");
}
});
});
}
function getTeamsByTournamentId(tournamentId) {
return new Promise(function(resolve, reject) {
connection.query("SELECT * FROM teams WHERE tournamentId = ?", [escapeString(tournamentId)], (err, teams) => {
if (err) {
console.log(err);
reject(err);
} else {
resolve(teams);
}
});
});
}
// #endregion
// #region team
function getTeam(teamId) {
return new Promise(function(resolve, reject) {
connection.query("SELECT * FROM teams WHERE id = ?", [escapeString(teamId)], (err, teams) => {
if (err) {
console.log(err);
reject(err);
} else {
if (teams.length == 0) {
reject("No such team exists");
}
resolve(teams[0]);
}
});
});
}
async function editTeam(teamId, name) {
let team = await getTeam(teamId);
if (!team) {
return Promise.reject("No such team exists");
}
if (team.name == name) {
return {message: "Team name unchanged"};
}
return new Promise(function(resolve, reject) {
connection.query("UPDATE teams SET name = ? WHERE id = ?", [escapeString(name), escapeString(teamId)], (err, sets) => {
if (err) {
console.log(err);
reject(err);
} else {
resolve("Team updated");
}
});
});
}
async function createTeam(tournamentId, name) {
//Check that the tournament exists
let tournament = await getTournament(tournamentId);
return new Promise(function(resolve, reject) {
if (!tournament) {
reject("No such tournament exists");
return;
}
if (tournament.teamLimit <= tournament.teamCount) {
reject("Tournament is full");
return;
}
connection.query("INSERT INTO teams (tournamentId, name) VALUES (?, ?)", [escapeString(tournamentId), escapeString(name)], async (err, sets) => {
if (err) {
console.log(err);
reject(err);
} else {
await assignFirstMatch(sets.insertId, tournamentId);
resolve({message: "Team created", teamId: sets.insertId});
}
});
});
}
function deleteTeam(teamId) {
return new Promise(async function(resolve, reject) {
connection.query("DELETE FROM teams WHERE id = ?", [escapeString(teamId)], (err, sets) => {
if (err) {
console.log(err);
reject(err);
} else {
resolve("Team deleted");
}
});
});
}
//Private function, assigns a starting match to the given team
async function assignFirstMatch(teamId, tournamentId) {
let tournament = await getTournament(tournamentId);
let matches = await getMatchesByTournamentId(tournamentId);
let highTier = Math.log2(tournament.teamLimit)-1;
let highTierMatches = matches.filter(match => match.tier == highTier);
return new Promise(function(resolve, reject) {
for (let match of highTierMatches) {
if (match.team1Id == null) {
connection.query("UPDATE matches SET team1Id = ? WHERE id = ?", [escapeString(teamId), escapeString(match.id)], (err, sets) => {
if (err) {
console.log(err);
reject(err);
} else {
resolve("Team assigned to match " + match.id);
}
});
return
} else if (match.team2Id == null) {
connection.query("UPDATE matches SET team2Id = ? WHERE id = ?", [escapeString(teamId), escapeString(match.id)], (err, sets) => {
if (err) {
console.log(err);
reject(err);
} else {
resolve("Team assigned to match " + match.id);
}
});
return
}
}
reject("Could not assign team to any matches");
});
}
// #endregion
// #region users
function getUsers () {
return new Promise(function(resolve, reject) {
connection.query("SELECT * FROM users", (err, userRows) => {
if (err) {
console.log(err);
reject(err);
} else {
let users = [];
userRows.forEach((userRow, index) => {
let user = results=JSON.parse(JSON.stringify(userRow))
user.isManager = user.isManager == 1;
users.push(user);
});
resolve(users);
}
});
});
}
function getUserByGoogleId(googleId) {
return new Promise(function(resolve, reject) {
connection.query("SELECT * FROM users WHERE googleId = ?", [escapeString(googleId)], (err, users) => {
if (err) {
console.log(err);
reject(err);
} else {
if (users.length == 0) {
reject("No such user exists");
}
users[0].isManager = users[0].isManager == 1;
resolve(users[0]);
}
});
});
}
function getUserByEmail(email) {
return new Promise(function(resolve, reject) {
connection.query("SELECT * FROM users WHERE email = ?", [escapeString(email)], (err, users) => {
if (err) {
console.log(err);
reject(err);
} else {
if (users.length == 0) {
reject("No such user exists");
return;
}
users[0].isManager = users[0].isManager == 1;
resolve(users[0]);
}
});
});
}
function createUserBlank(email) {
return new Promise(function(resolve, reject) {
//Check that the user doesn't already exist
getUserByEmail(email).then(user => {
reject("No such user exists");
}).catch(err => {
if (err != "No such user exists") {
console.log(err);
reject(err);
return;
}
// Create a user, with only an email address
connection.query("INSERT INTO users (email, isManager) VALUES (?, FALSE)", [escapeString(email)], (err, sets) => {
if (err) {
console.log(err);
reject(err);
} else {
resolve({message: "User Created", userId: sets.insertId});
}
});
});
});
}
function editUser(email, user) {
return new Promise(function(resolve, reject) {
connection.query("UPDATE users SET googleId = ?, name = ?, isManager = ? WHERE email = ?", [escapeString(user.googleId), escapeString(user.name), escapeString(user.isManager), escapeString(email)], (err, sets) => {
if (err) {
console.log(err);
reject(err);
} else {
console.log(sets);
resolve("User updated");
}
});
});
}
function userIsManager(userId) {
getUser(userId)
.then(user => { return user.isManager; })
.catch(err => { console.log(err); return false; });
}
// #endregion