Skip to content

Commit

Permalink
🎉 feat: multiple Discord roles for permissions (#574)
Browse files Browse the repository at this point in the history
  • Loading branch information
casperiv0 authored Apr 3, 2022
1 parent 3518443 commit 7053322
Show file tree
Hide file tree
Showing 13 changed files with 318 additions and 168 deletions.
71 changes: 71 additions & 0 deletions packages/api/prisma/migrations/20220403071829_/migration.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
-- CreateTable
CREATE TABLE "_dispatchRoles" (
"A" TEXT NOT NULL,
"B" TEXT NOT NULL
);

-- CreateTable
CREATE TABLE "_leoSupervisorRoles" (
"A" TEXT NOT NULL,
"B" TEXT NOT NULL
);

-- CreateTable
CREATE TABLE "_taxiRoles" (
"A" TEXT NOT NULL,
"B" TEXT NOT NULL
);

-- CreateTable
CREATE TABLE "_towRoles" (
"A" TEXT NOT NULL,
"B" TEXT NOT NULL
);

-- CreateIndex
CREATE UNIQUE INDEX "_dispatchRoles_AB_unique" ON "_dispatchRoles"("A", "B");

-- CreateIndex
CREATE INDEX "_dispatchRoles_B_index" ON "_dispatchRoles"("B");

-- CreateIndex
CREATE UNIQUE INDEX "_leoSupervisorRoles_AB_unique" ON "_leoSupervisorRoles"("A", "B");

-- CreateIndex
CREATE INDEX "_leoSupervisorRoles_B_index" ON "_leoSupervisorRoles"("B");

-- CreateIndex
CREATE UNIQUE INDEX "_taxiRoles_AB_unique" ON "_taxiRoles"("A", "B");

-- CreateIndex
CREATE INDEX "_taxiRoles_B_index" ON "_taxiRoles"("B");

-- CreateIndex
CREATE UNIQUE INDEX "_towRoles_AB_unique" ON "_towRoles"("A", "B");

-- CreateIndex
CREATE INDEX "_towRoles_B_index" ON "_towRoles"("B");

-- AddForeignKey
ALTER TABLE "_dispatchRoles" ADD FOREIGN KEY ("A") REFERENCES "DiscordRole"("id") ON DELETE CASCADE ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "_dispatchRoles" ADD FOREIGN KEY ("B") REFERENCES "DiscordRoles"("id") ON DELETE CASCADE ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "_leoSupervisorRoles" ADD FOREIGN KEY ("A") REFERENCES "DiscordRole"("id") ON DELETE CASCADE ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "_leoSupervisorRoles" ADD FOREIGN KEY ("B") REFERENCES "DiscordRoles"("id") ON DELETE CASCADE ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "_taxiRoles" ADD FOREIGN KEY ("A") REFERENCES "DiscordRole"("id") ON DELETE CASCADE ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "_taxiRoles" ADD FOREIGN KEY ("B") REFERENCES "DiscordRoles"("id") ON DELETE CASCADE ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "_towRoles" ADD FOREIGN KEY ("A") REFERENCES "DiscordRole"("id") ON DELETE CASCADE ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "_towRoles" ADD FOREIGN KEY ("B") REFERENCES "DiscordRoles"("id") ON DELETE CASCADE ON UPDATE CASCADE;
36 changes: 22 additions & 14 deletions packages/api/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -107,15 +107,19 @@ model DiscordRoles {
leoRoles DiscordRole[] @relation("leoRoles")
leoSupervisorRole DiscordRole? @relation("leoSupervisorRole", fields: [leoSupervisorRoleId], references: [id])
leoSupervisorRoleId String?
leoSupervisorRoles DiscordRole[] @relation("leoSupervisorRoles")
emsFdRole DiscordRole? @relation("emsFdRole", fields: [emsFdRoleId], references: [id])
emsFdRoleId String?
emsFdRoles DiscordRole[] @relation("emsFdRoles")
dispatchRole DiscordRole? @relation("dispatchRole", fields: [dispatchRoleId], references: [id])
dispatchRoleId String?
dispatchRoles DiscordRole[] @relation("dispatchRoles")
towRole DiscordRole? @relation("towRole", fields: [towRoleId], references: [id])
towRoleId String?
towRoles DiscordRole[] @relation("towRoles")
taxiRole DiscordRole? @relation("taxiRole", fields: [taxiRoleId], references: [id])
taxiRoleId String?
taxiRoles DiscordRole[] @relation("taxiRoles")
adminRole DiscordRole? @relation("adminRole", fields: [adminRoleId], references: [id])
adminRoleId String?
whitelistedRoleId String?
Expand All @@ -126,20 +130,24 @@ model DiscordRoles {
}

model DiscordRole {
id String @id @unique
name String
discordRoles DiscordRoles @relation(fields: [discordRolesId], references: [id])
discordRolesId String
leoRoles DiscordRoles[] @relation("leoRole")
leoRolesArr DiscordRoles[] @relation("leoRoles")
emsFdRoles DiscordRoles[] @relation("emsFdRole")
emsFdRolesArr DiscordRoles[] @relation("emsFdRoles")
dispatchRoles DiscordRoles[] @relation("dispatchRole")
towRoles DiscordRoles[] @relation("towRole")
taxiRoles DiscordRoles[] @relation("taxiRole")
leoSupervisorRoles DiscordRoles[] @relation("leoSupervisorRole")
adminRoles DiscordRoles[] @relation("adminRole")
whitelistedRoles DiscordRoles[] @relation("whitelistedRole")
id String @id @unique
name String
discordRoles DiscordRoles @relation(fields: [discordRolesId], references: [id])
discordRolesId String
leoRoles DiscordRoles[] @relation("leoRole")
leoRolesArr DiscordRoles[] @relation("leoRoles")
emsFdRoles DiscordRoles[] @relation("emsFdRole")
emsFdRolesArr DiscordRoles[] @relation("emsFdRoles")
dispatchRoles DiscordRoles[] @relation("dispatchRole")
towRoles DiscordRoles[] @relation("towRole")
taxiRoles DiscordRoles[] @relation("taxiRole")
leoSupervisorRoles DiscordRoles[] @relation("leoSupervisorRole")
adminRoles DiscordRoles[] @relation("adminRole")
whitelistedRoles DiscordRoles[] @relation("whitelistedRole")
dispatchRolesArr DiscordRoles[] @relation("dispatchRoles")
leoSupervisorRolesArr DiscordRoles[] @relation("leoSupervisorRoles")
taxiRolesArr DiscordRoles[] @relation("taxiRoles")
towRolesArr DiscordRoles[] @relation("towRoles")
}

model User {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { Get, Post } from "@tsed/schema";
import { RESTGetAPIGuildRolesResult, Routes } from "discord-api-types/v10";
import { IsAuth } from "middlewares/IsAuth";
import { prisma } from "lib/prisma";
import type { cad, DiscordRole, DiscordRoles } from "@prisma/client";
import type { cad, DiscordRole } from "@prisma/client";
import { BadRequest } from "@tsed/exceptions";
import { DISCORD_SETTINGS_SCHEMA } from "@snailycad/schemas";
import { validateSchema } from "lib/validateSchema";
Expand Down Expand Up @@ -86,10 +86,6 @@ export class DiscordSettingsController {

const createUpdateData = {
guildId,
dispatchRoleId: data.dispatchRoleId ?? null,
leoSupervisorRoleId: data.leoSupervisorRoleId ?? null,
towRoleId: data.towRoleId ?? null,
taxiRoleId: data.taxiRoleId ?? null,
adminRoleId: data.adminRoleId ?? null,
whitelistedRoleId: data.whitelistedRoleId ?? null,
};
Expand All @@ -98,11 +94,54 @@ export class DiscordSettingsController {
where: { id: String(cad.discordRolesId) },
update: createUpdateData,
create: createUpdateData,
include: { leoRoles: true, emsFdRoles: true },
include: {
leoRoles: true,
emsFdRoles: true,
leoSupervisorRoles: true,
towRoles: true,
taxiRoles: true,
dispatchRoles: true,
},
});

await this.updateLeoRoles(discordRoles, (data.leoRoles as string[] | null) ?? []);
await this.updateEmsFdRoles(discordRoles, (data.emsFdRoles as string[] | null) ?? []);
await Promise.all([
this.updateRoles({
discordRoleId: discordRoles.id,
discordRoles: discordRoles.leoRoles,
newRoles: (data.leoRoles as string[] | null) ?? [],
type: "leoRoles",
}),
this.updateRoles({
discordRoleId: discordRoles.id,
discordRoles: discordRoles.emsFdRoles,
newRoles: (data.emsFdRoles as string[] | null) ?? [],
type: "emsFdRoles",
}),
this.updateRoles({
discordRoleId: discordRoles.id,
discordRoles: discordRoles.leoSupervisorRoles,
newRoles: (data.leoSupervisorRoles as string[] | null) ?? [],
type: "leoSupervisorRoles",
}),
this.updateRoles({
discordRoleId: discordRoles.id,
discordRoles: discordRoles.towRoles,
newRoles: (data.towRoles as string[] | null) ?? [],
type: "towRoles",
}),
this.updateRoles({
discordRoleId: discordRoles.id,
discordRoles: discordRoles.dispatchRoles,
newRoles: (data.dispatchRoles as string[] | null) ?? [],
type: "dispatchRoles",
}),
this.updateRoles({
discordRoleId: discordRoles.id,
discordRoles: discordRoles.taxiRoles,
newRoles: (data.taxiRoles as string[] | null) ?? [],
type: "taxiRoles",
}),
]);

const updated = await prisma.cad.update({
where: { id: cad.id },
Expand All @@ -119,35 +158,32 @@ export class DiscordSettingsController {
);
}

protected async updateLeoRoles(
discordRoles: DiscordRoles & { leoRoles: DiscordRole[] },
newRoles: string[],
) {
protected async updateRoles(options: UpdateRolesOptions) {
const disconnectConnectArr = manyToManyHelper(
discordRoles.leoRoles.map((v) => v.id),
newRoles,
options.discordRoles.map((v) => v.id),
options.newRoles,
);

await prisma.$transaction(
disconnectConnectArr.map((v) =>
prisma.discordRoles.update({ where: { id: discordRoles.id }, data: { leoRoles: v } }),
prisma.discordRoles.update({
where: { id: options.discordRoleId },
data: { [options.type]: v },
}),
),
);
}
}

protected async updateEmsFdRoles(
discordRoles: DiscordRoles & { emsFdRoles: DiscordRole[] },
newRoles: string[],
) {
const disconnectConnectArr = manyToManyHelper(
discordRoles.emsFdRoles.map((v) => v.id),
newRoles,
);

await prisma.$transaction(
disconnectConnectArr.map((v) =>
prisma.discordRoles.update({ where: { id: discordRoles.id }, data: { emsFdRoles: v } }),
),
);
}
interface UpdateRolesOptions {
discordRoleId: string;
discordRoles: DiscordRole[];
newRoles: string[];
type:
| "leoRoles"
| "emsFdRoles"
| "leoSupervisorRoles"
| "dispatchRoles"
| "towRoles"
| "taxiRoles";
}
42 changes: 27 additions & 15 deletions packages/api/src/lib/discord/admin.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Rank, User, WhitelistStatus } from "@prisma/client";
import { DiscordRole, Rank, User, WhitelistStatus } from "@prisma/client";
import { RESTGetAPIGuildMemberResult, Routes } from "discord-api-types/v10";
import { BOT_TOKEN, getRest, GUILD_ID } from "lib/discord/config";
import { prisma } from "lib/prisma";
Expand All @@ -22,7 +22,15 @@ export async function updateMemberRoles(

const discordRoles = await prisma.discordRoles.findUnique({
where: { id: String(discordRolesId) },
include: { roles: true, leoRoles: true, emsFdRoles: true },
include: {
roles: true,
leoRoles: true,
emsFdRoles: true,
dispatchRoles: true,
towRoles: true,
taxiRoles: true,
leoSupervisorRoles: true,
},
});

if (!discordRoles) return;
Expand All @@ -34,23 +42,20 @@ export async function updateMemberRoles(

if (!discordMember?.user?.id || discordMember.pending) return;

const leoRoles = discordRoles.leoRoles.map((role) => ({
roleId: role.id,
method: createMethod(user.isLeo),
}));

const emsFdRoles = discordRoles.emsFdRoles.map((role) => ({
roleId: role.id,
method: createMethod(user.isEmsFd),
}));
const leoRoles = makeRolesArr(discordRoles.leoRoles, user.isLeo);
const leoSupervisorRoles = makeRolesArr(discordRoles.leoSupervisorRoles, user.isSupervisor);
const emsFdRoles = makeRolesArr(discordRoles.emsFdRoles, user.isEmsFd);
const dispatchRoles = makeRolesArr(discordRoles.dispatchRoles, user.isDispatch);
const towRoles = makeRolesArr(discordRoles.towRoles, user.isTow);
const taxiRoles = makeRolesArr(discordRoles.taxiRoles, user.isTaxi);

const data = [
...leoRoles,
...emsFdRoles,
{ roleId: discordRoles.leoSupervisorRoleId, method: createMethod(user.isSupervisor) },
{ roleId: discordRoles.dispatchRoleId, method: createMethod(user.isDispatch) },
{ roleId: discordRoles.towRoleId, method: createMethod(user.isTow) },
{ roleId: discordRoles.taxiRoleId, method: createMethod(user.isTaxi) },
...leoSupervisorRoles,
...dispatchRoles,
...towRoles,
...taxiRoles,
{ roleId: discordRoles.adminRoleId, method: createMethod(user.rank === Rank.ADMIN) },
{
roleId: discordRoles.whitelistedRoleId,
Expand All @@ -65,6 +70,13 @@ export async function updateMemberRoles(
);
}

function makeRolesArr(roles: DiscordRole[], isTrue: boolean) {
return roles.map((role) => ({
roleId: role.id,
method: createMethod(isTrue),
}));
}

async function addOrRemoveRole(discordId: string, roleId: string | null, method: "put" | "delete") {
if (!roleId) return false;

Expand Down
Loading

0 comments on commit 7053322

Please sign in to comment.