diff --git a/apps/dispatch-server/routes/aircraft.ts b/apps/dispatch-server/routes/aircraft.ts index 7c1522b2..4f37b90a 100644 --- a/apps/dispatch-server/routes/aircraft.ts +++ b/apps/dispatch-server/routes/aircraft.ts @@ -117,6 +117,8 @@ router.patch("/:id", async (req, res) => { router.delete("/:id", async (req, res) => { const { id } = req.params; const bann = req.body?.bann as boolean; + const reason = req.body?.reason as string; + const until = req.body?.until as Date | null; const requiredPermission = bann ? "ADMIN_USER" : "ADMIN_KICK"; @@ -146,14 +148,16 @@ router.delete("/:id", async (req, res) => { io.to(`user:${aircraft.userId}`).emit("notification", { type: "admin-message", - message: "Verbindung durch einen Administrator getrennt", + message: `Du wurdest von ${getPublicUser(req.user).publicId} ${until ? `bis zum ${new Date(until).toLocaleString()} ` : ""} ${ + status === "ban" ? "gebannt" : "gekickt" + }`, status, - data: { admin: getPublicUser(req.user) }, + data: { admin: getPublicUser(req.user), reason }, } as AdminMessage); io.in(`user:${aircraft.userId}`).disconnectSockets(true); - if (bann) { + if (bann && !until) { await prisma.user.update({ where: { id: aircraft.userId }, data: { @@ -163,6 +167,15 @@ router.delete("/:id", async (req, res) => { }, }); } + await prisma.penalty.create({ + data: { + userId: aircraft.userId, + type: bann ? (until ? "TIME_BAN" : "BAN") : "KICK", + until: until ? new Date(until) : null, + reason: reason, + createdUserId: req.user.id, + }, + }); res.status(204).send(); } catch (error) { diff --git a/apps/dispatch-server/routes/dispatcher.ts b/apps/dispatch-server/routes/dispatcher.ts index 61f85e29..15a1a897 100644 --- a/apps/dispatch-server/routes/dispatcher.ts +++ b/apps/dispatch-server/routes/dispatcher.ts @@ -34,6 +34,8 @@ import { Request, Response } from "express"; router.delete("/:id", async (req, res) => { const { id } = req.params; const bann = req.body?.bann as boolean; + const reason = req.body?.reason as string; + const until = req.body?.until as Date | null; const requiredPermission = bann ? "ADMIN_USER" : "ADMIN_KICK"; @@ -63,14 +65,16 @@ router.delete("/:id", async (req, res) => { io.to(`user:${dispatcher.userId}`).emit("notification", { type: "admin-message", - message: "Verbindung durch einen Administrator getrennt", + message: `Du wurdest von ${getPublicUser(req.user).publicId} ${until ? `bis zum ${new Date(until).toLocaleString()} ` : ""} ${ + status === "ban" ? "gebannt" : "gekickt" + }`, status, - data: { admin: getPublicUser(req.user) }, + data: { admin: getPublicUser(req.user), reason }, } as AdminMessage); io.in(`user:${dispatcher.userId}`).disconnectSockets(true); - if (bann) { + if (bann && !until) { await prisma.user.update({ where: { id: dispatcher.userId }, data: { @@ -80,6 +84,15 @@ router.delete("/:id", async (req, res) => { }, }); } + await prisma.penalty.create({ + data: { + userId: dispatcher.userId, + type: bann ? (until ? "TIME_BAN" : "BAN") : "KICK", + until: until ? new Date(until) : null, + reason: reason, + createdUserId: req.user.id, + }, + }); res.status(204).send(); } catch (error) { diff --git a/apps/dispatch/app/_components/Error.tsx b/apps/dispatch/app/_components/Error.tsx index 8386b58a..be04041e 100644 --- a/apps/dispatch/app/_components/Error.tsx +++ b/apps/dispatch/app/_components/Error.tsx @@ -2,13 +2,25 @@ import { useEffect } from "react"; -export const Error = ({ statusCode, title }: { statusCode: number; title: string }) => { +export const Error = ({ + statusCode, + title, + description, +}: { + statusCode: number; + title: string; + description?: string; +}) => { return (

{statusCode}

-

Oh nein! Ein Fehler ist aufgetreten.

-

{title || "Ein unerwarteter Fehler ist aufgetreten."}

+

+ {title ? title : "Oh nein! Ein Fehler ist aufgetreten."} +

+

+ {description || "Ein unerwarteter Fehler ist aufgetreten."} +

diff --git a/apps/dispatch/app/_components/customToasts/AdminMessage.tsx b/apps/dispatch/app/_components/customToasts/AdminMessage.tsx index 79e88b38..1e805f01 100644 --- a/apps/dispatch/app/_components/customToasts/AdminMessage.tsx +++ b/apps/dispatch/app/_components/customToasts/AdminMessage.tsx @@ -19,10 +19,9 @@ export const AdminMessageToast = ({ event, t }: { event: AdminMessage; t: Toast event.status == "kick" && "text-yellow-500 ", )} > - Du wurdes durch den Admin {event.data?.admin.publicId}{" "} - {event.status == "ban" ? "gebannt" : "gekickt"}! + {event.message} -

{event.message}

+

{event.data?.reason}

+
+ + ); +}; + export default function AdminPanel() { const queryClient = useQueryClient(); const { data: pilots } = useQuery({ @@ -45,9 +152,6 @@ export default function AdminPanel() { queryClient.invalidateQueries({ queryKey: ["livekit-rooms"] }); }, }); - const editUSerMutation = useMutation({ - mutationFn: editUserAPI, - }); const kickPilotMutation = useMutation({ mutationFn: kickAircraftAPI, onSuccess: () => { @@ -153,22 +257,23 @@ export default function AdminPanel() { )} - - + } + onClick={({ reason }) => + kickPilotMutation.mutate({ id: p.id, reason }) + } + /> + } + onClick={({ reason, until }) => + kickPilotMutation.mutate({ id: p.id, reason, bann: true, until }) + } + /> - - + } + onClick={({ reason }) => + kickDispatchMutation.mutate({ id: d.id, reason }) + } + /> + } + onClick={({ reason, until }) => + kickDispatchMutation.mutate({ id: d.id, reason, bann: true, until }) + } + /> { +export const kickAircraftAPI = async ({ + id, + bann, + reason, + until = null, +}: { + id: number; + bann?: boolean; + reason: string; + until?: Date | null; +}) => { const res = await serverApi.delete(`/aircrafts/${id}`, { - data: { bann }, + data: { bann, reason, until }, }); console.log(res.status); if (res.status != 204) { diff --git a/apps/dispatch/app/_querys/dispatcher.ts b/apps/dispatch/app/_querys/dispatcher.ts index 319d50af..c5e24c37 100644 --- a/apps/dispatch/app/_querys/dispatcher.ts +++ b/apps/dispatch/app/_querys/dispatcher.ts @@ -25,9 +25,19 @@ export const getConnectedDispatcherAPI = async (filter?: Prisma.ConnectedDispatc return res.data; }; -export const kickDispatcherAPI = async ({ id, bann }: { id: number; bann?: boolean }) => { +export const kickDispatcherAPI = async ({ + id, + bann, + reason, + until = null, +}: { + id: number; + bann?: boolean; + reason: string; + until?: Date | null; +}) => { const res = await serverApi.delete(`/dispatcher/${id}`, { - data: { bann }, + data: { bann, reason, until }, }); console.log(res.status); if (res.status != 204) { diff --git a/apps/dispatch/app/dispatch/layout.tsx b/apps/dispatch/app/dispatch/layout.tsx index d675657d..46a26aed 100644 --- a/apps/dispatch/app/dispatch/layout.tsx +++ b/apps/dispatch/app/dispatch/layout.tsx @@ -3,6 +3,7 @@ import Navbar from "./_components/navbar/Navbar"; import { redirect } from "next/navigation"; import { getServerSession } from "../api/auth/[...nextauth]/auth"; import { Error } from "_components/Error"; +import { prisma } from "@repo/db"; export const metadata: Metadata = { title: "VAR: Disponent", @@ -15,6 +16,15 @@ export default async function RootLayout({ children: React.ReactNode; }>) { const session = await getServerSession(); + const openPenaltys = await prisma.penalty.findMany({ + where: { + userId: session?.user.id, + until: { + gte: new Date(), + }, + type: "TIME_BAN", + }, + }); if (!session || !session.user) { redirect("/login"); @@ -25,6 +35,17 @@ export default async function RootLayout({ if (!session.user.permissions.includes("DISPO")) return ; + + if (openPenaltys[0]) { + return ( + + ); + } + return ( <> diff --git a/apps/dispatch/app/pilot/layout.tsx b/apps/dispatch/app/pilot/layout.tsx index ff9fdb97..7d40ab63 100644 --- a/apps/dispatch/app/pilot/layout.tsx +++ b/apps/dispatch/app/pilot/layout.tsx @@ -3,6 +3,7 @@ import Navbar from "./_components/navbar/Navbar"; import { redirect } from "next/navigation"; import { getServerSession } from "../api/auth/[...nextauth]/auth"; import { Error } from "_components/Error"; +import { prisma } from "@repo/db"; export const metadata: Metadata = { title: "VAR: Pilot", @@ -15,6 +16,15 @@ export default async function RootLayout({ children: React.ReactNode; }>) { const session = await getServerSession(); + const openPenaltys = await prisma.penalty.findMany({ + where: { + userId: session?.user.id, + until: { + gte: new Date(), + }, + type: "TIME_BAN", + }, + }); if (!session || !session.user.firstname) { redirect("/login"); @@ -25,6 +35,17 @@ export default async function RootLayout({ if (!session.user.permissions.includes("PILOT")) return ; + + if (openPenaltys[0]) { + return ( + + ); + } + return ( <> diff --git a/apps/hub/app/(app)/_components/Penalty.tsx b/apps/hub/app/(app)/_components/Penalty.tsx new file mode 100644 index 00000000..6a3f07ab --- /dev/null +++ b/apps/hub/app/(app)/_components/Penalty.tsx @@ -0,0 +1,36 @@ +import { prisma } from "@repo/db"; +import { TriangleAlert } from "lucide-react"; +import { getServerSession } from "next-auth"; + +export const Penalty = async () => { + const session = await getServerSession(); + const openPenaltys = await prisma.penalty.findMany({ + where: { + userId: session?.user.id, + until: { + gte: new Date(), + }, + type: "TIME_BAN", + }, + }); + console.log("Open Penaltys:", openPenaltys); + if (!openPenaltys[0]) { + return null; + } + + return ( +
+
+

+ + Aktive Strafe +

+

Du hast eine aktive Strafe, die dich daran hindert, an Flügen teilzunehmen.

+

Strafe: {openPenaltys[0].reason}

+ {openPenaltys[0].until && ( +

Bis: {new Date(openPenaltys[0].until).toLocaleDateString()}

+ )} +
+
+ ); +}; diff --git a/apps/hub/app/(app)/page.tsx b/apps/hub/app/(app)/page.tsx index 85a96c45..4d51f409 100644 --- a/apps/hub/app/(app)/page.tsx +++ b/apps/hub/app/(app)/page.tsx @@ -2,6 +2,7 @@ import Events from "./_components/FeaturedEvents"; import { Stats } from "./_components/Stats"; import { Badges } from "./_components/Badges"; import { RecentFlights } from "(app)/_components/RecentFlights"; +import { Penalty } from "(app)/_components/Penalty"; export default async function Home({ searchParams, @@ -12,6 +13,7 @@ export default async function Home({ const view = stats || "pilot"; return (
+
diff --git a/packages/database/prisma/json/SocketEvents.ts b/packages/database/prisma/json/SocketEvents.ts index 161162c8..de5c1fdb 100644 --- a/packages/database/prisma/json/SocketEvents.ts +++ b/packages/database/prisma/json/SocketEvents.ts @@ -25,6 +25,7 @@ export interface AdminMessage { message: string; data?: { admin: PublicUser; + reason: string; }; } diff --git a/packages/database/prisma/schema/penalty.prisma b/packages/database/prisma/schema/penalty.prisma new file mode 100644 index 00000000..78af9442 --- /dev/null +++ b/packages/database/prisma/schema/penalty.prisma @@ -0,0 +1,23 @@ +model Penalty { + id Int @id @default(autoincrement()) + userId String + createdUserId String + + type PenaltyType + reason String + until DateTime? + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + // relations: + User User @relation(fields: [userId], references: [id]) + CreatedUser User @relation("CreatedPenalties", fields: [createdUserId], references: [id]) + Report Report[] +} + +enum PenaltyType { + KICK + TIME_BAN + BAN +} diff --git a/packages/database/prisma/schema/report.prisma b/packages/database/prisma/schema/report.prisma index b184daab..f1034126 100644 --- a/packages/database/prisma/schema/report.prisma +++ b/packages/database/prisma/schema/report.prisma @@ -2,15 +2,17 @@ model Report { id Int @id @default(autoincrement()) text String senderUserId String - reportedUserRole String @default("KP") + reportedUserRole String @default("KP") reportedUserId String timestamp DateTime @default(now()) reviewerComment String? reviewed Boolean @default(false) reviewerUserId String? + penaltyId Int? // relations: - Sender User @relation("SentReports", fields: [senderUserId], references: [id]) - Reported User @relation("ReceivedReports", fields: [reportedUserId], references: [id]) - Reviewer User? @relation("ReviewedReports", fields: [reviewerUserId], references: [id]) + Penalty Penalty? @relation(fields: [penaltyId], references: [id]) + Sender User @relation("SentReports", fields: [senderUserId], references: [id]) + Reported User @relation("ReceivedReports", fields: [reportedUserId], references: [id]) + Reviewer User? @relation("ReviewedReports", fields: [reviewerUserId], references: [id]) } diff --git a/packages/database/prisma/schema/user.prisma b/packages/database/prisma/schema/user.prisma index c68be9b0..6ccff52c 100644 --- a/packages/database/prisma/schema/user.prisma +++ b/packages/database/prisma/schema/user.prisma @@ -66,6 +66,8 @@ model User { ConnectedDispatcher ConnectedDispatcher[] ConnectedAircraft ConnectedAircraft[] PositionLog PositionLog[] + Penalty Penalty[] + CreatedPenalties Penalty[] @relation("CreatedPenalties") @@map(name: "users") }