diff --git a/apps/core-server/modules/chron.ts b/apps/core-server/modules/chron.ts index 6e8e5f37..48692a50 100644 --- a/apps/core-server/modules/chron.ts +++ b/apps/core-server/modules/chron.ts @@ -1,6 +1,7 @@ import { DISCORD_ROLES, MissionLog, NotificationPayload, prisma } from "@repo/db"; import { io } from "index"; import cron from "node-cron"; +import { setUserStandardNamePermissions } from "routes/helper"; import { changeMemberRoles } from "routes/member"; const removeMission = async (id: number, reason: string) => { @@ -141,21 +142,12 @@ const removeConnectedAircrafts = async () => { }); }; const removePermissionsForBannedUsers = async () => { - const activePenalties = await prisma.penalty.findMany({ + const removePermissionsPenaltys = await prisma.penalty.findMany({ where: { - OR: [ - { - type: "BAN", - suspended: false, - }, - { - type: "TIME_BAN", - suspended: false, - until: { - gt: new Date().toISOString(), - }, - }, - ], + removePermissionApplied: false, + User: { + DiscordAccount: { isNot: null }, + }, }, include: { User: { @@ -167,16 +159,33 @@ const removePermissionsForBannedUsers = async () => { }, }); - for (const penalty of activePenalties) { - const user = penalty.User; + const addPermissionsPenaltys = await prisma.penalty.findMany({ + where: { + addPermissionApplied: false, + User: { + DiscordAccount: { isNot: null }, + }, + OR: [{ suspended: true }, { until: { lt: new Date().toISOString() } }], + }, + include: { + User: { + include: { + DiscordAccount: true, + FormerDiscordAccounts: true, + }, + }, + }, + }); - if (user.DiscordAccount) { - await changeMemberRoles( - user.DiscordAccount.discordId, - [DISCORD_ROLES.PILOT, DISCORD_ROLES.DISPATCHER], - "remove", - ); - } + for (const penalty of removePermissionsPenaltys) { + const user = penalty.User; + console.log(`Removing roles for user ${user.id} due to penalty ${penalty.id}`); + + await changeMemberRoles( + user.DiscordAccount!.discordId, + [DISCORD_ROLES.PILOT, DISCORD_ROLES.DISPATCHER], + "remove", + ); for (const formerAccount of user.FormerDiscordAccounts) { await changeMemberRoles( @@ -185,15 +194,29 @@ const removePermissionsForBannedUsers = async () => { "remove", ); } + await prisma.penalty.update({ + where: { id: penalty.id }, + data: { removePermissionApplied: true }, + }); + } + for (const penalty of addPermissionsPenaltys) { + console.log(`Restoring roles for user ${penalty.userId} due to penalty ${penalty.id}`); + await setUserStandardNamePermissions({ + memberId: penalty.User.DiscordAccount!.discordId, + userId: penalty.userId, + }); + await prisma.penalty.update({ + where: { id: penalty.id }, + data: { addPermissionApplied: true }, + }); } }; -cron.schedule("*/5 * * * *", async () => { - await removePermissionsForBannedUsers(); -}); +removePermissionsForBannedUsers(); cron.schedule("*/1 * * * *", async () => { try { + await removePermissionsForBannedUsers(); await removeClosedMissions(); await removeConnectedAircrafts(); } catch (error) { diff --git a/apps/core-server/routes/helper.ts b/apps/core-server/routes/helper.ts index 8ab02909..b452b450 100644 --- a/apps/core-server/routes/helper.ts +++ b/apps/core-server/routes/helper.ts @@ -10,18 +10,22 @@ export const eventCompleted = (event: Event, participant?: Participant) => { return true; }; -router.post("/set-standard-name", async (req, res) => { - const { memberId, userId } = req.body; - +export const setUserStandardNamePermissions = async ({ + memberId, + userId, +}: { + memberId: string; + userId: string; +}) => { const user = await prisma.user.findUnique({ where: { id: userId, }, }); if (!user) { - res.status(404).json({ error: "User not found" }); return; } + const participant = await prisma.participant.findMany({ where: { userId: user.id, @@ -72,6 +76,13 @@ router.post("/set-standard-name", async (req, res) => { await changeMemberRoles(memberId, [DISCORD_ROLES.PILOT], isPilot ? "add" : "remove"); await changeMemberRoles(memberId, [DISCORD_ROLES.DISPATCHER], isDispatcher ? "add" : "remove"); } +}; + +router.post("/set-standard-name", async (req, res) => { + const { memberId, userId } = req.body; + + await setUserStandardNamePermissions({ memberId, userId }); + res.status(200).json({ message: "Standard name and permissions set" }); }); export default router; diff --git a/packages/database/prisma/schema/migrations/20260131211657_penalty_fields_for_chronjob_discord_roles/migration.sql b/packages/database/prisma/schema/migrations/20260131211657_penalty_fields_for_chronjob_discord_roles/migration.sql new file mode 100644 index 00000000..c8853817 --- /dev/null +++ b/packages/database/prisma/schema/migrations/20260131211657_penalty_fields_for_chronjob_discord_roles/migration.sql @@ -0,0 +1,3 @@ +-- AlterTable +ALTER TABLE "Penalty" ADD COLUMN "addPermissionApplied" BOOLEAN NOT NULL DEFAULT false, +ADD COLUMN "removePermissionApplied" BOOLEAN NOT NULL DEFAULT false; diff --git a/packages/database/prisma/schema/migrations/20260131214339_add_id_field_to_former_dc_acc/migration.sql b/packages/database/prisma/schema/migrations/20260131214339_add_id_field_to_former_dc_acc/migration.sql new file mode 100644 index 00000000..2781a065 --- /dev/null +++ b/packages/database/prisma/schema/migrations/20260131214339_add_id_field_to_former_dc_acc/migration.sql @@ -0,0 +1,13 @@ +/* + Warnings: + + - The primary key for the `FormerDiscordAccount` table will be changed. If it partially fails, the table could be left without primary key constraint. + +*/ +-- DropIndex +DROP INDEX "FormerDiscordAccount_discord_id_key"; + +-- AlterTable +ALTER TABLE "FormerDiscordAccount" DROP CONSTRAINT "FormerDiscordAccount_pkey", +ADD COLUMN "id" SERIAL NOT NULL, +ADD CONSTRAINT "FormerDiscordAccount_pkey" PRIMARY KEY ("id"); diff --git a/packages/database/prisma/schema/penalty.prisma b/packages/database/prisma/schema/penalty.prisma index 743d16c2..bcd666a2 100644 --- a/packages/database/prisma/schema/penalty.prisma +++ b/packages/database/prisma/schema/penalty.prisma @@ -10,6 +10,10 @@ model Penalty { suspended Boolean @default(false) + // For Chronjob to know if permissions were already applied/removed + removePermissionApplied Boolean @default(false) + addPermissionApplied Boolean @default(false) + timestamp DateTime @default(now()) // relations: diff --git a/packages/database/prisma/schema/user.prisma b/packages/database/prisma/schema/user.prisma index 45cf2206..37058527 100644 --- a/packages/database/prisma/schema/user.prisma +++ b/packages/database/prisma/schema/user.prisma @@ -94,14 +94,13 @@ model User { } model FormerDiscordAccount { - discordId String @unique @map(name: "discord_id") + id Int @id @default(autoincrement()) + discordId String @map(name: "discord_id") userId String @map(name: "user_id") removedAt DateTime @default(now()) @map(name: "removed_at") DiscordAccount DiscordAccount? @relation(fields: [discordId], references: [discordId], onDelete: SetNull) User User @relation(fields: [userId], references: [id], onDelete: Cascade) - - @@id([discordId, userId]) } model DiscordAccount { @@ -119,9 +118,9 @@ model DiscordAccount { updatedAt DateTime @default(now()) @map(name: "updated_at") // Related User - userId String? @unique - User User? @relation(fields: [userId], references: [id], onDelete: SetNull) - formerDiscordAccount FormerDiscordAccount? + userId String? @unique + User User? @relation(fields: [userId], references: [id], onDelete: SetNull) + formerDiscordAccount FormerDiscordAccount[] @@map(name: "discord_accounts") } diff --git a/packages/shared-components/components/PenaltyDropdown.tsx b/packages/shared-components/components/PenaltyDropdown.tsx index 53f9353a..117ee03a 100644 --- a/packages/shared-components/components/PenaltyDropdown.tsx +++ b/packages/shared-components/components/PenaltyDropdown.tsx @@ -1,6 +1,7 @@ "use client"; import { ReactNode, useState } from "react"; import { cn } from "../helper/cn"; +import { Button } from "./Button"; export const PenaltyDropdown = ({ onClick, @@ -79,10 +80,10 @@ export const PenaltyDropdown = ({ )} - + )}