Merge branch 'server' into 'main'
Merge entire server branch into main See merge request felixalb/dcst1008-2022-group1!4
This commit is contained in:
commit
29f463f248
|
@ -0,0 +1,14 @@
|
||||||
|
## Server installation
|
||||||
|
* Clone the repository
|
||||||
|
** Checkout the "server" branch if not merged
|
||||||
|
* Enter the server directory: `cd src/server`
|
||||||
|
* Install the node dependencies: `npm install`
|
||||||
|
* Create the file `.env` containing your database login:
|
||||||
|
```
|
||||||
|
DB_HOST=mysql.stud.ntnu.no
|
||||||
|
DB_USER=dbusername
|
||||||
|
DB_PASSWORD=dbpassword
|
||||||
|
DB_DATABASE=dbname
|
||||||
|
```
|
||||||
|
* Build the client (separate instructions)
|
||||||
|
* Start the server `npm start`
|
|
@ -0,0 +1 @@
|
||||||
|
../../../sysut_client/src/client/build
|
|
@ -0,0 +1,592 @@
|
||||||
|
const path = require("path");
|
||||||
|
const express = require("express");
|
||||||
|
const session = require('express-session');
|
||||||
|
const https = require("https");
|
||||||
|
require("dotenv").config();
|
||||||
|
|
||||||
|
// Our self-written module for handling database operations
|
||||||
|
let tmdb = require("./tmdb.js");
|
||||||
|
|
||||||
|
// #region Express setup
|
||||||
|
const app = express();
|
||||||
|
const port = process.env.SERVER_PORT || 3000;
|
||||||
|
app.listen(parseInt(port), () => {
|
||||||
|
console.log(`Listening on port ${port}`)
|
||||||
|
})
|
||||||
|
app.use(express.json());
|
||||||
|
app.use(express.urlencoded({ extended: true }));
|
||||||
|
app.use(session({
|
||||||
|
resave: true,
|
||||||
|
saveUninitialized: false,
|
||||||
|
secret: process.env.COOKIE_SECRET,
|
||||||
|
rolling: true,
|
||||||
|
cookie: {
|
||||||
|
secure: (process.env.COOKIE_SECURE == "true"), // All env vars are strings, so cast bool manually
|
||||||
|
sameSite: 'strict', // Browsers will reject a "secure" cookie without this
|
||||||
|
maxAge: 60 * 60 * 1000 // 1 hour (in milliseconds)
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
let api = express.Router();
|
||||||
|
app.use("/api", api);
|
||||||
|
|
||||||
|
api.use(function(req, res, next) {
|
||||||
|
res.header("Access-Control-Allow-Origin", "*");
|
||||||
|
res.header("Access-Control-Allow-Methods", "GET, POST, DELETE");
|
||||||
|
// res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
api.use(require('express-log-url'));
|
||||||
|
app.use(require('express-log-url'));
|
||||||
|
|
||||||
|
// #endregion
|
||||||
|
|
||||||
|
// #region frontend
|
||||||
|
|
||||||
|
// Serve static files from the React app
|
||||||
|
app.use('/', express.static(path.join(__dirname, 'clientbuild')));
|
||||||
|
app.use('/login', express.static(path.join(__dirname, 'clientbuild', 'index.html')));
|
||||||
|
app.use('/history', express.static(path.join(__dirname, 'clientbuild', 'index.html')));
|
||||||
|
app.use('/admins', express.static(path.join(__dirname, 'clientbuild', 'index.html')));
|
||||||
|
app.use('/profile', express.static(path.join(__dirname, 'clientbuild', 'index.html')));
|
||||||
|
app.use('/tournament/*', express.static(path.join(__dirname, 'clientbuild', 'index.html')));
|
||||||
|
app.use('/static', express.static(path.join(__dirname, 'clientbuild/static')));
|
||||||
|
app.use('/static/*', express.static(path.join(__dirname, 'clientbuild/static')));
|
||||||
|
// #endregion
|
||||||
|
|
||||||
|
// #region PASSPORT / OAUTH
|
||||||
|
|
||||||
|
const passport = require('passport');
|
||||||
|
var userProfile;
|
||||||
|
|
||||||
|
app.use(passport.initialize());
|
||||||
|
app.use(passport.session());
|
||||||
|
|
||||||
|
passport.serializeUser(function(user, cb) {
|
||||||
|
cb(null, user);
|
||||||
|
});
|
||||||
|
|
||||||
|
passport.deserializeUser(function(obj, cb) {
|
||||||
|
cb(null, obj);
|
||||||
|
});
|
||||||
|
|
||||||
|
const GoogleStrategy = require('passport-google-oauth').OAuth2Strategy;
|
||||||
|
passport.use(new GoogleStrategy({
|
||||||
|
clientID: process.env.GOOGLE_CLIENT_ID,
|
||||||
|
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
|
||||||
|
callbackURL: process.env.GOOGLE_CALLBACK_URL
|
||||||
|
},
|
||||||
|
function(accessToken, refreshToken, profile, done) {
|
||||||
|
userProfile=profile;
|
||||||
|
return done(null, userProfile);
|
||||||
|
}
|
||||||
|
));
|
||||||
|
|
||||||
|
app.get('/auth/google',
|
||||||
|
passport.authenticate('google', { scope : ['profile', 'email']})
|
||||||
|
);
|
||||||
|
|
||||||
|
app.get('/auth/google/callback',
|
||||||
|
passport.authenticate('google', { failureRedirect: '/error' }),
|
||||||
|
async function(req, res) {
|
||||||
|
// Get user profile from passport
|
||||||
|
// This is retrieved from the callback url data ?code=...
|
||||||
|
let user = {
|
||||||
|
googleId: req.user.id,
|
||||||
|
name: req.user.displayName,
|
||||||
|
email: req.user.emails[0].value,
|
||||||
|
imgurl: req.user.photos[0].value,
|
||||||
|
asuraId: null,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if user exists in database
|
||||||
|
tmdb.getUserByEmail(user.email)
|
||||||
|
.then(dbUser => {
|
||||||
|
user.asuraId = dbUser.id; // asuraId is the database id / primary key
|
||||||
|
if (dbUser.googleId) {
|
||||||
|
// User is already registered with google, simply log them in
|
||||||
|
req.session.user = dbUser;
|
||||||
|
} else {
|
||||||
|
// User is "preregistered" with email only, so complete the registration
|
||||||
|
// This step will register the name, img and googleId
|
||||||
|
tmdb.editUser(user.email, user).catch(err => console.log(err));
|
||||||
|
|
||||||
|
req.session.user = user;
|
||||||
|
}
|
||||||
|
|
||||||
|
res.redirect(process.env.AUTH_SUCCESS_REDIRECT);
|
||||||
|
return;
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
// User is not in the database at all, do not give them a session.
|
||||||
|
res.json({"status": "error", message: "Email is not in administrator list."});
|
||||||
|
return;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// #endregion
|
||||||
|
|
||||||
|
|
||||||
|
// #region API
|
||||||
|
api.get("/tournament/getTournaments", (req, res) => {
|
||||||
|
tmdb.getTournaments()
|
||||||
|
.then(tournaments => res.json({"status": "OK", "data": tournaments}))
|
||||||
|
.catch(err => res.json({"status": "error", "data": err}));
|
||||||
|
});
|
||||||
|
|
||||||
|
// #region tournament/:tournamentId
|
||||||
|
api.get("/tournament/:tournamentId", (req, res) => {
|
||||||
|
let tournamentId = req.params.tournamentId;
|
||||||
|
if (isNaN(tournamentId)) {
|
||||||
|
res.json({"status": "error", "data": "Invalid tournament id"});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
tmdb.getTournament(parseInt(tournamentId))
|
||||||
|
.then(tournament => res.json({"status": "OK", "data": tournament}))
|
||||||
|
.catch(err => res.json({"status": "error", "data": err}));
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
api.get("/tournament/:tournamentId/getMatches", (req, res) => {
|
||||||
|
let tournamentId = req.params.tournamentId;
|
||||||
|
if (isNaN(tournamentId)) {
|
||||||
|
res.json({"status": "error", "data": "tournamentId must be a number"});
|
||||||
|
return
|
||||||
|
}
|
||||||
|
tournamentId = parseInt(tournamentId);
|
||||||
|
tmdb.getMatchesByTournamentId(tournamentId)
|
||||||
|
.then(matches => res.send({"status": "OK", "data": matches}))
|
||||||
|
.catch(err => res.send({"status": "error", "data": err}));
|
||||||
|
});
|
||||||
|
|
||||||
|
api.get("/tournament/:tournamentId/getTeams", (req, res) => {
|
||||||
|
let tournamentId = req.params.tournamentId;
|
||||||
|
if (!tournamentId || isNaN(tournamentId)) {
|
||||||
|
res.json({"status": "error", "data": "tournamentId must be a number"});
|
||||||
|
return
|
||||||
|
}
|
||||||
|
tournamentId = parseInt(tournamentId);
|
||||||
|
tmdb.getTeamsByTournamentId(tournamentId)
|
||||||
|
.then(teams => res.send({"status": "OK", "data": teams}))
|
||||||
|
.catch(err => res.send({"status": "error", "data": err}));
|
||||||
|
});
|
||||||
|
|
||||||
|
api.post("/tournament/:tournamentId/edit", async (req, res) => {
|
||||||
|
if (!(await isSessionLoggedIn(req.session))) {
|
||||||
|
res.json({"status": "error", "data": "User is not logged in"});
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let tournamentId = req.params.tournamentId;
|
||||||
|
if (isNaN(tournamentId)) {
|
||||||
|
res.json({"status": "error", "data": "tournamentId must be a number"});
|
||||||
|
return
|
||||||
|
}
|
||||||
|
tournamentId = parseInt(tournamentId);
|
||||||
|
let name = req.body.name;
|
||||||
|
let description = req.body.description;
|
||||||
|
let prize = req.body.prize;
|
||||||
|
let startDate = req.body.startDate;
|
||||||
|
let endDate = req.body.endDate;
|
||||||
|
console.log(startDate);
|
||||||
|
if (name == undefined || name == "" || description == undefined || description == "") {
|
||||||
|
res.json({"status": "error", "data": "name and description must be provided"});
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (startDate == undefined || endDate == undefined) {
|
||||||
|
res.json({"status": "error", "data": "startDate and endDate must be defined"});
|
||||||
|
return
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
startDate = new Date(parseInt(startDate));
|
||||||
|
endDate = new Date(parseInt(endDate));
|
||||||
|
} catch (err) {
|
||||||
|
res.json({"status": "error", "data": "startDate and endDate must be valid dates"});
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// let today = new Date();
|
||||||
|
// if (startDate < today) {
|
||||||
|
// res.json({"status": "error", "data": "startDate cannot be in the past"});
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
if (startDate > endDate) {
|
||||||
|
res.json({"status": "error", "data": "startDate cannot be after endDate"});
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
tmdb.editTournament(tournamentId, name, description, prize, startDate, endDate)
|
||||||
|
.then(msg => res.json({"status": "OK", "data": msg}))
|
||||||
|
.catch(err => res.json({"status": "error", "data": err}));
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
api.post("/tournament/:tournamentId/createTeam", async (req, res) => {
|
||||||
|
if (!(await isSessionLoggedIn(req.session))) {
|
||||||
|
res.json({"status": "error", "data": "User is not logged in"});
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let tournamentId = req.params.tournamentId;
|
||||||
|
if (isNaN(tournamentId)) {
|
||||||
|
res.json({"status": "error", "data": "tournamentId must be a number"});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
tournamentId = parseInt(tournamentId);
|
||||||
|
let teamName = req.body.name;
|
||||||
|
if (teamName == undefined || teamName == "") {
|
||||||
|
res.json({"status": "error", "data": "teamName must be a non-empty string"});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
tmdb.createTeam(tournamentId, teamName)
|
||||||
|
.then(msg => res.json({"status": "OK", "data": msg}))
|
||||||
|
.catch(err => res.json({"status": "error", "data": err}));
|
||||||
|
});
|
||||||
|
|
||||||
|
api.delete("/tournament/:tournamentId", async (req, res) => {
|
||||||
|
if (!(await isSessionLoggedIn(req.session))) {
|
||||||
|
res.json({"status": "error", "data": "User is not logged in"});
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let tournamentId = req.params.tournamentId;
|
||||||
|
if (isNaN(tournamentId)) {
|
||||||
|
res.json({"status": "error", "data": "tournamentId must be a number"});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
tournamentId = parseInt(tournamentId);
|
||||||
|
tmdb.deleteTournament(tournamentId)
|
||||||
|
.then(msg => res.json({"status": "OK", "data": msg}))
|
||||||
|
.catch(err => res.json({"status": "error", "data": err}));
|
||||||
|
});
|
||||||
|
|
||||||
|
// #endregion
|
||||||
|
|
||||||
|
// #region match/:matchId
|
||||||
|
|
||||||
|
|
||||||
|
api.get("/match/:matchId", (req, res) => {
|
||||||
|
let matchId = req.params.matchId;
|
||||||
|
if (isNaN(matchId)) {
|
||||||
|
res.json({"status": "error", "data": "matchId must be a number"});
|
||||||
|
return
|
||||||
|
}
|
||||||
|
matchId = parseInt(matchId);
|
||||||
|
tmdb.getMatch(matchId)
|
||||||
|
.then(match => res.send({"status": "OK", "data": match}))
|
||||||
|
.catch(err => res.send({"status": "error", "data": err}));
|
||||||
|
});
|
||||||
|
|
||||||
|
api.post("/match/:matchId/setWinner", async (req, res) => {
|
||||||
|
if (!(await isSessionLoggedIn(req.session))) {
|
||||||
|
res.json({"status": "error", "data": "User is not logged in"});
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let matchId = req.params.matchId;
|
||||||
|
let winnerId = req.body.winnerId;
|
||||||
|
if (isNaN(matchId)) {
|
||||||
|
res.json({"status": "error", "data": "matchId must be a number"});
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (winnerId == undefined || (isNaN(winnerId) && winnerId != "null")) {
|
||||||
|
res.json({"status": "error", "data": "winnerId must be a number"});
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
matchId = parseInt(matchId);
|
||||||
|
if (winnerId == "null") {
|
||||||
|
winnerId = null;
|
||||||
|
} else {
|
||||||
|
winnerId = parseInt(winnerId);
|
||||||
|
}
|
||||||
|
tmdb.setMatchWinner(matchId, winnerId)
|
||||||
|
.then(match => res.send({"status": "OK", "data": match}))
|
||||||
|
.catch(err => res.send({"status": "error", "data": err}));
|
||||||
|
});
|
||||||
|
|
||||||
|
api.post("/match/:matchId/unsetContestant", async (req, res) => {
|
||||||
|
if (!(await isSessionLoggedIn(req.session))) {
|
||||||
|
res.json({"status": "error", "data": "User is not logged in"});
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let matchId = req.params.matchId;
|
||||||
|
let contestantId = req.body.teamId;
|
||||||
|
if (isNaN(matchId)) {
|
||||||
|
res.json({"status": "error", "data": "matchId must be a number"});
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (contestantId == undefined || isNaN(contestantId)) {
|
||||||
|
res.json({"status": "error", "data": "contestantId must be a number"});
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
matchId = parseInt(matchId);
|
||||||
|
contestantId = parseInt(contestantId);
|
||||||
|
tmdb.unsetContestantAndWinner(matchId, contestantId)
|
||||||
|
.then(match => res.send({"status": "OK", "data": match}))
|
||||||
|
.catch(err => res.send({"status": "error", "data": err}));
|
||||||
|
});
|
||||||
|
// #endregion
|
||||||
|
|
||||||
|
// #region team/:teamId
|
||||||
|
api.get("/team/:teamId", (req, res) => {
|
||||||
|
let teamId = req.params.teamId;
|
||||||
|
if (isNaN(teamId)) {
|
||||||
|
res.json({"status": "error", "data": "teamId must be a number"});
|
||||||
|
return
|
||||||
|
}
|
||||||
|
teamId = parseInt(teamId);
|
||||||
|
tmdb.getTeam(teamId)
|
||||||
|
.then(match => res.send({"status": "OK", "data": match}))
|
||||||
|
.catch(err => res.send({"status": "error", "data": err}));
|
||||||
|
});
|
||||||
|
|
||||||
|
api.delete("/team/:teamId", async (req, res) => {
|
||||||
|
if (!(await isSessionLoggedIn(req.session))) {
|
||||||
|
res.json({"status": "error", "data": "User is not logged in"});
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let teamId = req.params.teamId;
|
||||||
|
if (isNaN(teamId)) {
|
||||||
|
res.json({"status": "error", "data": "teamId must be a number"});
|
||||||
|
return
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
teamId = parseInt(teamId);
|
||||||
|
} catch (err) {
|
||||||
|
res.json({"status": "error", "data": "teamId must be a number"});
|
||||||
|
return
|
||||||
|
}
|
||||||
|
tmdb.deleteTeam(teamId)
|
||||||
|
.then(match => res.send({"status": "OK", "data": match}))
|
||||||
|
.catch(err => res.send({"status": "error", "data": err}));
|
||||||
|
});
|
||||||
|
|
||||||
|
api.post("/team/:teamId/edit", async (req, res) => {
|
||||||
|
if (!(await isSessionLoggedIn(req.session))) {
|
||||||
|
res.json({"status": "error", "data": "User is not logged in"});
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let teamId = req.params.teamId;
|
||||||
|
let teamName = req.body.name;
|
||||||
|
console.log(req.body);
|
||||||
|
if (isNaN(teamId)) {
|
||||||
|
res.json({"status": "error", "data": "teamId must be a number"});
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (teamName == undefined || teamName == "") {
|
||||||
|
res.json({"status": "error", "data": "teamName must be a non-empty string"});
|
||||||
|
return
|
||||||
|
}
|
||||||
|
teamId = parseInt(teamId);
|
||||||
|
tmdb.editTeam(teamId, teamName)
|
||||||
|
.then(match => res.send({"status": "OK", "data": match}))
|
||||||
|
.catch(err => res.send({"status": "error", "data": err}));
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// #endregion
|
||||||
|
|
||||||
|
//Takes JSON body
|
||||||
|
api.post("/tournament/create", async (req, res) => {
|
||||||
|
if (!(await isSessionLoggedIn(req.session))) {
|
||||||
|
res.json({"status": "error", "data": "User is not logged in"});
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//Check that req body is valid
|
||||||
|
if (req.body.name == undefined || req.body.name == "") {
|
||||||
|
res.json({"status": "error", "data": "No data supplied"});
|
||||||
|
return
|
||||||
|
}
|
||||||
|
//Check that req is json
|
||||||
|
// if (req.get("Content-Type") != "application/json") {
|
||||||
|
console.log(req.get("Content-Type"));
|
||||||
|
let name = req.body.name;
|
||||||
|
let description = req.body.description;
|
||||||
|
let prize = req.body.prize;
|
||||||
|
let teamLimit = req.body.teamLimit;
|
||||||
|
let startDate = req.body.startDate; //TODO: timezones, 2 hr skips
|
||||||
|
let endDate = req.body.endDate;
|
||||||
|
console.log(startDate, endDate);
|
||||||
|
if (name == undefined || name == "" || description == undefined || description == "") {
|
||||||
|
res.json({"status": "error", "data": "name and description must be provided"});
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (teamLimit == undefined ) {
|
||||||
|
res.json({"status": "error", "data": "teamLimit must be provided"});
|
||||||
|
return
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
teamLimit = parseInt(teamLimit);
|
||||||
|
} catch (err) {
|
||||||
|
res.json({"status": "error", "data": "teamLimit must be a number"});
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (startDate == undefined || endDate == undefined) {
|
||||||
|
res.json({"status": "error", "data": "startDate and endDate must be defined"});
|
||||||
|
return
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
startDate = new Date(parseInt(startDate));
|
||||||
|
endDate = new Date(parseInt(endDate));
|
||||||
|
} catch (err) {
|
||||||
|
res.json({"status": "error", "data": "startDate and endDate must be valid dates"});
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let today = new Date();
|
||||||
|
if (startDate < today) {
|
||||||
|
res.json({"status": "error", "data": "startDate cannot be in the past"});
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (startDate > endDate) {
|
||||||
|
res.json({"status": "error", "data": "endDate must be later than startDate"});
|
||||||
|
return
|
||||||
|
}
|
||||||
|
console.log(startDate);
|
||||||
|
|
||||||
|
tmdb.createTournament(name, description, prize, startDate, endDate, teamLimit)
|
||||||
|
.then(msg => res.json({"status": "OK", "data": msg}))
|
||||||
|
.catch(err => res.json({"status": "error", "data": err}));
|
||||||
|
});
|
||||||
|
|
||||||
|
// #endregion
|
||||||
|
|
||||||
|
// #region users
|
||||||
|
|
||||||
|
function isSessionLoggedIn(session) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
if (process.env.DEBUG_ALLOW_ALL === "true") { resolve(true); return; }
|
||||||
|
|
||||||
|
if (session.user == undefined || session.user.googleId == undefined) {
|
||||||
|
return resolve(false);
|
||||||
|
}
|
||||||
|
let googleId = session.user.googleId;
|
||||||
|
|
||||||
|
tmdb.getUserByGoogleId(googleId)
|
||||||
|
.then(user => {return resolve(user != undefined) })
|
||||||
|
.catch(err => {resolve(false) });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function isSessionManager(session) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
if (process.env.DEBUG_ALLOW_ALL === "true") { resolve(true); return; }
|
||||||
|
|
||||||
|
if (session.user == undefined || session.user.googleId == undefined) {
|
||||||
|
return resolve(false);
|
||||||
|
}
|
||||||
|
let googleId = session.user.googleId;
|
||||||
|
tmdb.getUserByGoogleId(googleId)
|
||||||
|
.then(user => {return resolve(user.isManager) })
|
||||||
|
.catch(err => {resolve(false) });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
api.get("/users/getSavedUser", (req, res) => {
|
||||||
|
if (!req.session.user) {
|
||||||
|
res.json({"status": "error", "data": "No user logged in"});
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let googleId = req.session.user.googleId;
|
||||||
|
tmdb.getUserByGoogleId(googleId)
|
||||||
|
.then(user => res.json({"status": "OK", "data": user}))
|
||||||
|
.catch(err => res.json({"status": "error", "data": err}));
|
||||||
|
});
|
||||||
|
|
||||||
|
api.get("/users/getUsers", async (req, res) => {
|
||||||
|
if (!(await isSessionManager(req.session))) {
|
||||||
|
res.json({"status": "error", "data": "Not authorized"});
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
tmdb.getUsers()
|
||||||
|
.then(users => res.json({"status": "OK", "data": users}))
|
||||||
|
.catch(err => res.json({"status": "error", "data": err}));
|
||||||
|
});
|
||||||
|
|
||||||
|
api.post("/users/createBlank", async (req, res) => {
|
||||||
|
if (!(await isSessionManager(req.session))) {
|
||||||
|
res.json({"status": "error", "data": "Not authorized"});
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let email = req.body.email;
|
||||||
|
// Check if the user already exists
|
||||||
|
tmdb.getUserByEmail(email)
|
||||||
|
.then(user => {
|
||||||
|
res.json({"status": "error", "data": "User already exists", user: user});
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
console.log(err);
|
||||||
|
if (err == "No such user exists") {
|
||||||
|
// Create a new user
|
||||||
|
tmdb.createUserBlank(email)
|
||||||
|
.then(user => {
|
||||||
|
res.json({"status": "OK", "data": user});
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
res.json({"status": "error", "data": err});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
res.json({"status": "error", "data": err});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
api.post("/users/:asuraId/changeManagerStatus", async (req, res) => {
|
||||||
|
if (!(await isSessionManager(req.session))) {
|
||||||
|
res.json({"status": "error", "data": "Not authorized"});
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let asuraId = req.params.asuraId;
|
||||||
|
let isManager = req.body.isManager;
|
||||||
|
tmdb.changeManagerStatus(asuraId, isManager)
|
||||||
|
.then(msg => res.json({"status": "OK", "data": msg}))
|
||||||
|
.catch(err => res.json({"status": "error", "data": err}));
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
api.delete("/users/:asuraId", async (req, res) => {
|
||||||
|
if (!(await isSessionManager(req.session))) {
|
||||||
|
res.json({"status": "error", "data": "Not authorized"});
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let asuraId = req.params.asuraId;
|
||||||
|
|
||||||
|
tmdb.deleteUser(asuraId)
|
||||||
|
.then(msg => res.json({"status": "OK", "data": msg}))
|
||||||
|
.catch(err => res.json({"status": "error", "data": err}));
|
||||||
|
});
|
||||||
|
|
||||||
|
api.get("/users/logout", (req, res) => {
|
||||||
|
req.session.destroy();
|
||||||
|
res.redirect(process.env.AUTH_SUCCESS_REDIRECT);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// Debugging functions, disabled on purpouse
|
||||||
|
// api.get("/users/getSessionUser", (req, res) => {
|
||||||
|
// if (req.session.user) {
|
||||||
|
// res.json({"status": "OK", "data": req.session.user});
|
||||||
|
// } else {
|
||||||
|
// res.json({"status": "error", "data": "No user logged in"});
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
|
||||||
|
// api.get("/dumpsession", async (req, res) => {
|
||||||
|
// let out = {};
|
||||||
|
// out.session = req.session;
|
||||||
|
// out.header = req.headers;
|
||||||
|
// out.isLoggedIn = await isSessionLoggedIn(req.session);
|
||||||
|
// out.isManager = await isSessionManager(req.session);
|
||||||
|
// console.log(out);
|
||||||
|
// res.json(out);
|
||||||
|
// });
|
||||||
|
// #endregion
|
|
@ -0,0 +1,105 @@
|
||||||
|
-- WARNING: Will delete EVERYTHING in the database!
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS matches;
|
||||||
|
DROP TABLE IF EXISTS teams;
|
||||||
|
DROP TABLE IF EXISTS tournaments;
|
||||||
|
DROP TABLE IF EXISTS users;
|
||||||
|
|
||||||
|
-- Create the tables
|
||||||
|
CREATE TABLE tournaments (
|
||||||
|
id INTEGER PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
description TEXT,
|
||||||
|
prize TEXT,
|
||||||
|
teamLimit INTEGER NOT NULL,
|
||||||
|
startTime DATETIME NOT NULL,
|
||||||
|
endTime DATETIME NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE teams (
|
||||||
|
id INTEGER PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
tournamentId INTEGER NOT NULL,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
|
||||||
|
FOREIGN KEY (tournamentId) REFERENCES tournaments (id) ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE matches (
|
||||||
|
id INTEGER PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
tournamentId INTEGER NOT NULL,
|
||||||
|
parentMatchId INTEGER,
|
||||||
|
team1Id INTEGER,
|
||||||
|
team2Id INTEGER,
|
||||||
|
winnerId INTEGER,
|
||||||
|
tier INTEGER,
|
||||||
|
|
||||||
|
FOREIGN KEY (tournamentId) REFERENCES tournaments (id) ON DELETE CASCADE,
|
||||||
|
FOREIGN KEY (team1Id) REFERENCES teams (id) ON DELETE SET NULL,
|
||||||
|
FOREIGN KEY (team2Id) REFERENCES teams (id) ON DELETE SET NULL,
|
||||||
|
FOREIGN KEY (winnerId) REFERENCES teams (id) ON DELETE SET NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE users (
|
||||||
|
id INTEGER PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
googleId TEXT,
|
||||||
|
name TEXT,
|
||||||
|
email TEXT NOT NULL,
|
||||||
|
isManager BOOLEAN NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Example data (Two tournaments, 4 teams, single elimination)
|
||||||
|
INSERT INTO tournaments (name, description, prize, startTime, endTime, teamLimit) VALUES ('Tournament 1', 'First tournament, single elimination', '300 000 points', '2022-04-29 16:00:00', '2022-04-29 20:00:00', 4);
|
||||||
|
INSERT INTO tournaments (name, description, prize, startTime, endTime, teamLimit) VALUES ('Tournament 2', 'Second tournament, four teams', '450 000 points', '2022-04-29 09:00:00', '2022-04-29 10:30:00', 8);
|
||||||
|
INSERT INTO tournaments (name, description, prize, startTime, endTime, teamLimit) VALUES ('Tournament 3', 'Previous tournament, it is done', '200 000 points', '2022-04-24 12:00:00', '2022-04-25 12:00:00', 4);
|
||||||
|
|
||||||
|
INSERT INTO teams (tournamentId, name) VALUES (1, 'Fnatic'); -- 1
|
||||||
|
INSERT INTO teams (tournamentId, name) VALUES (1, 'Cloud 9'); -- 2
|
||||||
|
INSERT INTO teams (tournamentId, name) VALUES (1, 'Team Liquid'); -- 3
|
||||||
|
INSERT INTO teams (tournamentId, name) VALUES (1, 'LDLC'); -- 4
|
||||||
|
|
||||||
|
INSERT INTO teams (tournamentId, name) VALUES (2, 'Astralis'); -- 5
|
||||||
|
INSERT INTO teams (tournamentId, name) VALUES (2, 'Entropiq'); -- 6
|
||||||
|
INSERT INTO teams (tournamentId, name) VALUES (2, 'Team Vitality'); -- 7
|
||||||
|
INSERT INTO teams (tournamentId, name) VALUES (2, 'Godsent'); -- 8
|
||||||
|
INSERT INTO teams (tournamentId, name) VALUES (2, 'Team Secret'); -- 9
|
||||||
|
INSERT INTO teams (tournamentId, name) VALUES (2, 'Virtus.pro'); -- 10
|
||||||
|
INSERT INTO teams (tournamentId, name) VALUES (2, 'Natus Vincere'); -- 11
|
||||||
|
INSERT INTO teams (tournamentId, name) VALUES (2, 'FaZe'); -- 12
|
||||||
|
|
||||||
|
INSERT INTO teams(tournamentId, name) VALUES (3, 'Fnatic'); -- 13
|
||||||
|
INSERT INTO teams(tournamentId, name) VALUES (3, 'Cloud 9'); -- 14
|
||||||
|
INSERT INTO teams(tournamentId, name) VALUES (3, 'Team Liquid'); -- 15
|
||||||
|
INSERT INTO teams(tournamentId, name) VALUES (3, 'LDLC'); -- 16
|
||||||
|
|
||||||
|
|
||||||
|
-- tournament 1 --
|
||||||
|
-- Final match
|
||||||
|
INSERT INTO matches (tournamentId, parentMatchId, team1Id, team2Id, tier) VALUES (1, NULL, NULL, NULL, 0); -- 1
|
||||||
|
-- Semi-finals
|
||||||
|
INSERT INTO matches (tournamentId, parentMatchId, team1Id, team2Id, tier) VALUES (1, 1, 1, 2, 1); -- 2
|
||||||
|
INSERT INTO matches (tournamentId, parentMatchId, team1Id, team2Id, tier) VALUES (1, 1, 3, 4, 1); -- 3
|
||||||
|
|
||||||
|
-- tournament 2 --
|
||||||
|
-- Final match
|
||||||
|
INSERT INTO matches (tournamentId, parentMatchId, team1Id, team2Id, tier) VALUES (2, NULL, NULL, NULL, 0); -- 4
|
||||||
|
-- Semi-finals
|
||||||
|
INSERT INTO matches (tournamentId, parentMatchId, team1Id, team2Id, tier) VALUES (2, 4, NULL, NULL, 1); -- 5
|
||||||
|
INSERT INTO matches (tournamentId, parentMatchId, team1Id, team2Id, tier) VALUES (2, 4, NULL, NULL, 1); -- 6
|
||||||
|
-- Quarter-finals
|
||||||
|
INSERT INTO matches (tournamentId, parentMatchId, team1Id, team2Id, tier) VALUES (2, 5, 5, 6, 2); -- 7
|
||||||
|
INSERT INTO matches (tournamentId, parentMatchId, team1Id, team2Id, tier) VALUES (2, 5, 7, 8, 2); -- 8
|
||||||
|
INSERT INTO matches (tournamentId, parentMatchId, team1Id, team2Id, tier) VALUES (2, 6, 9, 10, 2); -- 9
|
||||||
|
INSERT INTO matches (tournamentId, parentMatchId, team1Id, team2Id, tier) VALUES (2, 6, 11, 12, 2); -- 10
|
||||||
|
|
||||||
|
-- tournament 3 --
|
||||||
|
-- Final match
|
||||||
|
INSERT INTO matches (tournamentId, parentMatchId, team1Id, team2Id, tier, winnerId) VALUES (3, NULL, 14, 15, 0, 14); -- 11
|
||||||
|
-- Semi-finals
|
||||||
|
INSERT INTO matches (tournamentId, parentMatchId, team1Id, team2Id, tier, winnerId) VALUES (3, 11, 13, 14, 1, 14); -- 12
|
||||||
|
INSERT INTO matches (tournamentId, parentMatchId, team1Id, team2Id, tier, winnerId) VALUES (3, 11, 15, 16, 1, 15); -- 13
|
||||||
|
|
||||||
|
-- Users
|
||||||
|
INSERT INTO users (email, isManager) VALUES ('felixalbrigtsen@gmail.com', 1);
|
||||||
|
INSERT INTO users (email, isManager) VALUES ('kriloneri@gmail.com', 1);
|
||||||
|
INSERT INTO users (email, isManager) VALUES ('limboblivion@gmail.com', 1);
|
||||||
|
INSERT INTO users (email, isManager) VALUES ('jonas.haugland98@gmail.com', 1);
|
|
@ -0,0 +1,3 @@
|
||||||
|
autossh -L 3306:mysql.stud.ntnu.no:3306 isvegg -N &
|
||||||
|
|
||||||
|
npm start
|
File diff suppressed because it is too large
Load Diff
|
@ -4,8 +4,24 @@
|
||||||
"description": "DCST1008 Project - Server - Asura Tournament Management System",
|
"description": "DCST1008 Project - Server - Asura Tournament Management System",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "echo \"Error: no test specified\" && exit 1"
|
"start": "nodemon index.js",
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1",
|
||||||
|
"initdb": "mysql -h mysql.stud.ntnu.no -u felixalb_sysut -p felixalb_asura < ./management/initDB.sql"
|
||||||
},
|
},
|
||||||
"author": "felixalb, kristoju, jonajha, krisleri",
|
"author": "felixalb, kristoju, jonajha, krisleri",
|
||||||
"license": "ISC"
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"dotenv": "^16.0.0",
|
||||||
|
"ejs": "^3.1.6",
|
||||||
|
"express": "^4.17.3",
|
||||||
|
"express-log-url": "^1.5.1",
|
||||||
|
"express-session": "^1.17.2",
|
||||||
|
"mysql": "^2.18.1",
|
||||||
|
"passport": "^0.5.2",
|
||||||
|
"passport-google-oauth": "^2.0.0",
|
||||||
|
"sequelize": "^6.17.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"nodemon": "^2.0.15"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>test</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Replace with files from client build</h1>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
|
@ -0,0 +1,17 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Document</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Tournament</h1>
|
||||||
|
<div id="output"></div>
|
||||||
|
<script>
|
||||||
|
window.TOURNAMENT = JSON.parse('<%- JSON.stringify(tournament) %>');
|
||||||
|
document.getElementById("output").innerHTML = JSON.stringify(TOURNAMENT);
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,600 @@
|
||||||
|
// 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,
|
||||||
|
changeManagerStatus: changeManagerStatus,
|
||||||
|
deleteUser, deleteUser,
|
||||||
|
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;
|
||||||
|
user.asuraId = user.id;
|
||||||
|
user.id = undefined;
|
||||||
|
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 || users.length == 0) {
|
||||||
|
reject("No such user exists");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
users[0].isManager = users[0].isManager == 1;
|
||||||
|
users[0].asuraId = users[0].id;
|
||||||
|
users[0].id = undefined;
|
||||||
|
resolve(users[0]);
|
||||||
|
} catch (e) {
|
||||||
|
reject("No such user exists");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
users[0].asuraId = users[0].id;
|
||||||
|
users[0].id = undefined;
|
||||||
|
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) {
|
||||||
|
if (user.isManager == undefined) {
|
||||||
|
user.isManager = false;
|
||||||
|
}
|
||||||
|
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 changeManagerStatus(userId, isManager) {
|
||||||
|
return new Promise(function(resolve, reject) {
|
||||||
|
let isManagerInt = (isManager === true || isManager === "true") ? 1 : 0;
|
||||||
|
connection.query("UPDATE users SET isManager = ? WHERE id = ?", [isManagerInt, escapeString(userId)], (err, sets) => {
|
||||||
|
if (err) {
|
||||||
|
console.log(err);
|
||||||
|
reject(err);
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (sets.affectedRows == 0) {
|
||||||
|
reject("No such user exists");
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resolve("User updated");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteUser(userId) {
|
||||||
|
return new Promise(function(resolve, reject) {
|
||||||
|
connection.query("DELETE FROM users WHERE id = ?", [escapeString(userId)], (err, sets) => {
|
||||||
|
if (err) {
|
||||||
|
console.log(err);
|
||||||
|
reject(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (sets.affectedRows == 0) {
|
||||||
|
reject("No such user exists");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
resolve("User deleted");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// #endregion
|
Loading…
Reference in New Issue