From 70533227a8b778a0924a63803b82834bc91db177 Mon Sep 17 00:00:00 2001 From: Casper <53900565+Dev-CasperTheGhost@users.noreply.github.com> Date: Sun, 3 Apr 2022 09:59:23 +0200 Subject: [PATCH] :tada: feat: multiple Discord roles for permissions (#574) --- .../migrations/20220403071829_/migration.sql | 71 ++++++++++++++ packages/api/prisma/schema.prisma | 36 ++++--- .../discord/DiscordSettingsController.ts | 96 +++++++++++++------ packages/api/src/lib/discord/admin.ts | 42 +++++--- packages/api/src/lib/discord/auth.ts | 44 +++++---- packages/api/src/lib/prisma.ts | 6 +- packages/api/src/middlewares/IsAuth.ts | 12 ++- .../api/src/migrations/emsRoleToEmsRoles.ts | 23 ----- .../api/src/migrations/leoRoleToLeoRoles.ts | 23 ----- packages/api/src/migrations/xToXArr.ts | 50 ++++++++++ .../manage/cad-settings/DiscordRolesTab.tsx | 62 ++++++------ packages/schemas/src/admin/index.ts | 8 +- packages/types/src/index.ts | 13 +-- 13 files changed, 318 insertions(+), 168 deletions(-) create mode 100644 packages/api/prisma/migrations/20220403071829_/migration.sql delete mode 100644 packages/api/src/migrations/emsRoleToEmsRoles.ts delete mode 100644 packages/api/src/migrations/leoRoleToLeoRoles.ts create mode 100644 packages/api/src/migrations/xToXArr.ts diff --git a/packages/api/prisma/migrations/20220403071829_/migration.sql b/packages/api/prisma/migrations/20220403071829_/migration.sql new file mode 100644 index 000000000..ca353f942 --- /dev/null +++ b/packages/api/prisma/migrations/20220403071829_/migration.sql @@ -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; diff --git a/packages/api/prisma/schema.prisma b/packages/api/prisma/schema.prisma index 2f327e77e..143f07a80 100644 --- a/packages/api/prisma/schema.prisma +++ b/packages/api/prisma/schema.prisma @@ -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? @@ -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 { diff --git a/packages/api/src/controllers/admin/manage/cad-settings/discord/DiscordSettingsController.ts b/packages/api/src/controllers/admin/manage/cad-settings/discord/DiscordSettingsController.ts index b9f08d50e..35f7e124f 100644 --- a/packages/api/src/controllers/admin/manage/cad-settings/discord/DiscordSettingsController.ts +++ b/packages/api/src/controllers/admin/manage/cad-settings/discord/DiscordSettingsController.ts @@ -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"; @@ -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, }; @@ -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 }, @@ -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"; } diff --git a/packages/api/src/lib/discord/admin.ts b/packages/api/src/lib/discord/admin.ts index b6f4e5a89..d356b6720 100644 --- a/packages/api/src/lib/discord/admin.ts +++ b/packages/api/src/lib/discord/admin.ts @@ -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"; @@ -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; @@ -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, @@ -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; diff --git a/packages/api/src/lib/discord/auth.ts b/packages/api/src/lib/discord/auth.ts index 9caf739e1..35ad34eac 100644 --- a/packages/api/src/lib/discord/auth.ts +++ b/packages/api/src/lib/discord/auth.ts @@ -1,4 +1,4 @@ -import { Rank, User, WhitelistStatus } from "@prisma/client"; +import { DiscordRole, Rank, User, WhitelistStatus } from "@prisma/client"; import { Routes, type RESTGetAPIGuildMemberResult } from "discord-api-types/v10"; import { getRest, GUILD_ID } from "lib/discord/config"; import { prisma } from "lib/prisma"; @@ -14,7 +14,15 @@ export async function updateMemberRolesLogin( 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; @@ -28,23 +36,20 @@ export async function updateMemberRolesLogin( if (!discordMember?.user?.id || discordMember.pending) return; - const isLeo = hasRole( - discordRoles.leoRoles.map((v) => v.id), - discordMember.roles, - ); - - const isEmsFd = hasRole( - discordRoles.emsFdRoles.map((v) => v.id), - discordMember.roles, - ); + const isLeo = hasRoleArr(discordRoles.leoRoles, discordMember.roles); + const isEmsFd = hasRoleArr(discordRoles.emsFdRoles, discordMember.roles); + const isDispatch = hasRoleArr(discordRoles.dispatchRoles, discordMember.roles); + const isTow = hasRoleArr(discordRoles.towRoles, discordMember.roles); + const isTaxi = hasRoleArr(discordRoles.taxiRoles, discordMember.roles); + const isSupervisor = hasRoleArr(discordRoles.leoSupervisorRoles, discordMember.roles); const updateData = { isLeo: discordRoles.leoRoles.length <= 0 ? undefined : isLeo, isEmsFd: discordRoles.emsFdRoles.length <= 0 ? undefined : isEmsFd, - isSupervisor: hasRole(discordRoles.leoSupervisorRoleId, discordMember.roles), - isDispatch: hasRole(discordRoles.dispatchRoleId, discordMember.roles), - isTow: hasRole(discordRoles.towRoleId, discordMember.roles), - isTaxi: hasRole(discordRoles.taxiRoleId, discordMember.roles), + isSupervisor: discordRoles.leoSupervisorRoles.length <= 0 ? undefined : isSupervisor, + isDispatch: discordRoles.dispatchRoles.length <= 0 ? undefined : isDispatch, + isTow: discordRoles.towRoles.length <= 0 ? undefined : isTow, + isTaxi: discordRoles.taxiRoles.length <= 0 ? undefined : isTaxi, rank: user.rank !== Rank.OWNER ? hasRole(discordRoles.adminRoleId, discordMember.roles) @@ -65,10 +70,13 @@ export async function updateMemberRolesLogin( return updatedUser; } -function hasRole(roleId: string | string[] | null, roleIds: string[]) { - if (!roleId) return undefined; +function hasRoleArr(roles: DiscordRole[], roleIds: string[]) { + return roles.some((role) => roleIds.includes(role.id)); +} - return roleIds.some((v) => (typeof roleId === "string" ? v === roleId : roleId.includes(v))); +function hasRole(roleId: string | null, roleIds: string[]) { + if (!roleId) return undefined; + return roleIds.includes(roleId); } function makeWhitelistStatus(cadWhitelisted: boolean, hasRole: boolean | undefined) { diff --git a/packages/api/src/lib/prisma.ts b/packages/api/src/lib/prisma.ts index 10c397748..0087eb21d 100644 --- a/packages/api/src/lib/prisma.ts +++ b/packages/api/src/lib/prisma.ts @@ -1,8 +1,7 @@ import { PrismaClient } from "@prisma/client"; import { divisionToDivisions } from "migrations/divisionToDivisions"; import { pairedSymbolToTemplate } from "migrations/pairedSymbolToTemplate"; -import { leoRoleToLeoRoles } from "migrations/leoRoleToLeoRoles"; -import { emsRoleToEmsRoles } from "migrations/emsRoleToEmsRoles"; +import { xToXArrAll } from "migrations/xToXArr"; import { disabledFeatureToCadFeature } from "migrations/disabledFeatureToCadFeature"; export const prisma = new PrismaClient({ @@ -14,8 +13,7 @@ async function handleMigrations() { await Promise.all([ divisionToDivisions(), pairedSymbolToTemplate(), - leoRoleToLeoRoles(), - emsRoleToEmsRoles(), + xToXArrAll(), disabledFeatureToCadFeature(), ]); } diff --git a/packages/api/src/middlewares/IsAuth.ts b/packages/api/src/middlewares/IsAuth.ts index 17bfa660d..cbc310989 100644 --- a/packages/api/src/middlewares/IsAuth.ts +++ b/packages/api/src/middlewares/IsAuth.ts @@ -36,7 +36,17 @@ const CAD_SELECT = (user?: Pick) => ({ discordRolesId: true, discordRoles: user?.rank === Rank.OWNER - ? { include: { roles: true, leoRoles: true, emsFdRoles: true } } + ? { + include: { + roles: true, + leoRoles: true, + emsFdRoles: true, + dispatchRoles: true, + leoSupervisorRoles: true, + taxiRoles: true, + towRoles: true, + }, + } : true, }); diff --git a/packages/api/src/migrations/emsRoleToEmsRoles.ts b/packages/api/src/migrations/emsRoleToEmsRoles.ts deleted file mode 100644 index 32bb44910..000000000 --- a/packages/api/src/migrations/emsRoleToEmsRoles.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { prisma } from "lib/prisma"; - -export async function emsRoleToEmsRoles() { - const discordRoles = await prisma.discordRoles.findFirst({ - where: { emsFdRoleId: { not: null } }, - }); - - if (!discordRoles || !discordRoles.emsFdRoleId) return; - - await prisma.discordRoles.update({ - where: { - id: discordRoles.id, - }, - data: { - emsFdRoleId: null, - emsFdRoles: { - connect: { - id: discordRoles.emsFdRoleId, - }, - }, - }, - }); -} diff --git a/packages/api/src/migrations/leoRoleToLeoRoles.ts b/packages/api/src/migrations/leoRoleToLeoRoles.ts deleted file mode 100644 index 88efe36e8..000000000 --- a/packages/api/src/migrations/leoRoleToLeoRoles.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { prisma } from "lib/prisma"; - -export async function leoRoleToLeoRoles() { - const discordRoles = await prisma.discordRoles.findFirst({ - where: { leoRoleId: { not: null } }, - }); - - if (!discordRoles || !discordRoles.leoRoleId) return; - - await prisma.discordRoles.update({ - where: { - id: discordRoles.id, - }, - data: { - leoRoleId: null, - leoRoles: { - connect: { - id: discordRoles.leoRoleId, - }, - }, - }, - }); -} diff --git a/packages/api/src/migrations/xToXArr.ts b/packages/api/src/migrations/xToXArr.ts new file mode 100644 index 000000000..a1335e375 --- /dev/null +++ b/packages/api/src/migrations/xToXArr.ts @@ -0,0 +1,50 @@ +import { prisma } from "lib/prisma"; + +const types = { + leoRoleId: "leoRoles", + emsFdRoleId: "emsFdRoles", + dispatchRoleId: "dispatchRoles", + towRoleId: "towRoles", + taxiRoleId: "taxiRoles", + leoSupervisorRoleId: "leoSupervisorRoles", +} as const; + +type Type = + | "leoRoleId" + | "emsFdRoleId" + | "dispatchRoleId" + | "towRoleId" + | "taxiRoleId" + | "leoSupervisorRoleId"; + +async function xToXArr(type: Type) { + const discordRoles = await prisma.discordRoles.findFirst({ + where: { [type]: { not: null } }, + }); + + if (!discordRoles || !discordRoles[type]) return; + + await prisma.discordRoles.update({ + where: { + id: discordRoles.id, + }, + data: { + [type]: null, + [types[type]]: { + connect: { + id: discordRoles[type], + }, + }, + }, + }); +} + +export async function xToXArrAll() { + const typesArr = Object.keys(types) as Type[]; + + await Promise.all( + typesArr.map(async (key) => { + await xToXArr(key); + }), + ); +} diff --git a/packages/client/src/components/admin/manage/cad-settings/DiscordRolesTab.tsx b/packages/client/src/components/admin/manage/cad-settings/DiscordRolesTab.tsx index 24a059530..052fd1863 100644 --- a/packages/client/src/components/admin/manage/cad-settings/DiscordRolesTab.tsx +++ b/packages/client/src/components/admin/manage/cad-settings/DiscordRolesTab.tsx @@ -7,9 +7,17 @@ import { Formik } from "formik"; import useFetch from "lib/useFetch"; import { useTranslations } from "next-intl"; import { useAuth } from "context/AuthContext"; -import type { DiscordRoles } from "@snailycad/types"; +import type { DiscordRole, DiscordRoles } from "@snailycad/types"; import { SettingsFormField } from "components/form/SettingsFormField"; +function makeRoleValues(roles?: DiscordRole[]) { + if (!roles) return []; + return roles.map((v) => ({ + label: v.name, + value: v.id, + })); +} + export function DiscordRolesTab() { const [roles, setRoles] = React.useState([]); const { state, execute } = useFetch(); @@ -22,20 +30,12 @@ export function DiscordRolesTab() { ); const INITIAL_VALUES = { - leoRoles: - discordRoles.leoRoles?.map((v) => ({ - label: v.name, - value: v.id, - })) ?? [], - emsFdRoles: - discordRoles.emsFdRoles?.map((v) => ({ - label: v.name, - value: v.id, - })) ?? [], - dispatchRoleId: discordRoles.dispatchRoleId, - leoSupervisorRoleId: discordRoles.leoSupervisorRoleId, - towRoleId: discordRoles.towRoleId, - taxiRoleId: discordRoles.taxiRoleId, + leoRoles: makeRoleValues(discordRoles.leoRoles), + emsFdRoles: makeRoleValues(discordRoles.emsFdRoles), + dispatchRoles: makeRoleValues(discordRoles.dispatchRoles), + leoSupervisorRoles: makeRoleValues(discordRoles.leoSupervisorRoles), + towRoles: makeRoleValues(discordRoles.towRoles), + taxiRoles: makeRoleValues(discordRoles.taxiRoles), adminRoleId: discordRoles.adminRoleId, whitelistedRoleId: discordRoles.whitelistedRoleId, }; @@ -55,6 +55,10 @@ export function DiscordRolesTab() { ...values, leoRoles: values.leoRoles.map((v) => v.value), emsFdRoles: values.emsFdRoles.map((v) => v.value), + dispatchRoles: values.dispatchRoles.map((v) => v.value), + towRoles: values.towRoles.map((v) => v.value), + taxiRoles: values.taxiRoles.map((v) => v.value), + leoSupervisorRoles: values.leoSupervisorRoles.map((v) => v.value), }, }); @@ -128,17 +132,18 @@ export function DiscordRolesTab() { ({ value: role.id, label: role.name, }))} - value={values.dispatchRoleId} - name="dispatchRoleId" + value={values.dispatchRoles} + name="dispatchRoles" onChange={handleChange} /> ({ value: role.id, label: role.name, }))} - value={values.taxiRoleId} - name="taxiRoleId" + value={values.taxiRoles} + name="taxiRoles" onChange={handleChange} /> diff --git a/packages/schemas/src/admin/index.ts b/packages/schemas/src/admin/index.ts index bd16a8f9f..cbefd4127 100644 --- a/packages/schemas/src/admin/index.ts +++ b/packages/schemas/src/admin/index.ts @@ -36,10 +36,10 @@ export const CAD_MISC_SETTINGS_SCHEMA = z.object({ export const DISCORD_SETTINGS_SCHEMA = z.object({ leoRoles: z.array(z.any()).nullable().optional(), emsFdRoles: z.array(z.any()).nullable().optional(), - leoSupervisorRoleId: z.string().nullable().optional(), - dispatchRoleId: z.string().nullable().optional(), - towRoleId: z.string().nullable().optional(), - taxiRoleId: z.string().nullable().optional(), + leoSupervisorRoles: z.array(z.any()).nullable().optional(), + dispatchRoles: z.array(z.any()).nullable().optional(), + towRoles: z.array(z.any()).nullable().optional(), + taxiRoles: z.array(z.any()).nullable().optional(), adminRoleId: z.string().nullable().optional(), whitelistedRoleId: z.string().nullable().optional(), }); diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index a50b8d2be..99a033c71 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -105,15 +105,10 @@ export interface DiscordRoles { guildId: string; leoRoles?: DiscordRole[]; emsFdRoles?: DiscordRole[]; - leoSupervisorRoleId: string | null; - leoSupervisorRole: DiscordRole | null; - emsFdRole: DiscordRole | null; - dispatchRoleId: string | null; - dispatchRole: DiscordRole | null; - towRoleId: string | null; - towRole: DiscordRole | null; - taxiRoleId: string | null; - taxiRole: DiscordRole | null; + dispatchRoles?: DiscordRole[]; + leoSupervisorRoles?: DiscordRole[]; + towRoles?: DiscordRole[]; + taxiRoles?: DiscordRole[]; adminRoleId: string | null; adminRole: DiscordRole | null; whitelistedRoleId: string | null;