diff --git a/public/styles/site/clans.sass b/public/styles/site/clans.sass index 864455c2..03fcbb74 100644 --- a/public/styles/site/clans.sass +++ b/public/styles/site/clans.sass @@ -69,6 +69,7 @@ .action-link color: #ec9d36 + margin-right: 2em &:link color: #ec9d36 &:visited diff --git a/src/backend/routes/views/clanRouter.js b/src/backend/routes/views/clanRouter.js index 18aeac1e..306b67ba 100644 --- a/src/backend/routes/views/clanRouter.js +++ b/src/backend/routes/views/clanRouter.js @@ -33,6 +33,11 @@ router.get( middlewares.isAuthenticated(), require('./clans/kick') ) +router.get( + '/transfer/:userId', + middlewares.isAuthenticated(), + require('./clans/transfer') +) router.get('/leave', middlewares.isAuthenticated(), leave) router.post('/leave', middlewares.isAuthenticated(), leave) router.get('/join', middlewares.isAuthenticated(), require('./clans/join')) diff --git a/src/backend/routes/views/clans/post/transfer.js b/src/backend/routes/views/clans/post/transfer.js deleted file mode 100755 index 59ff29cb..00000000 --- a/src/backend/routes/views/clans/post/transfer.js +++ /dev/null @@ -1,187 +0,0 @@ -const flash = {} -const 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 || `Unexpected status code ${res.statusCode}`) - } - }) - }) -} - -exports = module.exports = async function (req, res) { - const locals = res.locals - - locals.formData = req.body || {} - - const overallRes = res - - // 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 - const errors = validationResult(req) - - // Must have client side errors to fix - if (!errors.isEmpty()) { - flash.class = 'alert-danger' - flash.messages = errors - flash.type = 'Error!' - - const buff = Buffer.from(JSON.stringify(flash)) - const data = buff.toString('base64') - - return overallRes.redirect('manage?flash=' + data) - } else { - const clanId = req.body.clan_id - const userName = req.body.transfer_to - - // 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 new Error('You cannot transfer your own clan to yourself') - - const httpData = await promiseRequest(fetchRoute) - const clanData = JSON.parse(httpData) - - const members = {} - - for (const k in clanData.included) { - const record = clanData.included[k] - if (record.type !== 'player') continue - members[record.attributes.login] = record.id - } - - if (!members[userName]) - throw new Error( - '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!' - - const buff = Buffer.from(JSON.stringify(flash)) - const data = buff.toString('base64') - - 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 (err || res.statusCode !== 204) { - const errorMessages = [] - let msg = 'Error during the ownership transfer' - try { - msg += - ': ' + - JSON.stringify( - JSON.parse(res.body).errors[0].detail - ) - } catch {} - - errorMessages.push({ msg }) - flash.class = 'alert-danger' - flash.messages = errorMessages - flash.type = 'Error!' - - const buff = Buffer.from(JSON.stringify(flash)) - const 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) { - if (err) { - console.error( - 'There was an error updating a session after a clan transfer:', - err - ) - - return - } - try { - const 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' - ) - } - } - ) - } - } - ) - } -} diff --git a/src/backend/routes/views/clans/transfer.js b/src/backend/routes/views/clans/transfer.js new file mode 100755 index 00000000..c66ed43c --- /dev/null +++ b/src/backend/routes/views/clans/transfer.js @@ -0,0 +1,35 @@ +const { JavaApiError } = require('../../../services/ApiErrors') + +exports = module.exports = [ + async (req, res) => { + if (!req.requestContainer.get('UserService').getUser()?.clan?.id) { + await req.asyncFlash('error', "You don't own a clan") + + return res.redirect('/clans') + } + + const newOwnerId = parseInt(req.params.userId) + try { + await req.requestContainer + .get('ClanManagementService') + .transferOwnership(newOwnerId) + await req.asyncFlash('info', 'Clan ownership transferred') + + return res.redirect( + '/clans/view/' + + req.requestContainer.get('UserService').getUser().clan.id + ) + } catch (e) { + let message = e.toString() + if (e instanceof JavaApiError && e.error?.errors) { + message = e.error.errors[0].detail + } + + await req.asyncFlash('error', message) + return res.redirect( + '/clans/view/' + + req.requestContainer.get('UserService').getUser().clan.id + ) + } + }, +] diff --git a/src/backend/services/ClanManagementRepository.js b/src/backend/services/ClanManagementRepository.js index b581bad0..659551c1 100644 --- a/src/backend/services/ClanManagementRepository.js +++ b/src/backend/services/ClanManagementRepository.js @@ -86,6 +86,50 @@ class ClanManagementRepository { } } + async transferOwnership(newOwnerId, clanId) { + try { + const transferRequestBody = { + data: { + type: 'clan', + id: clanId, + relationships: { + leader: { + data: { + id: newOwnerId, + type: 'player', + }, + }, + }, + }, + } + + const response = await this.javaApiClient.patch( + '/data/clan/' + clanId, + JSON.stringify(transferRequestBody), + { + headers: { + 'Content-Type': 'application/vnd.api+json', + Accept: 'application/vnd.api+json', + }, + } + ) + + if (response.status !== 204) { + throw new JavaApiError( + response.status, + response.config.url, + JSON.parse(response.data) || [] + ) + } + } catch (e) { + if (e instanceof JavaApiError) { + throw e + } + + throw new GenericJavaApiError(e.toString()) + } + } + async createInvite(clanId, playerId) { try { const response = await this.javaApiClient.get( diff --git a/src/backend/services/ClanManagementService.js b/src/backend/services/ClanManagementService.js index ffcc1f55..862260ce 100644 --- a/src/backend/services/ClanManagementService.js +++ b/src/backend/services/ClanManagementService.js @@ -36,6 +36,22 @@ class ClanManagementService { } } + async transferOwnership(newOwnerId) { + await this.clanManagementRepository.transferOwnership( + newOwnerId, + this.userService.getUser().clan.id + ) + try { + this.clanService + .getAll(true) + .then(() => {}) + .catch((e) => console.error(e.stack)) + await this.userService.refreshUser() + } catch (e) { + console.error(e.stack) + } + } + async deleteClan() { const clanId = parseInt(this.userService.getUser()?.clan.id) if (!clanId) { diff --git a/src/backend/templates/views/clans/clan.pug b/src/backend/templates/views/clans/clan.pug index eadd3448..23c7da88 100644 --- a/src/backend/templates/views/clans/clan.pug +++ b/src/backend/templates/views/clans/clan.pug @@ -54,8 +54,13 @@ block content td if isLeader && member.membershipId !== userMembershipId a.action-link( - href='/clans/kick/' + member.membershipId, + href=`/clans/kick/${member.membershipId}`, onclick='return confirm(\'Kick?\')' ) Kick + a.action-link( + href=`/clans/transfer/${member.id}`, + onclick='return confirm(\'Transfer Clan Ownership?\')' + ) Make Leader + block js script(src=webpackAssetJS('clan'))