mirror of
https://github.com/roleypoly/roleypoly-v1.git
synced 2025-04-24 19:59:12 +00:00
feat(api/servers): re-add transactionally based role edits
This commit is contained in:
parent
b91aaa9c43
commit
690671317f
2 changed files with 56 additions and 22 deletions
|
@ -22,7 +22,7 @@ module.exports = (R, $) => {
|
||||||
await next()
|
await next()
|
||||||
})
|
})
|
||||||
|
|
||||||
R.get('/api/server/:id', async ctx => {
|
R.get('/api/server/:id', async (ctx) => {
|
||||||
const { userId } = ctx.session
|
const { userId } = ctx.session
|
||||||
const { id } = ctx.params
|
const { id } = ctx.params
|
||||||
|
|
||||||
|
@ -46,7 +46,7 @@ module.exports = (R, $) => {
|
||||||
ctx.body = server
|
ctx.body = server
|
||||||
})
|
})
|
||||||
|
|
||||||
R.get('/api/server/:id/slug', async ctx => {
|
R.get('/api/server/:id/slug', async (ctx) => {
|
||||||
const { id } = ctx.params
|
const { id } = ctx.params
|
||||||
|
|
||||||
const srv = await $.discord.getServer(id)
|
const srv = await $.discord.getServer(id)
|
||||||
|
@ -60,7 +60,7 @@ module.exports = (R, $) => {
|
||||||
ctx.body = srv
|
ctx.body = srv
|
||||||
})
|
})
|
||||||
|
|
||||||
R.patch('/api/server/:id', async ctx => {
|
R.patch('/api/server/:id', async (ctx) => {
|
||||||
const { userId } = ctx.session
|
const { userId } = ctx.session
|
||||||
const { id } = ctx.params
|
const { id } = ctx.params
|
||||||
|
|
||||||
|
@ -89,20 +89,19 @@ module.exports = (R, $) => {
|
||||||
...(categories != null ? { categories } : {}),
|
...(categories != null ? { categories } : {}),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
ctx.body = { ok: true }
|
ctx.body = { ok: true }
|
||||||
|
|
||||||
$.P.invalidate(id)
|
$.P.invalidate(id)
|
||||||
$.discord.invalidate(id)
|
$.discord.invalidate(id)
|
||||||
})
|
})
|
||||||
|
|
||||||
R.get('/api/admin/servers', async ctx => {
|
R.get('/api/admin/servers', async (ctx) => {
|
||||||
const { userId } = ctx.session
|
const { userId } = ctx.session
|
||||||
if (!$.discord.isRoot(userId)) {
|
if (!$.discord.isRoot(userId)) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.body = $.discord.client.guilds.map(g => ({
|
ctx.body = $.discord.client.guilds.map((g) => ({
|
||||||
url: `${process.env.APP_URL}/s/${g.id}`,
|
url: `${process.env.APP_URL}/s/${g.id}`,
|
||||||
name: g.name,
|
name: g.name,
|
||||||
members: g.members.array().length,
|
members: g.members.array().length,
|
||||||
|
@ -110,7 +109,7 @@ module.exports = (R, $) => {
|
||||||
}))
|
}))
|
||||||
})
|
})
|
||||||
|
|
||||||
R.patch('/api/servers/:server/roles', async ctx => {
|
R.patch('/api/servers/:server/roles', async (ctx) => {
|
||||||
const { userId } = ctx.session
|
const { userId } = ctx.session
|
||||||
const { server } = ctx.params
|
const { server } = ctx.params
|
||||||
const { added, removed } = ctx.request.body
|
const { added, removed } = ctx.request.body
|
||||||
|
@ -126,21 +125,28 @@ module.exports = (R, $) => {
|
||||||
|
|
||||||
// current roles and allowed roles are an inclusive set.
|
// current roles and allowed roles are an inclusive set.
|
||||||
// first, filter added and removed.
|
// first, filter added and removed.
|
||||||
const sanitizedAdded = added.filter(role => allowedRoles.includes(role))
|
const sanitizedAdded = added.filter((role) => allowedRoles.includes(role))
|
||||||
const sanitizedRemoved = removed.filter(role => allowedRoles.includes(role))
|
const sanitizedRemoved = removed.filter((role) => allowedRoles.includes(role))
|
||||||
|
|
||||||
// filter currentRoles by what's been removed (down is faster than up)
|
// filter currentRoles by what's been removed (down is faster than up)
|
||||||
let newRoles = currentRoles.filter(role => !sanitizedRemoved.includes(role))
|
let newRoles = currentRoles.filter((role) => !sanitizedRemoved.includes(role))
|
||||||
|
|
||||||
// last, add new roles
|
// last, add new roles
|
||||||
newRoles = [...newRoles, ...sanitizedAdded]
|
newRoles = [...newRoles, ...sanitizedAdded]
|
||||||
|
|
||||||
if (!arrayMatches(currentRoles, newRoles)) {
|
if (!arrayMatches(currentRoles, newRoles)) {
|
||||||
await $.discord.updateRoles(gm, newRoles)
|
if (process.env.FF_TransactionalRoles !== '0') {
|
||||||
|
await $.discord.updateRolesTx(gm, {
|
||||||
|
added: sanitizedAdded,
|
||||||
|
removed: sanitizedRemoved,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
await $.discord.updateRoles(gm, newRoles)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.body = { ok: true }
|
ctx.body = { ok: true }
|
||||||
|
|
||||||
$.P.invalidate(userId)
|
$.P.invalidate(userId)
|
||||||
$.discord.invalidate(userId)
|
$.discord.invalidate(userId)
|
||||||
})
|
})
|
||||||
|
@ -160,4 +166,4 @@ const arrayMatches = (a, b) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
const Service = require('./Service')
|
const Service = require('./Service')
|
||||||
const superagent = require('superagent')
|
const superagent = require('superagent')
|
||||||
const { DiscordClient, Member } = require('@roleypoly/rpc/discord')
|
const { DiscordClient, Member, RoleTransaction, TxDelta } = require('@roleypoly/rpc/discord')
|
||||||
const { IDQuery, DiscordUser } = require('@roleypoly/rpc/shared')
|
const { IDQuery, DiscordUser } = require('@roleypoly/rpc/shared')
|
||||||
const { Empty } = require('google-protobuf/google/protobuf/empty_pb')
|
const { Empty } = require('google-protobuf/google/protobuf/empty_pb')
|
||||||
const { NodeHttpTransport } = require('@improbable-eng/grpc-web-node-http-transport')
|
const { NodeHttpTransport } = require('@improbable-eng/grpc-web-node-http-transport')
|
||||||
|
@ -34,7 +34,7 @@ class DiscordService extends Service {
|
||||||
this.bootstrapRetries = 0
|
this.bootstrapRetries = 0
|
||||||
this.bootstrapRetriesMax = 10
|
this.bootstrapRetriesMax = 10
|
||||||
|
|
||||||
this.bootstrap().catch(e => {
|
this.bootstrap().catch((e) => {
|
||||||
console.error(`bootstrap failure`, e)
|
console.error(`bootstrap failure`, e)
|
||||||
process.exit(-1)
|
process.exit(-1)
|
||||||
})
|
})
|
||||||
|
@ -44,7 +44,7 @@ class DiscordService extends Service {
|
||||||
try {
|
try {
|
||||||
const ownUser = await this.rpc.ownUser(new Empty(), this.sharedHeaders)
|
const ownUser = await this.rpc.ownUser(new Empty(), this.sharedHeaders)
|
||||||
this.ownUser = ownUser.toObject()
|
this.ownUser = ownUser.toObject()
|
||||||
|
|
||||||
const listGuilds = await this.rpc.listGuilds(new Empty(), this.sharedHeaders)
|
const listGuilds = await this.rpc.listGuilds(new Empty(), this.sharedHeaders)
|
||||||
this.syncGuilds(listGuilds.toObject().guildsList)
|
this.syncGuilds(listGuilds.toObject().guildsList)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -112,7 +112,7 @@ class DiscordService extends Service {
|
||||||
q.setGuildid(serverId)
|
q.setGuildid(serverId)
|
||||||
|
|
||||||
const roles = await this.rpc.getGuildRoles(q, this.sharedHeaders)
|
const roles = await this.rpc.getGuildRoles(q, this.sharedHeaders)
|
||||||
return roles.toObject().rolesList.filter(role => role.id !== serverId)
|
return roles.toObject().rolesList.filter((role) => role.id !== serverId)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -124,11 +124,11 @@ class DiscordService extends Service {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const matchFor = permissionInt =>
|
const matchFor = (permissionInt) =>
|
||||||
!!gm.rolesList
|
!!gm.rolesList
|
||||||
.map(id => guildRoles.find(role => role.id === id))
|
.map((id) => guildRoles.find((role) => role.id === id))
|
||||||
.filter(x => !!x)
|
.filter((x) => !!x)
|
||||||
.find(role => (role.permissions & permissionInt) === permissionInt)
|
.find((role) => (role.permissions & permissionInt) === permissionInt)
|
||||||
|
|
||||||
const isAdmin = guild.ownerid === gm.user.id || matchFor(0x00000008)
|
const isAdmin = guild.ownerid === gm.user.id || matchFor(0x00000008)
|
||||||
const canManageRoles = isAdmin || matchFor(0x10000000)
|
const canManageRoles = isAdmin || matchFor(0x10000000)
|
||||||
|
@ -155,6 +155,34 @@ class DiscordService extends Service {
|
||||||
await this.rpc.updateMember(member, this.sharedHeaders)
|
await this.rpc.updateMember(member, this.sharedHeaders)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async updateRolesTx(memberObj, { added, removed }) {
|
||||||
|
const roleTx = new RoleTransaction()
|
||||||
|
roleTx.setMember(this.memberToQueryProto(memberObj))
|
||||||
|
|
||||||
|
for (let toAdd of added) {
|
||||||
|
const delta = new TxDelta()
|
||||||
|
delta.setAction(TxDelta.Action.ADD)
|
||||||
|
delta.setRole(toAdd)
|
||||||
|
roleTx.addDelta(delta)
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let toRemove of removed) {
|
||||||
|
const delta = new TxDelta()
|
||||||
|
delta.setAction(TxDelta.Action.REMOVE)
|
||||||
|
delta.setRole(toRemove)
|
||||||
|
roleTx.addDelta(delta)
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.rpc.updateMemberRoles(roleTx, this.sharedHeaders)
|
||||||
|
}
|
||||||
|
|
||||||
|
memberToQueryProto(member) {
|
||||||
|
const query = new IDQuery()
|
||||||
|
query.setGuildid(member.guildid)
|
||||||
|
query.setMemberid(member.user.id)
|
||||||
|
return query
|
||||||
|
}
|
||||||
|
|
||||||
memberToProto(member) {
|
memberToProto(member) {
|
||||||
const memberProto = new Member()
|
const memberProto = new Member()
|
||||||
memberProto.setGuildid(member.guildid)
|
memberProto.setGuildid(member.guildid)
|
||||||
|
@ -224,7 +252,7 @@ class DiscordService extends Service {
|
||||||
}
|
}
|
||||||
|
|
||||||
async syncGuilds(guilds) {
|
async syncGuilds(guilds) {
|
||||||
guilds.forEach(guild => this.ctx.server.ensure(guild))
|
guilds.forEach((guild) => this.ctx.server.ensure(guild))
|
||||||
}
|
}
|
||||||
|
|
||||||
async cacheCurry(key, func) {
|
async cacheCurry(key, func) {
|
||||||
|
|
Loading…
Add table
Reference in a new issue