Account migration funktioniert nun
This commit is contained in:
@@ -93,7 +93,9 @@ const EventSelect = ({ pathSelected }: { pathSelected: "disponent" | "pilot" })
|
|||||||
export const FirstPath = () => {
|
export const FirstPath = () => {
|
||||||
const modalRef = useRef<HTMLDialogElement>(null);
|
const modalRef = useRef<HTMLDialogElement>(null);
|
||||||
const { data: session } = useSession();
|
const { data: session } = useSession();
|
||||||
const [selected, setSelected] = useState<"disponent" | "pilot" | null>(null);
|
const [selected, setSelected] = useState<"disponent" | "pilot" | null>(
|
||||||
|
session?.user.badges.includes("D1") ? "disponent" : null,
|
||||||
|
);
|
||||||
const [page, setPage] = useState<"path" | "event-select">("path");
|
const [page, setPage] = useState<"path" | "event-select">("path");
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -105,12 +107,28 @@ export const FirstPath = () => {
|
|||||||
return (
|
return (
|
||||||
<dialog ref={modalRef} className="modal">
|
<dialog ref={modalRef} className="modal">
|
||||||
<div className="modal-box w-11/12 max-w-5xl">
|
<div className="modal-box w-11/12 max-w-5xl">
|
||||||
<h3 className="flex items-center gap-2 text-lg font-bold mb-10">Wähle deinen Einstieg!</h3>
|
<h3 className="flex items-center gap-2 text-lg font-bold mb-10">
|
||||||
<p className="mb-8 text-base text-base-content/80 text-center">
|
{session?.user.migratedFromV1
|
||||||
Willkommen bei Virtual Air Rescue!
|
? "Hallo, Hier hat sich einiges geändert!"
|
||||||
<br /> Wie möchtest du bei uns starten? Du kannst später jederzeit auch den anderen Pfad
|
: "Wähle deinen Einstieg!"}
|
||||||
ausprobieren, wenn du möchtest.
|
</h3>
|
||||||
</p>
|
<h2 className="text-2xl font-bold mb-4 text-center">Willkommen bei Virtual Air Rescue!</h2>
|
||||||
|
{session?.user.migratedFromV1 ? (
|
||||||
|
<p className="mb-8 text-base text-base-content/80 text-center">
|
||||||
|
Dein Account wurde erfolgreich auf das neue System migriert. Herzlich wilkommen im neuen
|
||||||
|
HUB! Um die Erfahrung für alle Nutzer zu steigern haben wir uns dazu entschlossen, dass
|
||||||
|
alle Nutzer einen Test absolvieren müssen:{" "}
|
||||||
|
{session.user.badges.includes("D1") &&
|
||||||
|
`Da du vorher schon den D1-Test absolviert hast, kannst du unter Disponent das Quick-Lane Event auswähen. Um Pilot zu werden kannst du dann später den Piloten-Kurs absolvieren.`}
|
||||||
|
{(!session.user.badges.includes("D1") || session.user.badges.includes("P1")) &&
|
||||||
|
`Als Pilot musst du den Piloten-Test abschließen.`}
|
||||||
|
</p>
|
||||||
|
) : (
|
||||||
|
<p>
|
||||||
|
Wie möchtest du bei uns starten? Du kannst später jederzeit auch den anderen Pfad
|
||||||
|
ausprobieren, wenn du möchtest.
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
<div className="flex flex-col items-center justify-center m-20">
|
<div className="flex flex-col items-center justify-center m-20">
|
||||||
{page === "path" && <PathsOptions selected={selected} setSelected={setSelected} />}
|
{page === "path" && <PathsOptions selected={selected} setSelected={setSelected} />}
|
||||||
{page === "event-select" && (
|
{page === "event-select" && (
|
||||||
|
|||||||
70
apps/hub/app/(app)/admin/report/columns.tsx
Normal file
70
apps/hub/app/(app)/admin/report/columns.tsx
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
import { Report, User } from "@repo/db";
|
||||||
|
import { ColumnDef } from "@tanstack/react-table";
|
||||||
|
import { Check, Eye, Plane, ShieldQuestion, Workflow, X } from "lucide-react";
|
||||||
|
import Link from "next/link";
|
||||||
|
|
||||||
|
export const reportColumns: ColumnDef<Report & { Sender?: User; Reported: User }>[] = [
|
||||||
|
{
|
||||||
|
accessorKey: "reviewed",
|
||||||
|
header: "Erledigt",
|
||||||
|
|
||||||
|
cell: ({ row }) => {
|
||||||
|
return (
|
||||||
|
<div className="text-center">
|
||||||
|
{row.getValue("reviewed") ? (
|
||||||
|
<Check className="text-green-500 w-5 h-5" />
|
||||||
|
) : (
|
||||||
|
<X className="text-red-500 w-5 h-5" />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "Sender",
|
||||||
|
header: "Sender",
|
||||||
|
cell: ({ row }) => {
|
||||||
|
const user = row.original.Sender;
|
||||||
|
if (!user) return "Unbekannt";
|
||||||
|
return `${user.firstname} ${user.lastname} (${user.publicId})`;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "reportedUserRole",
|
||||||
|
header: "Rolle des gemeldeten Nutzers",
|
||||||
|
cell: ({ row }) => {
|
||||||
|
const role = row.getValue("reportedUserRole") as string | undefined;
|
||||||
|
const Icon = role ? (role.startsWith("LST") ? Workflow : Plane) : ShieldQuestion;
|
||||||
|
return (
|
||||||
|
<span className="flex items-center gap-2">
|
||||||
|
<Icon className="w-4 h-4" />
|
||||||
|
{role || "Unbekannt"}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "Reported",
|
||||||
|
header: "Reported",
|
||||||
|
cell: ({ row }) => {
|
||||||
|
const user = row.original.Reported;
|
||||||
|
return `${user.firstname} ${user.lastname} (${user.publicId})`;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "timestamp",
|
||||||
|
header: "Time",
|
||||||
|
cell: ({ row }) => new Date(row.getValue("timestamp")).toLocaleString(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "actions",
|
||||||
|
header: "Actions",
|
||||||
|
cell: ({ row }) => (
|
||||||
|
<Link href={`/admin/report/${row.original.id}`}>
|
||||||
|
<button className="btn btn-sm btn-outline btn-info flex items-center gap-2">
|
||||||
|
<Eye className="w-4 h-4" /> Anzeigen
|
||||||
|
</button>
|
||||||
|
</Link>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
];
|
||||||
@@ -1,10 +1,6 @@
|
|||||||
"use client";
|
"use client";
|
||||||
import { Check, Eye, ShieldQuestion, X } from "lucide-react";
|
|
||||||
import Link from "next/link";
|
|
||||||
import { PaginatedTable } from "_components/PaginatedTable";
|
import { PaginatedTable } from "_components/PaginatedTable";
|
||||||
import { Report, User } from "@repo/db";
|
import { reportColumns } from "(app)/admin/report/columns";
|
||||||
import { ColumnDef } from "@tanstack/react-table";
|
|
||||||
import { Workflow, Plane } from "lucide-react";
|
|
||||||
|
|
||||||
export default function ReportPage() {
|
export default function ReportPage() {
|
||||||
return (
|
return (
|
||||||
@@ -15,72 +11,7 @@ export default function ReportPage() {
|
|||||||
Sender: true,
|
Sender: true,
|
||||||
Reported: true,
|
Reported: true,
|
||||||
}}
|
}}
|
||||||
columns={
|
columns={reportColumns}
|
||||||
[
|
|
||||||
{
|
|
||||||
accessorKey: "reviewed",
|
|
||||||
header: "Erledigt",
|
|
||||||
|
|
||||||
cell: ({ row }) => {
|
|
||||||
return (
|
|
||||||
<div className="text-center">
|
|
||||||
{row.getValue("reviewed") ? (
|
|
||||||
<Check className="text-green-500 w-5 h-5" />
|
|
||||||
) : (
|
|
||||||
<X className="text-red-500 w-5 h-5" />
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: "Sender",
|
|
||||||
header: "Sender",
|
|
||||||
cell: ({ row }) => {
|
|
||||||
const user = row.getValue("Sender") as User;
|
|
||||||
return `${user.firstname} ${user.lastname} (${user.publicId})`;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: "reportedUserRole",
|
|
||||||
header: "Rolle des gemeldeten Nutzers",
|
|
||||||
cell: ({ row }) => {
|
|
||||||
const role = row.getValue("reportedUserRole") as string | undefined;
|
|
||||||
const Icon = role ? (role.startsWith("LST") ? Workflow : Plane) : ShieldQuestion;
|
|
||||||
return (
|
|
||||||
<span className="flex items-center gap-2">
|
|
||||||
<Icon className="w-4 h-4" />
|
|
||||||
{role || "Unbekannt"}
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: "Reported",
|
|
||||||
header: "Reported",
|
|
||||||
cell: ({ row }) => {
|
|
||||||
const user = row.getValue("Reported") as User;
|
|
||||||
return `${user.firstname} ${user.lastname} (${user.publicId})`;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: "timestamp",
|
|
||||||
header: "Time",
|
|
||||||
cell: ({ row }) => new Date(row.getValue("timestamp")).toLocaleString(),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: "actions",
|
|
||||||
header: "Actions",
|
|
||||||
cell: ({ row }) => (
|
|
||||||
<Link href={`/admin/report/${row.original.id}`}>
|
|
||||||
<button className="btn btn-sm btn-outline btn-info flex items-center gap-2">
|
|
||||||
<Eye className="w-4 h-4" /> Anzeigen
|
|
||||||
</button>
|
|
||||||
</Link>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
] as ColumnDef<Report>[]
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ import { setStandardName } from "../../../../../../helper/discord";
|
|||||||
import { penaltyColumns } from "(app)/admin/penalty/columns";
|
import { penaltyColumns } from "(app)/admin/penalty/columns";
|
||||||
import { PenaltyDropdown } from "(app)/admin/user/[id]/_components/AddPenaltyDropdown";
|
import { PenaltyDropdown } from "(app)/admin/user/[id]/_components/AddPenaltyDropdown";
|
||||||
import { addPenalty, editPenalty, editPenaltys } from "(app)/admin/penalty/actions";
|
import { addPenalty, editPenalty, editPenaltys } from "(app)/admin/penalty/actions";
|
||||||
|
import { reportColumns } from "(app)/admin/report/columns";
|
||||||
|
|
||||||
interface ProfileFormProps {
|
interface ProfileFormProps {
|
||||||
user: User;
|
user: User;
|
||||||
@@ -405,50 +406,7 @@ export const UserReports = ({ user }: { user: User }) => {
|
|||||||
Sender: true,
|
Sender: true,
|
||||||
Reported: true,
|
Reported: true,
|
||||||
}}
|
}}
|
||||||
columns={
|
columns={reportColumns}
|
||||||
[
|
|
||||||
{
|
|
||||||
accessorKey: "reviewed",
|
|
||||||
header: "Erledigt",
|
|
||||||
|
|
||||||
cell: ({ row }) => {
|
|
||||||
return (
|
|
||||||
<div className="text-center">
|
|
||||||
{row.getValue("reviewed") ? (
|
|
||||||
<Check className="text-green-500 w-5 h-5" />
|
|
||||||
) : (
|
|
||||||
<X className="text-red-500 w-5 h-5" />
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: "Sender",
|
|
||||||
header: "Sender",
|
|
||||||
cell: ({ row }) => {
|
|
||||||
const user = row.getValue("Sender") as User;
|
|
||||||
return `${user.firstname} ${user.lastname} (${user.publicId})`;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: "timestamp",
|
|
||||||
header: "Time",
|
|
||||||
cell: ({ row }) => new Date(row.getValue("timestamp")).toLocaleString(),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: "actions",
|
|
||||||
header: "Actions",
|
|
||||||
cell: ({ row }) => (
|
|
||||||
<Link href={`/admin/report/${row.original.id}`}>
|
|
||||||
<button className="btn btn-sm btn-outline btn-info flex items-center gap-2">
|
|
||||||
<Eye className="w-4 h-4" /> Anzeigen
|
|
||||||
</button>
|
|
||||||
</Link>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
] as ColumnDef<Report>[]
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ export const ProfileForm = ({
|
|||||||
user: User;
|
user: User;
|
||||||
penaltys: Penalty[];
|
penaltys: Penalty[];
|
||||||
}): React.JSX.Element => {
|
}): React.JSX.Element => {
|
||||||
const canEdit = penaltys.length === 0 && user.isBanned;
|
const canEdit = penaltys.length === 0 && !user.isBanned;
|
||||||
|
|
||||||
const schema = z.object({
|
const schema = z.object({
|
||||||
firstname: z.string().min(2).max(30),
|
firstname: z.string().min(2).max(30),
|
||||||
@@ -318,7 +318,7 @@ export const DeleteForm = ({ user, penaltys }: { user: User; penaltys: Penalty[]
|
|||||||
className="btn-error btn-outline btn-sm w-full"
|
className="btn-error btn-outline btn-sm w-full"
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
await deleteUser(user.id);
|
await deleteUser(user.id);
|
||||||
router.push("/login");
|
router.push("/logout");
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Trash2 size={15} /> Konto sofort löschen
|
<Trash2 size={15} /> Konto sofort löschen
|
||||||
|
|||||||
@@ -16,15 +16,10 @@ const BadgeImage = {
|
|||||||
[BADGES.D2]: D2,
|
[BADGES.D2]: D2,
|
||||||
[BADGES.D3]: D3,
|
[BADGES.D3]: D3,
|
||||||
[BADGES.DAY1]: DAY1,
|
[BADGES.DAY1]: DAY1,
|
||||||
|
[BADGES.V1Veteran]: DAY1,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Badge = ({
|
export const Badge = ({ name, className }: { name: BADGES; className?: string }) => {
|
||||||
name,
|
|
||||||
className,
|
|
||||||
}: {
|
|
||||||
name: BADGES;
|
|
||||||
className?: string;
|
|
||||||
}) => {
|
|
||||||
const image = BadgeImage[name];
|
const image = BadgeImage[name];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
import { AuthOptions, getServerSession as getNextAuthServerSession } from "next-auth";
|
import { AuthOptions, getServerSession as getNextAuthServerSession } from "next-auth";
|
||||||
import { PrismaAdapter } from "@next-auth/prisma-adapter";
|
import { PrismaAdapter } from "@next-auth/prisma-adapter";
|
||||||
import Credentials from "next-auth/providers/credentials";
|
import Credentials from "next-auth/providers/credentials";
|
||||||
import { prisma } from "@repo/db";
|
import { DiscordAccount, prisma, User } from "@repo/db";
|
||||||
import bcrypt from "bcryptjs";
|
import bcrypt from "bcryptjs";
|
||||||
|
import oldUser from "./var.User.json";
|
||||||
|
import { OldUser } from "../../../../types/oldUser";
|
||||||
|
|
||||||
export const options: AuthOptions = {
|
export const options: AuthOptions = {
|
||||||
providers: [
|
providers: [
|
||||||
@@ -14,9 +16,59 @@ export const options: AuthOptions = {
|
|||||||
async authorize(credentials) {
|
async authorize(credentials) {
|
||||||
try {
|
try {
|
||||||
if (!credentials) throw new Error("No credentials provided");
|
if (!credentials) throw new Error("No credentials provided");
|
||||||
const user = await prisma.user.findFirstOrThrow({
|
const user = await prisma.user.findFirst({
|
||||||
where: { email: credentials.email },
|
where: { email: credentials.email },
|
||||||
});
|
});
|
||||||
|
const v1User = (oldUser as OldUser[]).find((u) => u.email === credentials.email);
|
||||||
|
if (!user && v1User) {
|
||||||
|
if (bcrypt.compareSync(credentials.password, v1User.password)) {
|
||||||
|
const newUser = await prisma.user.create({
|
||||||
|
data: {
|
||||||
|
email: v1User.email,
|
||||||
|
password: v1User.password,
|
||||||
|
migratedFromV1: true,
|
||||||
|
firstname: v1User.firstname,
|
||||||
|
lastname: v1User.lastname,
|
||||||
|
publicId: v1User.publicId,
|
||||||
|
badges: [
|
||||||
|
...v1User.badges
|
||||||
|
.map((badge) => {
|
||||||
|
switch (badge) {
|
||||||
|
case "day-1-member":
|
||||||
|
return "DAY1";
|
||||||
|
case "d-1":
|
||||||
|
return "D1";
|
||||||
|
case "p-1":
|
||||||
|
return "P1";
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.filter((badge) => badge !== null),
|
||||||
|
"V1Veteran",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (v1User.discord) {
|
||||||
|
await prisma.discordAccount.create({
|
||||||
|
data: {
|
||||||
|
tokenType: "Bearer",
|
||||||
|
refreshToken: v1User.discord.tokens.refresh_token,
|
||||||
|
discordId: v1User.discord.profile.id,
|
||||||
|
userId: newUser.id,
|
||||||
|
username: v1User.discord.profile.username,
|
||||||
|
globalName: v1User.discord.profile.global_name,
|
||||||
|
avatar: v1User.discord.profile.avatar,
|
||||||
|
email: v1User.discord.profile.email,
|
||||||
|
verified: v1User.discord.profile.verified,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return newUser;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!user) return null;
|
||||||
|
|
||||||
if (bcrypt.compareSync(credentials.password, user.password)) {
|
if (bcrypt.compareSync(credentials.password, user.password)) {
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
|||||||
749736
apps/hub/app/api/auth/[...nextauth]/var.User.json
Normal file
749736
apps/hub/app/api/auth/[...nextauth]/var.User.json
Normal file
File diff suppressed because it is too large
Load Diff
26
apps/hub/types/oldUser.ts
Normal file
26
apps/hub/types/oldUser.ts
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
export interface OldUser {
|
||||||
|
firstname: string;
|
||||||
|
publicId: string;
|
||||||
|
badges: string[];
|
||||||
|
lastname: string;
|
||||||
|
email: string;
|
||||||
|
password: string;
|
||||||
|
settings: {
|
||||||
|
privacyHideLastnameInNickname?: boolean;
|
||||||
|
notifyRoomID?: string;
|
||||||
|
};
|
||||||
|
discord?: {
|
||||||
|
profile: {
|
||||||
|
id: string;
|
||||||
|
username: string;
|
||||||
|
global_name: string;
|
||||||
|
avatar: string;
|
||||||
|
email: string;
|
||||||
|
verified: boolean;
|
||||||
|
};
|
||||||
|
tokens: {
|
||||||
|
refresh_token: string;
|
||||||
|
scope: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -45,8 +45,8 @@ model MissionOnStationUsers {
|
|||||||
stationId Int
|
stationId Int
|
||||||
|
|
||||||
// relations:
|
// relations:
|
||||||
User User @relation(fields: [userId], references: [id])
|
User User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||||
Mission Mission @relation(fields: [missionId], references: [id])
|
Mission Mission @relation(fields: [missionId], references: [id], onDelete: Cascade)
|
||||||
Station Station @relation(fields: [stationId], references: [id])
|
Station Station @relation(fields: [stationId], references: [id])
|
||||||
|
|
||||||
@@unique([userId, missionId, stationId])
|
@@unique([userId, missionId, stationId])
|
||||||
@@ -57,7 +57,7 @@ model MissionsOnStations {
|
|||||||
stationId Int
|
stationId Int
|
||||||
|
|
||||||
// relations:
|
// relations:
|
||||||
Mission Mission @relation(fields: [missionId], references: [id])
|
Mission Mission @relation(fields: [missionId], references: [id], onDelete: Cascade)
|
||||||
Station Station @relation(fields: [stationId], references: [id])
|
Station Station @relation(fields: [stationId], references: [id])
|
||||||
|
|
||||||
@@id([missionId, stationId])
|
@@id([missionId, stationId])
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ enum BADGES {
|
|||||||
D2
|
D2
|
||||||
D3
|
D3
|
||||||
DAY1
|
DAY1
|
||||||
|
V1Veteran
|
||||||
}
|
}
|
||||||
|
|
||||||
enum PERMISSION {
|
enum PERMISSION {
|
||||||
@@ -34,6 +35,7 @@ model User {
|
|||||||
|
|
||||||
// Settings:
|
// Settings:
|
||||||
pathSelected Boolean @default(false)
|
pathSelected Boolean @default(false)
|
||||||
|
migratedFromV1 Boolean @default(false)
|
||||||
settingsNtfyRoom String? @map(name: "settings_ntfy_room")
|
settingsNtfyRoom String? @map(name: "settings_ntfy_room")
|
||||||
settingsMicDevice String? @map(name: "settings_mic_device")
|
settingsMicDevice String? @map(name: "settings_mic_device")
|
||||||
settingsMicVolume Float? @map(name: "settings_mic_volume")
|
settingsMicVolume Float? @map(name: "settings_mic_volume")
|
||||||
@@ -83,14 +85,14 @@ model DiscordAccount {
|
|||||||
avatar String? @map(name: "avatar")
|
avatar String? @map(name: "avatar")
|
||||||
globalName String @map(name: "global_name")
|
globalName String @map(name: "global_name")
|
||||||
verified Boolean @default(false)
|
verified Boolean @default(false)
|
||||||
accessToken String @map(name: "access_token")
|
accessToken String? @map(name: "access_token")
|
||||||
refreshToken String @map(name: "refresh_token")
|
refreshToken String @map(name: "refresh_token")
|
||||||
tokenType String @map(name: "token_type")
|
tokenType String @map(name: "token_type")
|
||||||
createdAt DateTime @default(now()) @map(name: "created_at")
|
createdAt DateTime @default(now()) @map(name: "created_at")
|
||||||
updatedAt DateTime @default(now()) @map(name: "updated_at")
|
updatedAt DateTime @default(now()) @map(name: "updated_at")
|
||||||
|
|
||||||
// relations:
|
// relations:
|
||||||
user User @relation(fields: [userId], references: [id]) // Beziehung zu User
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade) // Beziehung zu User
|
||||||
|
|
||||||
@@map(name: "discord_accounts")
|
@@map(name: "discord_accounts")
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user