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 = ({
)}
-
+
)}