From da3f74f8bb1560045c0cf8193791cc590500068e Mon Sep 17 00:00:00 2001 From: fcaps Date: Sat, 18 Nov 2023 02:51:36 +0100 Subject: [PATCH] cherry-pick from account branch, a non wip copy from javi --- express.js | 25 ++- public/js/app/clans.js | 1 - public/js/app/getClans.js | 72 ------- public/styles/site/clans.sass | 9 - routes/views/clans/get/clans.js | 29 +++ routes/views/clans/get/create.js | 8 +- routes/views/clans/get/getClan.js | 64 +++++++ routes/views/clans/get/manage.js | 240 +++++++++++------------- routes/views/clans/post/create.js | 190 +++++++------------ routes/views/clans/post/destroy.js | 139 +++----------- routes/views/clans/post/invite.js | 189 ++++--------------- routes/views/clans/post/join.js | 97 ++-------- routes/views/clans/post/kick.js | 109 ++--------- routes/views/clans/post/leave.js | 130 +++---------- routes/views/clans/post/transfer.js | 212 +++++++-------------- routes/views/clans/post/update.js | 234 ++++++++--------------- templates/mixins/flash-messages.pug | 13 +- templates/views/clans/accept_invite.pug | 17 +- templates/views/clans/create.pug | 64 +++---- templates/views/clans/getClan.pug | 24 +++ templates/views/clans/manage.pug | 239 +++++++++++------------ templates/views/clans/seeClan.pug | 27 --- 22 files changed, 738 insertions(+), 1394 deletions(-) delete mode 100644 public/js/app/getClans.js create mode 100644 routes/views/clans/get/clans.js create mode 100644 routes/views/clans/get/getClan.js create mode 100644 templates/views/clans/getClan.pug delete mode 100644 templates/views/clans/seeClan.pug diff --git a/express.js b/express.js index 310938ff..73599592 100644 --- a/express.js +++ b/express.js @@ -102,12 +102,12 @@ app.use("/news", newsRouter) // --- UNPROTECTED ROUTES --- const appGetRouteArray = [ // This first '' is the home/index page - '', 'newshub', 'campaign-missions', 'scfa-vs-faf', 'donation', 'tutorials-guides', 'ai', 'patchnotes', 'faf-teams', 'contribution', 'content-creators', 'tournaments', 'training', 'leaderboards', 'play', 'clans',]; + '', 'newshub', 'campaign-missions', 'scfa-vs-faf', 'donation', 'tutorials-guides', 'ai', 'patchnotes', 'faf-teams', 'contribution', 'content-creators', 'tournaments', 'training', 'leaderboards', 'play']; //Renders every page written above appGetRouteArray.forEach(page => app.get(`/${page}`, (req, res) => { // disabled due https://github.com/FAForever/website/issues/445 - if (['leaderboards', 'clans'].includes(page)) { + if (['leaderboards'].includes(page)) { return res.status(503).render('errors/503-known-issue') } res.render(page); @@ -150,24 +150,21 @@ const routes = './routes/views/'; const clansRoutesGet = [ 'create', 'manage', 'accept_invite',]; -// disabled due https://github.com/FAForever/website/issues/445 -// clansRoutesGet.forEach(page => app.get(`/clans/${page}`, loggedIn, require(`${routes}clans/get/${page}`))); -clansRoutesGet.forEach(page => app.get(`/clans/${page}`, loggedIn, (req, res) => res.status(503).render('errors/503-known-issue'))); +clansRoutesGet.forEach(page => app.get(`/clans/${page}`, loggedIn, require(`${routes}clans/get/${page}`))); const clansRoutesPost = [ 'create', 'destroy', 'invite', 'kick', 'transfer', 'update', 'leave', 'join',]; -// disabled due https://github.com/FAForever/website/issues/445 -// clansRoutesPost.forEach(page => app.post(`/clans/${page}`, loggedIn, require(`${routes}clans/post/${page}`))); -clansRoutesPost.forEach(page => app.post(`/clans/${page}`, loggedIn, (req, res) => res.status(503).render('errors/503-known-issue'))); +clansRoutesPost.forEach(page => app.post(`/clans/${page}`, loggedIn, require(`${routes}clans/post/${page}`))); +app.get('/clans/*', (req, res) => { + let id = req.path.slice(-3); + res.redirect(`/clans/getClan?tag=${id}`); +}); -// disabled due https://github.com/FAForever/website/issues/445 +//Unprotected clan routes +app.get('/clans', require(`${routes}clans/get/clans`)); +app.get('/clans/getClan', require(`${routes}clans/get/getClan`)); //When searching for a specific clan -// app.get('/clans/*', (req, res) => { -// res.render(`clans/seeClan`); -// }); -app.get('/clans/*', (req, res) => res.status(503).render('errors/503-known-issue')); - // Markdown Routes diff --git a/public/js/app/clans.js b/public/js/app/clans.js index 9ddd1faa..3859ffcb 100644 --- a/public/js/app/clans.js +++ b/public/js/app/clans.js @@ -47,7 +47,6 @@ function clanUpdate() { for (clanIndex; clanIndex < next100Players; clanIndex++) { if (clanIndex < 0) { clanIndex = 0; - console.log('There are no more players left.'); } // Gets the player data and inserts it into the li element diff --git a/public/js/app/getClans.js b/public/js/app/getClans.js deleted file mode 100644 index f34fec5e..00000000 --- a/public/js/app/getClans.js +++ /dev/null @@ -1,72 +0,0 @@ -const clanName = document.getElementById('clanName'); -const clanTag = document.getElementById('clanTag'); -const clanDescription = document.getElementById('clanDescription'); -const clanCreation = document.getElementById('clanCreation'); -const clanLeader = document.getElementById('clanLeader'); -const clanMembers = document.getElementById('clanMembers'); - - -let leaderName = ''; - -async function getClan() { - - //So here we check the tag of the clan in the url - let url = window.location.href; - const sliceIndicator = url.indexOf('/clans'); -// The slice has + 7 because thats the amount of characters in "/clans/" yes with two /, not one! - let findClanTag = url.slice(sliceIndicator + 7, sliceIndicator + 10); - let clanTag = await findClanTag.replace(/\?m|\?/gm,''); - - // We compare the url TAG with the TAGS available in getAllClans and find the clan leader this way - - // TODO: Change this hardcoded url into something with env - const response = await fetch(`https://api.faforever.com/data/clan?include=memberships.player&filter=tag==${clanTag}`); - const fetchData = await response.json(); - - const leaderID = fetchData.data[0].relationships.leader.data.id; - - fetchData.included.forEach((element, index) => { - if (index % 2 !== 0) { - if (element.id === leaderID) { - leaderName = element.attributes.login; - } - } - }); - - - //verifies if user is a member, which allows them to leave the clan - const clanMember = document.getElementById('iAmMember'); - const isMember = url.indexOf('?member'); - let verifyMembership = url.slice(isMember + 8); - if (verifyMembership === 'true') { - clanMember.style.display = 'block'; - } - return fetchData; -} -setTimeout( ()=> { - getClan() - .then(fetchData => { - - const { attributes} = fetchData.data[0]; - clanName.insertAdjacentHTML('afterbegin', - `${attributes.name}`); - clanDescription.insertAdjacentHTML('afterbegin', - `${attributes.description}`); - clanTag.insertAdjacentHTML('afterbegin', - `Welcome to "${attributes.tag}"`); - //clanLeader.insertAdjacentHTML('afterbegin', - // `${fetchData.data[0].attributes.id}`); - clanCreation.insertAdjacentHTML('afterbegin', - `Created on ${attributes.createTime.slice(0, 10)}`); - clanLeader.insertAdjacentHTML('afterbegin', - `Led by ${leaderName}`); - - for (let i = 0; i < fetchData.included.length; i++) { - if (i % 2 !== 0) { - clanMembers.insertAdjacentHTML('afterbegin', - `
  • ${fetchData.included[i].attributes.login}
  • `); - } - } - }); - -},750); diff --git a/public/styles/site/clans.sass b/public/styles/site/clans.sass index d3f94d76..3c38a387 100644 --- a/public/styles/site/clans.sass +++ b/public/styles/site/clans.sass @@ -178,15 +178,6 @@ input textarea width: 40vw - .clanManagementDanger - background-color: variables.$Cybran-dark - border-radius: 20px - ul - text-align: center - li - display: inline-block - list-style: none - text-align: left .clanManagementTable padding: 1.5em display: inline-block diff --git a/routes/views/clans/get/clans.js b/routes/views/clans/get/clans.js new file mode 100644 index 00000000..e88c966c --- /dev/null +++ b/routes/views/clans/get/clans.js @@ -0,0 +1,29 @@ +exports = module.exports = function (req, res) { + let flash = {}; + if (req.query.flash) { + + flash.class = 'alert-success'; + flash.type = 'Success!'; + switch (req.query.flash) { + case 'leave': + flash.messages = 'You left your clan.'; + break; + + case 'destroy': + flash.messages = 'You deleted your clan.'; + break; + + case 'transfer': + flash.messages = `You have transferred your clan to ${req.query.newLeader}.`; + break; + + case 'error': + flash.class = 'alert-danger'; + flash.messages = 'There was an issue with your request.'; + flash.type = 'Error!'; + break; + } + } + res.render('clans', {flash: flash}); + +}; diff --git a/routes/views/clans/get/create.js b/routes/views/clans/get/create.js index 4b6a13eb..5a4ca878 100644 --- a/routes/views/clans/get/create.js +++ b/routes/views/clans/get/create.js @@ -10,10 +10,10 @@ exports = module.exports = function(req, res) { request.get( { - url: process.env.API_URL + '/clans/me', - headers: { - 'Authorization': 'Bearer ' + req.user.data.attributes.token - } + url: process.env.API_URL + '/clans/me', + headers: { + 'Authorization': 'Bearer ' + req.user.token + } }, function (err, childRes, body) { diff --git a/routes/views/clans/get/getClan.js b/routes/views/clans/get/getClan.js new file mode 100644 index 00000000..371454b0 --- /dev/null +++ b/routes/views/clans/get/getClan.js @@ -0,0 +1,64 @@ +const axios = require('axios'); +require('dotenv').config(); +exports = module.exports = function(req, res) { + + if (!req.query.tag) res.redirect('../clans?flash=error'); + else { + + + //We call the API and get the info needed + axios.get(`${process.env.API_URL}/data/clan?include=memberships.player&filter=tag==${req.query.tag.toLowerCase()}` + ).then(response => { + const {attributes} = response.data.data[0]; + const {name, description, createTime} = attributes; + + // first lets check user is logged in and has a clan + if (req.user && req.user.data.attributes.clan !== undefined) { + // lets check if the user belongs to the clan + if (req.user.data.attributes.clan.tag.toLowerCase() === req.query.tag.toLowerCase()) { + res.locals.leaveButton = true; + } + } + + //We set the values as local variables in our response + res.locals.clanName = name; + res.locals.clanDescription = description; + res.locals.clanCreation = createTime.slice(0, 10); + res.locals.clanTag = req.query.tag.toUpperCase(); + + + //We add in the clan members + let clanMembers = []; + response.data.included.forEach((member, index) => { + // We only allow odd numbers because the API brings extra information on even numbers that don't include a members login/username + if (index % 2 !== 0) { + clanMembers.push(member.attributes.login); + } + }); + res.locals.clanMembers = clanMembers; + + //We find the clan leader + const leaderID = response.data.data[0].relationships.leader.data.id; + response.data.included.forEach((element, index) => { + if (index % 2 !== 0) { + if (element.id === leaderID) { + res.locals.clanLeaderName = element.attributes.login; + } + } + }); + + + + + }).catch((e) => { + + res.redirect('../clans?flash=error'); + + }).finally(() => { + + + + res.render('clans/getClan'); + }); + } +}; diff --git a/routes/views/clans/get/manage.js b/routes/views/clans/get/manage.js index 5bcd4ab6..8a1ac814 100755 --- a/routes/views/clans/get/manage.js +++ b/routes/views/clans/get/manage.js @@ -1,134 +1,120 @@ -const request = require('request'); - -exports = module.exports = function(req, res) { - - let locals = res.locals; - - // locals.section is used to set the currently selected - // item in the header navigation. - locals.section = 'clan'; - - let flash = null; - - let clanMembershipId = null; - try{ - clanMembershipId = req.user.data.attributes.clan.membershipId; - } - catch{ - // The user doesnt belong to a clan - res.redirect('/clans'); - return; - } - - // In case the user has just generated an invite link - if (req.query.invitation_id) { - flash = {}; - flash.class = 'alert-invite'; - - flash.messages = [ - {msg: - `

    Right click on me and copy the invitation link

    Note: It only works for the user you typed.`} - ]; - flash.type = ''; - - } - - - - - request.get( - { - url: - process.env.API_URL - + '/data/clanMembership/'+clanMembershipId+'/clan' - + '?include=memberships.player' - + '&fields[clan]=createTime,description,name,tag,updateTime,websiteUrl,founder,leader' - + '&fields[player]=login,updateTime' - + '&fields[clanMembership]=createTime,player', - headers: { - 'Authorization': 'Bearer ' + req.user.data.attributes.token - } - }, - function (err, childRes, body) { - - const clan = JSON.parse(body); - - if (err || !clan.data){ - flash = {}; - flash.class = 'alert-danger'; - flash.messages = [{msg: "Unknown error while retrieving your clan information"}]; - flash.type = 'Error!'; - - let buff = Buffer.from(JSON.stringify(flash)); - let data = buff.toString('base64'); - - return res.redirect('/clans?flash='+data); - } - - if (clan.data.relationships.leader.data.id != req.user.data.id){ - // Not the leader! Shouldn't be able to manage stuff - res.redirect(`/clans/${req.user.data.attributes.clan.tag}?member=true`); +const axios = require("axios"); +const error = require("../../account/post/error"); + +exports = module.exports = function (req, res) { + + let flash = {}; + let clanMembershipId = null; + try { + clanMembershipId = req.user.data.attributes.clan.membershipId; + } catch (e) { + // The user doesnt belong to a clan + res.redirect('../clans'); return; - } - - locals.clan_name = clan.data.attributes.name; - locals.clan_tag = clan.data.attributes.tag; - locals.clan_description = clan.data.attributes.description; - locals.clan_create_time = clan.data.attributes.createTime; - locals.me = req.user.data.id; - locals.clan_id = clan.data.id; - locals.clan_link = process.env.HOST + "/clans/see?id="+clan.data.id; - - let members = {}; - - for (k in clan.included){ - switch(clan.included[k].type){ - case "player": - const player = clan.included[k]; - if (!members[player.id]) members[player.id] = {}; - members[player.id].id = player.id; - members[player.id].name = player.attributes.login; - - if (clan.data.relationships.founder.data.id == player.id){ - locals.founder_name = player.attributes.login + } + + + axios.get(`${process.env.API_URL}/data/clanMembership/${clanMembershipId}/clan?include=memberships.player&fields[clan]=createTime,description,name,tag,updateTime,websiteUrl,founder,leader&fields[player]=login,updateTime&fields[clanMembership]=createTime,player`, null, + { + headers: {'Authorization': `Bearer ${req.user.token}`}, + + }).then(response => { + + let clan = response.data + + // Not the leader! Shouldn't be able to manage stuff + if (clan.data.relationships.leader.data.id != req.user.data.attributes.userId) { + res.redirect(`/clans/getClan?tag=${req.user.data.attributes.clan.tag}`); + } else { + + + // Lets create the schema for all the members and clan descriptions + res.locals.clan_name = clan.data.attributes.name; + res.locals.clan_tag = clan.data.attributes.tag; + res.locals.clan_description = clan.data.attributes.description; + res.locals.clan_create_time = clan.data.attributes.createTime; + res.locals.me = req.user.data.attributes.userId; + res.locals.clan_id = clan.data.id; + + let members = {}; + + for (k in clan.included) { + switch (clan.included[k].type) { + case "player": + const player = clan.included[k]; + if (!members[player.id]) members[player.id] = {}; + members[player.id].id = player.id; + members[player.id].name = player.attributes.login; + + if (clan.data.relationships.founder.data.id == player.id) { + res.locals.founder_name = player.attributes.login + } + break; + + case "clanMembership": + const membership = clan.included[k]; + const member = membership.relationships.player.data; + if (!members[member.id]) members[member.id] = {}; + members[member.id].id = member.id; + members[member.id].membershipId = membership.id; + members[member.id].joinedAt = membership.attributes.createTime; + break; + } + } + res.locals.clan_members = members; + + // Lets check the different flash types + if (req.query.flash) { + flash.class = 'alert-success'; + flash.type = 'Success!'; + switch (req.query.flash) { + + case 'created': + flash.messages = 'You have created your clan.'; + break; + + case 'kick': + flash.messages = `You have kicked ${req.query.kickPlayer}.`; + break; + + case 'update': + flash.messages = `You have updated your clan information.`; + break; + + + case 'error': + flash.class = 'alert-danger'; + flash.messages = 'There was an error with your request.'; + flash.type = 'Error!'; + break; + case 'alreadyTaken': + flash.class = 'alert-danger'; + flash.messages = 'The clan name/tag is already taken. Choose a different one.'; + flash.type = 'Error!'; + break; + } } - break; - case "clanMembership": - const membership = clan.included[k]; - const member = membership.relationships.player.data; - if (!members[member.id]) members[member.id] = {}; - members[member.id].id = member.id; - members[member.id].membershipId = membership.id; - members[member.id].joinedAt = membership.attributes.createTime; - break; + //Lets check if they tried inviting an user + if (req.query.invitation_id && req.query.invitation_id !== 'error') { + + flash.class = 'alert-invite'; + flash.hasHTML = `${process.env.HOST}/clans/accept_invite?i=${req.query.invitation_id}`; + flash.type = 'invite'; + } else if (req.query.invitation_id === 'error') { + flash.class = 'alert-danger'; + flash.messages = `User isn't a valid username (check your spelling). If error continues contact support`; + flash.type = 'Error!'; + } + res.render('clans/manage', {flash: flash}); } - } - - locals.clan_members = members; - - if (req.originalUrl == '/clan_created') { - flash = {}; - flash.class = 'alert-success'; - flash.messages = [{msg: 'You have successfully created your clan'}]; - flash.type = 'Success!'; - } - else if (req.query.flash){ - let buff = Buffer.from(req.query.flash, 'base64'); - let text = buff.toString('ascii'); - try{ - flash = JSON.parse(text); - } - catch(e){ - console.error("Parsing error while trying to decode a flash error: " + text); - console.error(e); - flash = [{msg: "Unknown error"}]; - } - } + }).catch((e) => { + console.log(e); + error.parseApiErrors(e.response, flash); + res.render('clans/manage', {flash: flash}); + + + }); - // Render the view - res.render('clans/manage', {flash: flash}); - } - ); }; diff --git a/routes/views/clans/post/create.js b/routes/views/clans/post/create.js index 8c43d917..18ac900c 100755 --- a/routes/views/clans/post/create.js +++ b/routes/views/clans/post/create.js @@ -1,130 +1,66 @@ let flash = {}; -let request = require('request'); -const {check, validationResult} = require('express-validator'); +const axios = require('axios'); +const {body, validationResult} = require('express-validator'); +const error = require("../../account/post/error"); + +exports = module.exports = [ + + // validate the input + body('clan_tag', 'Your clan tag is too long (max 3 characters)').notEmpty().isLength({max: 3}), + body('clan_description', 'Your clan description is too long (max 1000 characters)').notEmpty().isLength({max: 1000}), + body('clan_name', 'Your clan name is too long (max 40 characters)').isLength({max: 40}), + async (req, res) => { + // check the validation object for errors + if (!validationResult(req).isEmpty()) error.errorChecking(req, res, 'clans/create'); + // No errors in form, continue ahead + else { + + // Take variables from form + const clanName = req.body.clan_name; + const clanTag = req.body.clan_tag; + const clanDescription = req.body.clan_description; + + // We check if the clan already exists + let clanExists = ''; + await axios.get(`${process.env.API_URL}/data/clan?filter=name=="${clanName}",tag=="${clanTag}"`) + .then(response => { + clanExists = !response.data.data[0]; + }).catch(e => { + error.parseApiErrors(e.response, flash); + }); + if (!clanExists) { + flash.class = 'alert-danger'; + flash.messages = 'The clan tag/name are already taken. Choose a different one.'; + flash.type = 'Error!'; + res.render('clans/create', {flash: flash}); + + } + // Clan doesn't exist, lets create it! + else { + axios.post(`${process.env.API_URL}/clans/create?name=${clanName}&tag=${clanTag}&description=${clanDescription}`, null, + { + headers: {'Authorization': 'Bearer ' + req.user.token} + }).then( () => { + + // Refreshing user + axios.get(`${process.env.API_URL}/me`, { + headers: { + 'Authorization': `Bearer ${req.user.token}`, + } + }).then( () => { + + //Lets update our user + error.userUpdate(req, res, '/clans/create' ); + }).catch(e => { + error.parseApiErrors(e.response, flash); + }); + + }).catch((e) => { + error.parseApiErrors(e.response, flash); + res.render('clans/create', {flash: flash}); + }); + } -function promiseRequest(url) { - return new Promise(function (resolve, reject) { - request(url, function (error, res, body) { - if (!error && res.statusCode < 300) { - resolve(body); - } else { - reject(error); - } - }); - }); -} - -exports = module.exports = async function (req, res) { - - let locals = res.locals; - - locals.formData = req.body || {}; - - let overallRes = res; - - // validate the input - check('clan_tag', 'Please indicate the clan tag - No special characters and 3 characters maximum').notEmpty().isLength({max: 3}); - check('clan_description', 'Please add a description for your clan').notEmpty().isLength({max: 1000}); - check('clan_name', "Please indicate your clan's name").notEmpty().isLength({max: 40}); - - // check the validation object for errors - let errors = validationResult(req); - - //Must have client side errors to fix - if (!errors.isEmpty()) { - flash.class = 'alert-danger'; - flash.messages = errors; - flash.type = 'Error!'; - - let buff = Buffer.from(JSON.stringify(flash)); - let data = buff.toString('base64'); - - return overallRes.redirect('create?flash=' + data); - } else { - - const clanName = req.body.clan_name; - const clanTag = req.body.clan_tag; - const clanDescription = req.body.clan_description; - const userId = req.body.user_id; - - // Let's check first that the name or tag are not taken - const clanFetchRoute = process.env.API_URL+'/data/clan?filter=name=="'+clanName+'",tag=="'+clanTag+'"'; - let exists = true; - try { - const httpData = await promiseRequest(clanFetchRoute); - exists = JSON.parse(httpData).data.length > 0; - } - catch(e){ - flash.class = 'alert-danger'; - flash.messages = [{msg: 'Error while creating the clan '+e}]; - flash.type = 'Error!'; - - let buff = Buffer.from(JSON.stringify(flash)); - let data = buff.toString('base64'); - - return overallRes.redirect('create?flash='+data+'&clan_name='+clanName+'&clan_tag='+clanTag+'&clan_description='+clanDescription+''); - } - - const queryUrl = - process.env.API_URL - + '/clans/create' - + '?name=' + encodeURIComponent(clanName) - + '&tag='+encodeURIComponent(clanTag) - + '&description='+encodeURIComponent(clanDescription) - ; - - //Run post to endpoint - request.post({ - url: queryUrl, - body: "", - headers: { - 'Authorization': 'Bearer ' + req.user.data.attributes.token - } - }, function (err, res, body) { - - let resp; - let errorMessages = []; - - if (res.statusCode !== 200) { - let msg = 'Error while creating the clan'; - try { - - msg += ': ' + JSON.stringify(JSON.parse(res.body).errors[0].detail); - } catch { } - errorMessages.push({msg: msg}); - flash.class = 'alert-danger'; - flash.messages = errorMessages; - flash.type = 'Error!'; - - let buff = Buffer.from(JSON.stringify(flash)); - let data = buff.toString('base64'); - - return overallRes.redirect('create?flash='+data+'&clan_name='+clanName+'&clan_tag='+clanTag+'&clan_description='+clanDescription+''); - } - - // Refreshing user - request.get({ - url: process.env.API_URL + '/me', - headers: { - 'Authorization': 'Bearer ' + req.user.data.attributes.token, - } - }, - function (err, res, body) { - try{ - let user = JSON.parse(body); - user.data.attributes.token = req.user.data.attributes.token; - user.data.id = user.data.attributes.userId; - req.logIn(user, function(err){ - if (err) console.error(err); - return overallRes.redirect('/clans/manage'); - }); - } - catch{ - console.error("There was an error updating a session after a clan creation"); - } - }); - - }); - } -} + } +]; diff --git a/routes/views/clans/post/destroy.js b/routes/views/clans/post/destroy.js index 0caba265..01a42236 100755 --- a/routes/views/clans/post/destroy.js +++ b/routes/views/clans/post/destroy.js @@ -1,111 +1,30 @@ -let flash = {}; -let request = require('request'); -const {check, validationResult} = require('express-validator'); - -function promiseRequest(url) { - return new Promise(function (resolve, reject) { - request(url, function (error, res, body) { - if (!error && res.statusCode < 300) { - resolve(body); - } else { - reject(error); - } - }); - }); -} - -exports = module.exports = async function (req, res) { - - let locals = res.locals; - - locals.formData = req.body || {}; - - let overallRes = res; - - // validate the input - check('clan_id', 'Internal error while processing your query: invalid clan ID').notEmpty(); - - // check the validation object for errors - let errors = validationResult(req); - - //Must have client side errors to fix - if (!errors.isEmpty()) { - flash.class = 'alert-danger'; - flash.messages = errors; - flash.type = 'Error!'; - - let buff = Buffer.from(JSON.stringify(flash)); - let data = buff.toString('base64'); - - return overallRes.redirect('manage?flash=' + data); - } else { - - // Building update query - const queryUrl = - process.env.API_URL - + '/data/clan/' + req.body.clan_id - ; - - //Run post to endpoint - request.delete({ - url: queryUrl, - body: "", - headers: { - 'Authorization': 'Bearer ' + req.user.data.attributes.token - } - }, function (err, res, body) { - - let resp; - let errorMessages = []; - - if (res.statusCode != 204) { - let msg = 'Error while destroying the clan'; - try{ - - msg += ': '+JSON.stringify(JSON.parse(res.body).errors[0].detail); - } - catch{} - errorMessages.push({msg: msg}); - flash.class = 'alert-danger'; - flash.messages = errorMessages; - flash.type = 'Error!'; - - let buff = Buffer.from(JSON.stringify(flash)); - let data = buff.toString('base64'); - - return overallRes.redirect('manage?flash='+data); - } - - flash = {}; - flash.class = 'alert-success'; - flash.messages = [{msg: 'The clan was successfully destroyed'}]; - flash.type = 'Success!'; - - let buff = Buffer.from(JSON.stringify(flash)); - let data = buff.toString('base64'); - - // Refreshing user - request.get({ - url: process.env.API_URL + '/me', - headers: { - 'Authorization': 'Bearer ' + req.user.data.attributes.token, - } - }, - - function (err, res, body) { - try{ - let user = JSON.parse(body); - user.data.id = user.data.attributes.userId; - user.data.attributes.token = req.user.data.attributes.token; - req.logIn(user, function(err){ - if (err) console.error(err); - return overallRes.redirect('/clans?flash='+data); +let axios = require('axios'); +const {body, validationResult} = require('express-validator'); +const error = require("../../account/post/error"); + + +exports = module.exports = [ + // validate the input + body('clan_id', 'Internal error while processing your query: invalid clan ID').notEmpty(), + (req, res) => { + // check the validation object for errors + if (!validationResult(req).isEmpty()) error.errorChecking(req, res, 'clans'); + // No errors in form, continue ahead + + else { + //Run post to endpoint + axios.delete(`${process.env.API_URL}/data/clan/${req.body.clan_id}`, + { + headers: {'Authorization': `Bearer ${req.user.token}`} + }).then( ()=> { + + // Refreshing user + error.userUpdate(req, res, '/clans?flash=destroy'); + + }).catch((e) => { + res.redirect('manage?flash=error'); }); - } - catch{ - console.error("There was an error updating a session after a clan destruction"); - } - }); - }); - } -} + } + } +] +; diff --git a/routes/views/clans/post/invite.js b/routes/views/clans/post/invite.js index 918dcb21..2c25d82b 100644 --- a/routes/views/clans/post/invite.js +++ b/routes/views/clans/post/invite.js @@ -1,156 +1,45 @@ -let flash = {}; -const request = require('request'); -const {check, validationResult} = require('express-validator'); +const axios = require('axios'); +const error = require("../../account/post/error"); -function promiseRequest(url) { - return new Promise(function (resolve, reject) { - request(url, function (error, res, body) { - if (!error && res.statusCode < 300) { - resolve(body); - } else { - console.error("Call to " + url + " failed: " + error); - reject(error); - } - }); - }); -} -function setLongTimeout(func, delayMs) { - const maxDelay = 214748364-1; // JS Limit for 32 bit integers - - if (delayMs > maxDelay) { - const remainingDelay = delayMs - maxDelay; - - // we cut it in smaller, edible chunks - setTimeout(() => { - setLongTimeout(func, remainingDelay); - }, maxDelay); - } - else{ - setTimeout(func, delayMs); - } -} - -exports = module.exports = async function (req, res) { - - let locals = res.locals; - - locals.formData = req.body || {}; - - let overallRes = res; - - - // validate the input - check('invited_player', 'Please indicate the player name').notEmpty(); - - // check the validation object for errors - let errors = validationResult(req); - - //Must have client side errors to fix - if (!errors.isEmpty()) { - flash.class = 'alert-danger'; - flash.messages = errors; - flash.type = 'Error!'; - - let buff = Buffer.from(JSON.stringify(flash)); - let data = buff.toString('base64'); - - return overallRes.redirect('manage?flash=' + data); - } else { - - const clanId = req.body.clan_id; +exports = module.exports = async (req, res) => { + // Let's get the local variables + const clanId = req.body.clan_id; const userName = req.body.invited_player; - - // Let's check first that the player exists - const fetchRoute = process.env.API_URL + '/data/player?filter=login=="' + userName + '"&fields[player]='; - - let exists = true; - let playerData = null; let playerId = null; - try { - const httpData = await promiseRequest(fetchRoute); - playerData = JSON.parse(httpData).data; - exists = playerData.length > 0; - playerId = playerData[0].id; - } - catch(e){ - flash.class = 'alert-danger'; - flash.messages = [{msg: 'The player ' + userName + " doesn't seem to exist" + e}]; - flash.type = 'Error!'; - - let buff = Buffer.from(JSON.stringify(flash)); - let data = buff.toString('base64'); - - return overallRes.redirect('manage?flash='+data); - } - - const queryUrl = - process.env.API_URL - + '/clans/generateInvitationLink' - + '?clanId=' + encodeURIComponent(clanId) - + '&playerId=' + encodeURIComponent(playerId) - ; - - //Run post to endpoint - request.get({ - url: queryUrl, - body: "", - headers: { - 'Authorization': 'Bearer ' + req.user.data.attributes.token - } - }, function (err, res, body) { - - if (res.statusCode !== 200) { - - let errorMessages = []; - let msg = 'Error while generating the invite link'; - try { - - msg += ': ' + JSON.stringify(JSON.parse(res.body).errors[0].detail); - } catch { - } - - errorMessages.push({msg: msg}); - flash.class = 'alert-danger'; - flash.messages = errorMessages; - flash.type = 'Error!'; - - let buff = Buffer.from(JSON.stringify(flash)); - let data = buff.toString('base64'); - return overallRes.redirect('manage?flash='+data); - } - else{ - try{ - const token = JSON.parse(res.body).jwtToken; - - const id = Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 5).toUpperCase(); - + // Let's check first that the player exists + await axios.get(`${process.env.API_URL}/data/player?filter=login==${userName}&fields[player]=`) + .then(response => { + + // Player exists + if (response.data.data[0] ? response.data.data[0] : false) { + playerId = response.data.data[0].id; + + } else { // Player doesn't exist + + res.redirect(`manage?invitation_id=error`); + } + }).catch(e => { + console.log(e); + res.redirect(`manage?invitation_id=error`); + }); + //Player does exist, lets create the invite link + if (playerId !== null) { + await axios.get(`${process.env.API_URL}/clans/generateInvitationLink?clanId=${clanId}&playerId=${playerId}`, + { + headers: {'Authorization': `Bearer ${req.user.token}`}, + }).then(response => { + let data = response.data; + const token = data.jwtToken; + let id = Math.random().toString(36).replace(/[^a-z]+/g, '').substring(0, 5).toUpperCase(); req.app.locals.clanInvitations[id] = { - token:token, - clan:clanId + token: token, + clan: clanId }; - - // We use timeout here because if we delete the invite link whenver the page is GET, - // then discord and other messaging applications will destroy the link accidentally - // when pre-fetching the page. So we will delete it later. Regardless if the website is restarted all the links will be - // killed instantly, which is fine. They are short lived by design. - const lifespan = process.env.CLAN_INVITES_LIFESPAN_DAYS * 24 * 3600 * 1000; - setLongTimeout(()=>{ - delete req.app.locals.clanInvitations[id]; - console.log(`Killed invitation with id ${id} after having waited ${lifespan} seconds (${process.env.CLAN_INVITES_LIFESPAN_DAYS} days)`); - }, lifespan); - - return overallRes.redirect('manage?invitation_id='+id); - } - catch (e){ - flash.class = 'alert-danger'; - flash.messages = [{msg:"Unkown error while generating the invite link: "+e}]; - flash.type = 'Error!'; - let buff = Buffer.from(JSON.stringify(flash)); - let data = buff.toString('base64'); - return overallRes.redirect('manage?flash='+data); - } - } - }); - } -} + res.redirect(`manage?invitation_id=${id}`); + }).catch(e => { + console.log(e); + res.redirect(`manage?invitation_id=error`); + }); + } +}; diff --git a/routes/views/clans/post/join.js b/routes/views/clans/post/join.js index b4e51d6b..d4d69c47 100644 --- a/routes/views/clans/post/join.js +++ b/routes/views/clans/post/join.js @@ -1,87 +1,28 @@ -const request = require('request'); +const axios = require("axios"); +const error = require("../../account/post/error"); -exports = module.exports = function(req, res) { +exports = module.exports = function (req, res) { - let locals = res.locals; - - // locals.section is used to set the currently selected - // item in the header navigation. - locals.section = 'clan'; - + // item in the header navigation. let flash = {}; - const overallRes = res; - - if (!req.query.token || !req.query.clan_id){ + + if (!req.query.token) { flash.type = 'Error!'; flash.class = 'alert-danger'; flash.messages = [{msg: 'The invitation link is invalid!'}]; + res.render('/clans', {flash: flash}); + } else { + const token = req.query.token; - let buff = Buffer.from(JSON.stringify(flash)); - let data = buff.toString('base64'); - - return res.redirect('/clans?flash='+data+''); + axios.post(`${process.env.API_URL}/clans/joinClan?token=${token}`, null, + { + headers: {'Authorization': `Bearer null`} + }).then(() => { + // Refreshing user, by going to clan/manage, user is redirected to their own clan. + error.userUpdate(req, res, '/clans/manage'); + }).catch( e => { + console.log(e); + res.redirect('../clans?flash=error'); + }); } - - const token = req.query.token; - const clanId = req.query.clan_id; - - request.post( - { - url: process.env.API_URL + '/clans/joinClan?token='+token, - headers: { - 'Authorization': 'Bearer ' + req.user.data.attributes.token - } - }, - function (err, childRes, body) { - let flashData; - if (childRes.statusCode == 200 || childRes.statusCode == 201){ - flash.class = 'alert-success'; - flash.messages = [ - {msg: "Welcome to your new clan!"} - ]; - flash.type = 'Success!'; - let buff = Buffer.from(JSON.stringify(flash)); - flashData = buff.toString('base64'); - - // Refreshing user - return request.get({ - url: process.env.API_URL + '/me', - headers: { - 'Authorization': 'Bearer ' + req.user.data.attributes.token, - } - }, - - function (err, res, body) { - try{ - let user = JSON.parse(body); - user.data.id = user.data.attributes.userId; - user.data.attributes.token = req.user.data.attributes.token; - req.logIn(user, function(err){ - if (err) console.error(err); - return overallRes.redirect(`${user.data.attributes.clan.tag}?member=true&flash=${flashData}`); - }); - } - catch{ - console.error("There was an error updating a session after an user left a clan"); - } - }); - } - else{ - flash.type = 'Error!'; - flash.class = 'alert-danger'; - let msg = 'The invitation is invalid or has expired, or you are already part of a clan'; - try{ - msg += ': '+JSON.stringify(JSON.parse(childRes.body).errors[0].detail); - } catch{} - - flash.messages = [{msg: msg}]; - - let buff = Buffer.from(JSON.stringify(flash)); - flashData = buff.toString('base64'); - - return overallRes.redirect('/clans?flash='+flashData+''); - } - - } - ); }; diff --git a/routes/views/clans/post/kick.js b/routes/views/clans/post/kick.js index 13914bf1..57a525eb 100755 --- a/routes/views/clans/post/kick.js +++ b/routes/views/clans/post/kick.js @@ -1,96 +1,25 @@ -let flash = {}; -let request = require('request'); -const {check, validationResult} = require('express-validator'); +let axios = require('axios'); -function promiseRequest(url) { - return new Promise(function (resolve, reject) { - request(url, function (error, res, body) { - if (!error && res.statusCode < 300) { - resolve(body); - } else { - reject(error); - } - }); - }); -} +exports = module.exports = function (req, res) { -exports = module.exports = async function (req, res) { + // Check if we are missing the member to kick or if someone is trying to kick themselves. Should not happen normally, but you never know + if (req.body.membership_id === req.user.data.attributes.clan.membershipId || !req.body.membership_id) { + res.redirect('manage?flash=error&error=missingData'); + } else { + const membershipId = req.body.membership_id; + const kickedPlayer = req.body.membership_name; + //Run post to endpoint + axios.delete(`${process.env.API_URL}/data/clanMembership/${membershipId}`, { + headers: {'Authorization': `Bearer ${req.user.token}`} + }).then(() => { + res.redirect(`manage?flash=kick&kickPlayer=${kickedPlayer}`); - let locals = res.locals; + }).catch((e) => { + console.log(e); + res.redirect(`manage?flash=error`); - locals.formData = req.body || {}; + }); - let overallRes = res; + } - // validate the input - check('clan_id', 'Internal error while processing your query: invalid clan ID').notEmpty(); - check('membership_id', 'Internal error while processing your query: invalid member ID').notEmpty(); - - // check the validation object for errors - let errors = validationResult(req); - - // Should not happen normally, but you never know - if (req.body.membership_id == req.user.data.attributes.clan.membershipId) errors = [{msg: "You cannot kick yourself"}]; - - //Must have client side errors to fix - if (!errors.isEmpty()) { - flash.class = 'alert-danger'; - flash.messages = errors; - flash.type = 'Error!'; - - let buff = Buffer.from(JSON.stringify(flash)); - let data = buff.toString('base64'); - - return overallRes.redirect('manage?flash=' + data); - } else { - - // Building update query - const membershipId = req.body.membership_id; - const queryUrl = - process.env.API_URL - + '/data/clanMembership/' + membershipId - - ; - - //Run post to endpoint - request.delete({ - url: queryUrl, - body: "", - headers: { - 'Authorization': 'Bearer ' + req.user.data.attributes.token - } - }, function (err, res, body) { - - let resp; - let errorMessages = []; - - if (res.statusCode != 204) { - let msg = 'Error while removing the member'; - try{ - - msg += ': '+JSON.stringify(JSON.parse(res.body).errors[0].detail); - } - catch{} - errorMessages.push({msg: msg}); - flash.class = 'alert-danger'; - flash.messages = errorMessages; - flash.type = 'Error!'; - - let buff = Buffer.from(JSON.stringify(flash)); - let data = buff.toString('base64'); - - return overallRes.redirect('manage?flash='+data); - } - - flash = {}; - flash.class = 'alert-success'; - flash.messages = [{msg: 'The member was kicked'}]; - flash.type = 'Success!'; - - let buff = Buffer.from(JSON.stringify(flash)); - let data = buff.toString('base64'); - - return overallRes.redirect('manage?flash='+data); - }); - } -} +}; diff --git a/routes/views/clans/post/leave.js b/routes/views/clans/post/leave.js index 2028675f..94ff8bd4 100755 --- a/routes/views/clans/post/leave.js +++ b/routes/views/clans/post/leave.js @@ -1,111 +1,25 @@ -let flash = {}; -let request = require('request'); -const {check, validationResult} = require('express-validator'); +const error = require("../../account/post/error"); +let axios = require('axios'); + +exports = module.exports = function (req, res) { + if (!req.user.data.attributes.clan) { + res.redirect('../clans?flash=error&error=missingData'); + } else { + const membershipId = req.user.data.attributes.clan.membershipId; + //Run post to endpoint + axios.delete(`${process.env.API_URL}/data/clanMembership/${membershipId}`, { + headers: { + 'Authorization': `Bearer ${req.user.token}` + } + }).then(() => { + + // Refreshing user + error.userUpdate(req, res, '/clans?flash=leave'); + + }).catch(e => { + console.log(e.response); + res.redirect(`../clans?flash=error`); -function promiseRequest(url) { - return new Promise(function (resolve, reject) { - request(url, function (error, res, body) { - if (!error && res.statusCode < 300) { - resolve(body); - } else { - reject(error); - } - }); - }); -} - -exports = module.exports = async function (req, res) { - - let locals = res.locals; - - locals.formData = req.body || {}; - - let overallRes = res; - - // validate the input - check('clan_id', 'Internal error while processing your query: invalid clan ID').notEmpty(); - check('membership_id', 'Internal error while processing your query: invalid member ID').notEmpty(); - - // check the validation object for errors - let errors = validationResult(req); - - //Must have client side errors to fix - if (!errors.isEmpty()) { - flash.class = 'alert-danger'; - flash.messages = errors; - flash.type = 'Error!'; - - let buff = Buffer.from(JSON.stringify(flash)); - let data = buff.toString('base64'); - - return overallRes.redirect('/clans?flash=' + data); - } else { - - // Building update query - const membershipId = req.body.membership_id; - const queryUrl = `${process.env.API_URL}/data/clanMembership/${membershipId}`; - - //Run post to endpoint - request.delete({ - url: `${process.env.API_URL}/data/clanMembership/${req.user.data.attributes.clan.membershipId}`, - headers: { - 'Authorization': 'Bearer ' + req.user.data.attributes.token - } - }, function (err, res, body) { - - let resp; - let errorMessages = []; - - if (res.statusCode != 204) { - let msg = 'Error while leaving the clan'; - try{ - - msg += ': '+JSON.stringify(JSON.parse(res.body).errors[0].detail); - } - catch{ - errorMessages.push({msg: msg}); - flash.class = 'alert-danger'; - flash.messages = errorMessages; - flash.type = 'Error!'; - } - - - let buff = Buffer.from(JSON.stringify(flash)); - let data = buff.toString('base64'); - - return overallRes.redirect('/clans?flash='+data); - } - - flash = {}; - flash.class = 'alert-success'; - flash.messages = [{msg: 'You left the clan'}]; - flash.type = 'Success!'; - - let buff = Buffer.from(JSON.stringify(flash)); - let data = buff.toString('base64'); - - // Refreshing user - request.get({ - url: process.env.API_URL + '/me', - headers: { - 'Authorization': 'Bearer ' + req.user.data.attributes.token, - } - }, - - function (err, res, body) { - try{ - let user = JSON.parse(body); - user.data.id = user.data.attributes.userId; - user.data.attributes.token = req.user.data.attributes.token; - req.logIn(user, function(err){ - if (err) console.error(err); - return overallRes.redirect('/clans?flash='+data); - }); - } - catch{ - console.error("There was an error updating a session after an user left a clan"); - } }); - }); - } + } }; diff --git a/routes/views/clans/post/transfer.js b/routes/views/clans/post/transfer.js index 82cf178a..8a8b141e 100755 --- a/routes/views/clans/post/transfer.js +++ b/routes/views/clans/post/transfer.js @@ -1,159 +1,85 @@ -let flash = {}; -const request = require('request'); -const {check, validationResult} = require('express-validator'); +const axios = require('axios'); +const error = require("../../account/post/error"); -function promiseRequest(url) { - return new Promise(function (resolve, reject) { - request(url, function (error, res, body) { - if (!error && res.statusCode < 300) { - resolve(body); - } else { - reject(error || `Unexpected status code ${res.statusCode}`); - } - }); - }); -} +exports = module.exports = function (req, res) { -exports = module.exports = async function (req, res) { + const clanId = req.body.clan_id; + const transferUsername = req.body.transfer_to; + let playerId = null; - let locals = res.locals; - locals.formData = req.body || {}; - let overallRes = res; + // If clan id or the transfer username are missing, then we can't transfer an unknown clan to an unknown clan member. + if (!transferUsername || !clanId) res.redirect('manage?flash=error&error=missingData'); + else { - // validate the input - check('transfer_to', 'Please indicate the recipient name').notEmpty(); - check('clan_id', 'Internal error while processing your query: invalid clan ID').notEmpty(); - // check the validation object for errors - let errors = validationResult(req); + // Let's check first that the player exists AND is part of this clan + axios.get(`${process.env.API_URL}/data/clan/${clanId}?include=memberships.player&fields[player]=login`) + .then(response => { - //Must have client side errors to fix - if (!errors.isEmpty()) { - flash.class = 'alert-danger'; - flash.messages = errors; - flash.type = 'Error!'; + // can't transfer clan to yourself + if (transferUsername === req.user.data.attributes.userName) res.redirect('manage?flash=error&error=transferToSelf'); - let buff = Buffer.from(JSON.stringify(flash)); - let data = buff.toString('base64'); - return overallRes.redirect('manage?flash=' + data); - } else { - - const clanId = req.body.clan_id; - const userName = req.body.transfer_to; + // Lets make an array of all members - // Let's check first that the player exists AND is part of this clan - const fetchRoute = process.env.API_URL+'/data/clan/'+clanId+'?include=memberships.player&fields[player]=login'; - - let playerId = null; - - try { - if (userName === req.user.data.attributes.userName) throw "You cannot transfer your own clan to yourself"; - - const httpData = await promiseRequest(fetchRoute); - clanData = JSON.parse(httpData); - - let members = {}; - - for (k in clanData.included){ - const record = clanData.included[k]; - if (record.type !== "player") continue; - members[record.attributes.login] = record.id; - } - - if (!members[userName]) throw "User does not exist or is not part of the clan"; - playerId = members[userName]; - } - catch(e){ - flash.class = 'alert-danger'; - flash.messages = [{msg: 'There was an error during the transfer to ' + userName + ": "+e}]; - flash.type = 'Error!'; - let buff = Buffer.from(JSON.stringify(flash)); - let data = buff.toString('base64'); + response.data.included.forEach(player => { + if (player.type === "player") { + if (player.attributes.login === transferUsername) playerId = player.id; - return overallRes.redirect('manage?flash='+data); - } - - - // Building update query - const queryUrl = - process.env.API_URL - + '/data/clan/' + clanId - ; - - const newClanObject = - { - "data": { - "type": "clan", - "id": clanId, - "relationships": { - "leader": { - "data":{ - "id": playerId, - "type": "player" } - } - } - } - }; - - //Run post to endpoint - request.patch({ - url: queryUrl, - body: JSON.stringify(newClanObject), - headers: { - 'Authorization': 'Bearer ' + req.user.data.attributes.token, - 'Content-Type': 'application/vnd.api+json' - } - }, function (err, res, body) { - - if (res.statusCode != 204) { - - let errorMessages = []; - let msg = 'Error during the ownership transfer'; - try{ - msg += ': '+JSON.stringify(JSON.parse(res.body).errors[0].detail); + }); + + }).then(() => { + +//Lets check our array for our transfer player + if (playerId === null) res.redirect('manage?flash=error&error=notClanMember'); + else { + + const newClanObject = + { + "data": { + "type": "clan", + "id": clanId, + "relationships": { + "leader": { + "data": { + "id": playerId, + "type": "player" + } + } + } + } + }; + + //Run post to endpoint / Transfer clan + axios.patch(`${process.env.API_URL}/data/clan/${clanId}`, newClanObject, + { + + headers: { + 'Authorization': `Bearer ${req.user.token}`, + 'Content-Type': 'application/vnd.api+json' + } + }).then(() => { + // Refreshing user + error.userUpdate(req, res, `../clans?flash=transfer&newLeader=${transferUsername}`); + + }).catch(e => { + + res.redirect('manage?flash=error'); + }); } - catch{} - - errorMessages.push({msg: msg}); - flash.class = 'alert-danger'; - flash.messages = errorMessages; - flash.type = 'Error!'; - - let buff = Buffer.from(JSON.stringify(flash)); - let data = buff.toString('base64'); - - return overallRes.redirect('manage?flash='+data); - } - else{ - // Refreshing user - request.get({ - url: process.env.API_URL + '/me', - headers: { - 'Authorization': 'Bearer ' + req.user.data.attributes.token, - } - }, - - function (err, res, body) { - try{ - let user = JSON.parse(body); - user.data.id = user.data.attributes.userId; - user.data.attributes.token = req.user.data.attributes.token; - req.logIn(user, function(err){ - if (err) console.error(err); - return overallRes.redirect('see?id='+clanId); - }); - } - catch{ - console.error("There was an error updating a session after a clan transfer"); - } - }); - } - }); - } -} + + + }).catch(e => { + + res.redirect('manage?flash=error'); + }); + + } + + +}; diff --git a/routes/views/clans/post/update.js b/routes/views/clans/post/update.js index 4822433f..8b99b83b 100644 --- a/routes/views/clans/post/update.js +++ b/routes/views/clans/post/update.js @@ -1,156 +1,82 @@ -let flash = {}; -let request = require('request'); +let axios = require('axios'); const {check, validationResult} = require('express-validator'); - -function promiseRequest(url) { - return new Promise(function (resolve, reject) { - request(url, function (error, res, body) { - if (!error && res.statusCode < 300) { - resolve(body); - } else { - reject(error); - } - }); - }); -} - -exports = module.exports = async function (req, res) { - - let locals = res.locals; - - locals.formData = req.body || {}; - - let overallRes = res; - - // validate the input - check('clan_tag', 'Please indicate the clan tag - No special characters and 3 characters maximum').notEmpty().isLength({max: 3}); - check('clan_description', 'Please add a description for your clan').notEmpty().isLength({max: 1000}); - check('clan_name', "Please indicate your clan's name").notEmpty().isLength({max: 64}); - check('clan_id', 'Internal error while processing your query: invalid clan ID').notEmpty(); - - // check the validation object for errors - let errors = validationResult(req); - - //Must have client side errors to fix - if (!errors.isEmpty()) { - flash.class = 'alert-danger'; - flash.messages = errors; - flash.type = 'Error!'; - - let buff = Buffer.from(JSON.stringify(flash)); - let data = buff.toString('base64'); - - return overallRes.redirect('manage?flash=' + data); - } else { - - const newName = req.body.clan_name; - const newTag = req.body.clan_tag; - const oldName = req.body.original_clan_name; - const oldTag = req.body.original_clan_tag; - const clanDescription = req.body.clan_description; - const userId = req.body.user_id; - - // Is the name taken ? - try { - let msg = null; - - flash.class = 'alert-danger'; - flash.type = 'Error!'; - - if (oldName != newName){ - const fetchRoute = process.env.API_URL+'/data/clan?filter=name=="'+encodeURIComponent(newName)+'"'; - const data = await promiseRequest(fetchRoute); - const exists = JSON.parse(data).data.length > 0; - - if (exists) msg = "This name is already taken: "+encodeURIComponent(newName); - } - if (oldTag != newTag){ - const fetchRoute = process.env.API_URL+'/data/clan?filter=tag=="'+encodeURIComponent(newTag)+'"'; - const data = await promiseRequest(fetchRoute); - const exists = JSON.parse(data).data.length > 0; - - if (exists) msg = "This tag is already taken: "+encodeURIComponent(newTag); - } - - if (msg){ - flash.messages = [{msg: msg}]; - let buff = Buffer.from(JSON.stringify(flash)); - let data = buff.toString('base64'); - return overallRes.redirect('manage?flash='+data); - } - } - catch(e){ - flash.class = 'alert-danger'; - flash.messages = [{msg: 'Error while updating the clan '+e}]; - flash.type = 'Error!'; - - let buff = Buffer.from(JSON.stringify(flash)); - let data = buff.toString('base64'); - - return overallRes.redirect('manage?flash='+data); - } - - // Building update query - const queryUrl = - process.env.API_URL - + '/data/clan/' + req.body.clan_id - ; - - const newClanObject ={ - "data": { - "type": "clan", - "id": req.body.clan_id, - "attributes": { - "description": clanDescription, - "name": newName, - "tag": newTag - } - } - }; - - - //Run post to endpoint - request.patch({ - url: queryUrl, - body: JSON.stringify(newClanObject), - headers: { - 'Authorization': 'Bearer ' + req.user.data.attributes.token, - 'Content-Type': 'application/vnd.api+json', - 'Accept': 'application/vnd.api+json' - } - }, function (err, res, body) { - - let resp; - let errorMessages = []; - - if (res.statusCode != 204) { - let msg = 'Error while updating the clan'; - try{ - - msg += ': '+JSON.stringify(JSON.parse(res.body).errors[0].detail); +const error = require("../../account/post/error"); + +exports = module.exports = [ + + // validate the input + check('clan_tag', 'Please indicate the clan tag - No special characters and 3 characters maximum').isLength({max: 3}), + check('clan_description', 'Please add a description for your clan').notEmpty().isLength({max: 1000}), + check('clan_name', "Please indicate your clan's name").isLength({max: 64}), + check('clan_id', 'Internal error while processing your query: invalid clan ID').notEmpty(), + + async (req, res) => { + // check the validation object for errors + if (!validationResult(req).isEmpty()) error.errorChecking(req, res, 'clans'); + // No errors in form, continue ahead + else { + + const newName = req.body.clan_name; + const newTag = req.body.clan_tag; + const oldName = req.body.original_clan_name; + const oldTag = req.body.original_clan_tag; + const clanDescription = req.body.clan_description; + + + // Is the clan name / clan tag taken ? + + if (oldName !== newName) { + await axios.get(`${process.env.API_URL}/data/clan?filter=name=="${newName}"`) + .then(response => { + console.log(response.data); + if (response.data.data.length >= 0) res.redirect('/manage?flash=alreadyTaken'); + }).catch(e => { + console.log(e); + res.redirect('/manage?flash=error'); + }); + + } + if (oldTag !== newTag) { + await axios.get(`${process.env.API_URL}/data/clan?filter=name=="${newTag}"`) + .then(response => { + console.log(response.data); + if (response.data.data.length >= 0) res.redirect('/manage?flash=alreadyTaken'); + }).catch(e => { + console.log(e); + res.redirect('/manage?flash=error'); + }); + } + + // Building update query + const newClanObject = { + "data": { + "type": "clan", + "id": req.body.clan_id, + "attributes": { + "description": clanDescription, + "name": newName, + "tag": newTag + } + } + }; + + + //Run post to endpoint + axios.patch(`${process.env.API_URL}/data/clan/${req.body.clan_id}`, newClanObject, + { + + headers: { + 'Authorization': `Bearer ${req.user.token}`, + 'Content-Type': 'application/vnd.api+json', + 'Accept': 'application/vnd.api+json' + } + }).then(response => { + //Yay we did it + res.redirect('/manage?flash=update'); + }).catch(e => { + console.log(e); + res.redirect('/manage?flash=error'); + }); } - catch{} - errorMessages.push({msg: msg}); - flash.class = 'alert-danger'; - flash.messages = errorMessages; - flash.type = 'Error!'; - - let buff = Buffer.from(JSON.stringify(flash)); - let data = buff.toString('base64'); - - return overallRes.redirect('manage?flash='+data); - } - - - flash = {}; - flash.class = 'alert-success'; - flash.messages = [{msg: 'You have successfully updated your clan'}]; - flash.type = 'Success!'; - - let buff = Buffer.from(JSON.stringify(flash)); - let data = buff.toString('base64'); - - return overallRes.redirect('manage?flash='+data); - }); - } -}; + } +]; diff --git a/templates/mixins/flash-messages.pug b/templates/mixins/flash-messages.pug index d515504a..45cd3edd 100644 --- a/templates/mixins/flash-messages.pug +++ b/templates/mixins/flash-messages.pug @@ -1,8 +1,7 @@ mixin flash-messages(messages) - if flash - div.alert(class=flash['class']) - ul.flash-errors - if flash.messages - if flash.messages[0] - if flash.messages[0].msg - li #{flash.type} !{flash.messages[0].msg} + if flash + div.alert(class=flash['class']) + if flash.messages + p #{flash.type} #{flash['messages']} + if flash.hasHTML + #{flash.hasHTML} diff --git a/templates/views/clans/accept_invite.pug b/templates/views/clans/accept_invite.pug index f5a7b6db..7806262f 100644 --- a/templates/views/clans/accept_invite.pug +++ b/templates/views/clans/accept_invite.pug @@ -2,18 +2,11 @@ extends ../../layouts/default include ../../mixins/flash-messages block bannerMixin block content - .containerCenter.text-center - .row - .col-md-12 - h1.account-title Accept invitation - h4.account-subtitle.text-center Click the button below to accept the invitation from #{clanLeaderName} to join #{clanName} - - .row - .col-md-offset-3.col-md-6 + .containerCenter.text-center + h2 Clan Invitation + h4 Click the button below to accept the invitation to join the clan #{clanName}. + +flash-messages(flash) - .row - .col-md-offset-3.col-md-6 form(method='post', action=acceptURL, data-toggle="validator") - button(type='submit').btn.btn-default.btn-lg.btn-outro.btn-danger Join #{clanName} - + button(type='submit') Join #{clanName} diff --git a/templates/views/clans/create.pug b/templates/views/clans/create.pug index 78d8abee..0f1cfd60 100644 --- a/templates/views/clans/create.pug +++ b/templates/views/clans/create.pug @@ -4,45 +4,39 @@ include ../../mixins/form/account block bannerMixin block content - .containerCenter.text-center - .row - .col-md-12 + .containerCenter.text-center + h1.account-title Create a clan div - p You can create your own clan, and then invite other players to join it. - p Be sure to - a(href='/rules') review the rules - | before naming your clan! - p Offensive clan names will result in an immediate sanction - - .row - .col-md-offset-3.col-md-6 - +flash-messages(flash) - form(method='post', action="/clans/create", data-toggle="validator") - input(type='hidden', name="user_id", value=userId) + p You can create your own clan, and then invite other players to join it. + p Be sure to + a(href='/rules') review the rules + | before naming your clan! + p Offensive clan names will result in an immediate sanction + +flash-messages(flash) + form(method='post', action="/clans/create", data-toggle="validator") + input(type='hidden', name="user_id", value=userId) - .clanManagement - .column12 - div.clanManagementItem - label Name - input(type='text', name='clan_name', value=clan_name, placeholder='Clan name').form-control - input(type='hidden', name='original_clan_name', value=clan_name) - span(aria-hidden='true').glyphicon.form-control-feedback + .clanManagement + .column12 + .clanManagementItem + label Name + input(type='text', required='required', name='clan_name', value=clan_name, placeholder='Clan name').form-control + input(type='hidden', name='original_clan_name', value=clan_name) + span(aria-hidden='true').glyphicon.form-control-feedback - div.clanManagementItem - label Tag: + .clanManagementItem + label Tag: - input(type='text', required='required', name='clan_tag', value=clan_tag, placeholder='TAG', style="display:inline;margin-left:5px;margin-right:5px;width:5em;").form-control - input(type='hidden', name='original_clan_tag', value=clan_tag) + input(type='text', required='required', name='clan_tag', value=clan_tag, placeholder='TAG', style="display:inline;margin-left:5px;margin-right:5px;width:5em;").form-control + input(type='hidden', name='original_clan_tag', value=clan_tag) - span(aria-hidden='true').glyphicon.form-control-feedback - br - div.clanManagementItem - label Clan description - br - br - textarea(rows='12', name='clan_description', required='required', placeholder='The description players will see when they look your clan').form-control #{clan_description} - span(aria-hidden='true').glyphicon.form-control-feedback + span(aria-hidden='true') + br + .clanManagementItem + label Clan description + br - .form-actions - button(type='submit').bigButton Create your Clan + textarea(rows='12', name='clan_description', required='required', placeholder='The description players will see when they look your clan').form-control #{clan_description} + span(aria-hidden='true') + button(type='submit') Create your Clan diff --git a/templates/views/clans/getClan.pug b/templates/views/clans/getClan.pug new file mode 100644 index 00000000..8b8e516c --- /dev/null +++ b/templates/views/clans/getClan.pug @@ -0,0 +1,24 @@ +extends ../../layouts/default +block bannerMixin + +block content + // Most of this page is generated through its js file, this pug file is just used to put the ids in place + .renderClan + .renderClanContainer.column12 + h2 #{clanTag} + h1 #{clanName} + p #{clanDescription} + p Founded on #{clanCreation} + h1 Led by #{clanLeaderName} 👑 + + if leaveButton + form(method='post', action="/clans/leave", onsubmit="return confirm('You will not be able to return in that clan unless invited again. Press OK to confim.');") + .formStart + input(type='hidden', name='clan_id', value=clan_id) + input(type='hidden', name='membership_id', value=my_membership) + button(type='submit') Leave my clan + h2 Clan Members + + ul.renderClanSubGrid + each member in clanMembers + li #{member} diff --git a/templates/views/clans/manage.pug b/templates/views/clans/manage.pug index b277f998..dc58f3d6 100644 --- a/templates/views/clans/manage.pug +++ b/templates/views/clans/manage.pug @@ -3,129 +3,116 @@ include ../../mixins/flash-messages include ../../mixins/form/account block bannerMixin block content - - .containerCenter - .row - .col-md-12 - h1.account-title Clan Management - br - .row - .col-md-offset-3.col-md-6 - +flash-messages(flash) - - .row.important-form - .col-md-6 - h2 Invite players - form(method='post',action="/clans/invite") - p This will generate an invitation link for the player of your choice - p Be sure to type the player name correctly! - - .row.inline-panel - input(type='hidden', name='clan_id', value=clan_id) - input(type='text', name='invited_player', placeholder='Player name', style="margin-left:5px;margin-right:5px").form-control - button(type='submit' onclick="copyTextButton()") Invite - - - br - h2 Clan Settings - .col-md-6 - form(method='post',action="/clans/update",data-toggle="validator") - input(type='hidden', name='clan_id', value=clan_id) - .clanManagement - .column12 - div.clanManagementItem - label Name - input(type='text', name='clan_name', value=clan_name, placeholder='Clan name').form-control - input(type='hidden', name='original_clan_name', value=clan_name) - span(aria-hidden='true').glyphicon.form-control-feedback - - div.clanManagementItem - label Tag: - - input(type='text', required='required', name='clan_tag', value=clan_tag, placeholder='TAG', style="display:inline;margin-left:5px;margin-right:5px;width:5em;").form-control - input(type='hidden', name='original_clan_tag', value=clan_tag) - - span(aria-hidden='true').glyphicon.form-control-feedback - br - div.clanManagementItem - label Clan description - br - br - textarea(rows='12', name='clan_description', required='required', placeholder='The description players will see when they look your clan').form-control #{clan_description} - span(aria-hidden='true').glyphicon.form-control-feedback - - .form-actions - button(type='submit').bigButton Update Clan Settings - - .clanManagement - .column12 - .clanManagementTable(style="overflow:auto;") - table.table.table-striped.table-hover - thead - tr - th.text-center Player - th.text-center Joined - th.text-center Kick member - tbody - each member in clan_members - tr(class= member.id == me ? "leader me" : "") - td - if member.id == me - abbr(title="Leader") 👑 - | #{member.name} - td #{member.joinedAt} - td - if member.id != me - form(method='post',action="/clans/kick") - input(type='hidden', name="membership_id", value=member.membershipId) - input(type='hidden', name='clan_id', value=clan_id) - button(type='submit').btn Kick - else - span - - .clanManagement - .clanManagementDanger.column12 - h1.danger DANGER ZONE - p The settings below CANNOT be undone. Do not touch these settings unless you are sure about what you are doing. - - form(method='post',action="/clans/transfer",data-toggle="validator", onsubmit="return confirm('ALL YOUR RIGHTS OVER THE CLAN WILL BE LOST. Press OK to confirm the clan transfer');") - input(type='hidden', name='clan_id', value=clan_id) - h2 Transfer ownership - p.text-left This operation will transfer the leadership of your clan to a new member. - br - p.text-left After the Leadership transfer, - ul.text-left - li You can no longer update the clan - br - li You can no longer delete the clan - br - li You can no longer invite new players - br - li You can no longer kick a player - br - br - p.danger By clicking the "transfer" button, you - b FORFEIT - | all your rights over this clan in favor of the new owner. - - .row.centered-flex - input(type='text', id="ownership_transfer_textbox", name='transfer_to', placeholder='Member name', style="margin-left:5px;margin-right:5px").form-control - button(type='submit').btn-danger.btn Transfer clan ownership - - form(method='post',action="/clans/destroy", onsubmit="return confirm('THIS OPERATION IS DEFINITIVE. Press OK to confirm you want to delete your clan');") - input(type='hidden', name='clan_id', value=clan_id) - br - br - h2 Delete the clan - p.text-left All memberships will be terminated and the clan will be removed. The name and tag of the clan will become free. - p.danger This operation - b CANNOT BE CANCELED - - - .row.centered-flex - button(type='submit').btn-danger.btn Delete my clan - br - br - - - + + .clanManagementMain + .clanManagementContainer.column12 + // Display non-html flash message + if flash.messages + +flash-messages(flash) + + + + + + .clanManagementContainer.column12 + h1 Clan Management + h2 Invite players + form(method='post',action="/clans/invite") + p This will generate an invitation link for the player of your choice + p Be sure to type the player name correctly! + input(type='hidden', name='clan_id', value=clan_id) + input(type='text', name='invited_player', placeholder='Player name', required='required') + br + button(type='submit' onclick="copyTextButton()") Invite Player + // We use HTML for the invite button + if flash.hasHTML + br + h2 Invitation link + .displayNone + #{flash.hasHTML} + + + button(onclick="copyInviteLink()") Copy invite link to Clipboard + p Note: It only works for the user you typed. + + + .clanManagementContainer.column12 + h2 Clan Settings + form(method='post',action="/clans/update",data-toggle="validator") + input(type='hidden', name='clan_id', value=clan_id) + label Clan Name + input(type='text', name='clan_name', value=clan_name, placeholder='Clan name', required='required') + input(type='hidden', name='original_clan_name', value=clan_name) + span(aria-hidden='true') + label Clan Tag: + input(type='text', required='required', name='clan_tag', value=clan_tag, placeholder='TAG') + input(type='hidden', name='original_clan_tag', value=clan_tag) + span(aria-hidden='true') + br + label Clan description + br + + textarea(rows='12', name='clan_description', required='required', placeholder='The description players will see when they look your clan') #{clan_description} + span(aria-hidden='true') + br + + button(type='submit') Update Clan Settings + + .clanManagementContainer.column12 + .clanMembers + each member in clan_members + .column2 + p #{member.name} + .column1 + if member.id != me + form(method='post',action="/clans/kick") + input(type='hidden', name="membership_name", value=member.name) + input(type='hidden', name="membership_id", value=member.membershipId) + input(type='hidden', name='clan_id', value=clan_id) + button(type='submit') Kick + else + span + .clanManagementMain + .clanManagementDanger.clanManagementContainer.column12 + h1 DANGER ZONE + p The settings below CANNOT be undone. Do not touch these settings unless you are sure about what you are doing. + form(method='post',action="/clans/transfer",data-toggle="validator", onsubmit="return confirm('ALL YOUR RIGHTS OVER THE CLAN WILL BE LOST. Press OK to confirm the clan transfer');") + input(type='hidden', name='clan_id', value=clan_id) + h2 Transfer ownership + p This operation will transfer the leadership of your clan to a new member. + br + h2 After the Leadership transfer: + li You can no longer update the clan + + li You can no longer delete the clan + + li You can no longer invite new players + + li You can no longer kick a player + br + br + p By clicking the "transfer" button, you + b FORFEIT + | all your rights over this clan in favor of the new owner. + + + input(type='text', required='required', id="ownership_transfer_textbox", name='transfer_to', placeholder='New clan owner') + br + button(type='submit') Transfer clan ownership + + form(method='post',action="/clans/destroy", onsubmit="return confirm('THIS OPERATION IS DEFINITIVE. Press OK to confirm you want to delete your clan');") + input(type='hidden', name='clan_id', value=clan_id) + br + br + h2 Delete the clan + p All memberships will be terminated and the clan will be removed. The name and tag of the clan will become free. + p This operation + b CANNOT BE CANCELED + br + button(type='submit') Delete my clan + br + br + // This script makes it so people can copy the invite link with just a click + script. + function copyInviteLink() { + navigator.clipboard.writeText(document.getElementById("inviteLink").getAttribute("href"))} diff --git a/templates/views/clans/seeClan.pug b/templates/views/clans/seeClan.pug deleted file mode 100644 index cd8c3d08..00000000 --- a/templates/views/clans/seeClan.pug +++ /dev/null @@ -1,27 +0,0 @@ -extends ../../layouts/default -block bannerMixin - -block content - // Most of this page is generated through its js file, this pug file is just used to put the ids in place - .renderClan - .renderClanContainer.column12 - h2#clanTag - h1#clanName - p#clanDescription - p#clanCreation - h1#clanLeader - - - #iAmMember - form(method='post', action="/clans/leave", onsubmit="return confirm('You will not be able to return in that clan unless invited again. Press OK to confim.');") - input(type='hidden', name='clan_id', value=clan_id) - input(type='hidden', name='membership_id', value=my_membership) - h2.row.centered-flex - button(type='submit').danger.btn.btn-lg Leave my clan - p Clan Members - - ul.renderClanSubGrid#clanMembers - - -block js - script( src="../../js/app/getClans.js")