From 445a429be7cdeae68df6258ae96099e04293bc06 Mon Sep 17 00:00:00 2001 From: averel10 Date: Sun, 8 Mar 2026 10:27:23 +0100 Subject: [PATCH] added admin system --- src/app/actions/manage-users.ts | 36 +++++ .../datasets/[id]/entries/[entryId]/page.tsx | 12 ++ src/app/admin/datasets/[id]/page.tsx | 12 ++ src/app/admin/page.tsx | 31 +++- src/app/admin/users/page.tsx | 39 +++++ src/components/Header.tsx | 8 + src/components/UsersList.tsx | 140 ++++++++++++++++++ src/lib/auth.ts | 34 +++-- src/lib/model/auth-schema.ts | 1 + 9 files changed, 297 insertions(+), 16 deletions(-) create mode 100644 src/app/actions/manage-users.ts create mode 100644 src/app/admin/users/page.tsx create mode 100644 src/components/UsersList.tsx diff --git a/src/app/actions/manage-users.ts b/src/app/actions/manage-users.ts new file mode 100644 index 0000000..7ba5b9a --- /dev/null +++ b/src/app/actions/manage-users.ts @@ -0,0 +1,36 @@ +"use server"; + +import db from "@/lib/db"; +import { user } from "@/lib/model/auth-schema"; +import { eq } from "drizzle-orm"; +import { requireAdmin } from "@/lib/auth"; + +export async function deleteUser(userId: string) { + const result = await requireAdmin(); + + if (!result.authenticated || !result.admin) { + throw new Error("Unauthorized"); + } + + await db.delete(user).where(eq(user.id, userId)); +} + +export async function setUserAdmin(userId: string, isAdmin: boolean) { + const result = await requireAdmin(); + + if (!result.authenticated || !result.admin) { + throw new Error("Unauthorized"); + } + + await db.update(user).set({ admin: isAdmin }).where(eq(user.id, userId)); +} + +export async function getAllUsers() { + const result = await requireAdmin(); + + if (!result.authenticated || !result.admin) { + throw new Error("Unauthorized"); + } + + return db.select().from(user); +} diff --git a/src/app/admin/datasets/[id]/entries/[entryId]/page.tsx b/src/app/admin/datasets/[id]/entries/[entryId]/page.tsx index 01e7131..5fffc87 100644 --- a/src/app/admin/datasets/[id]/entries/[entryId]/page.tsx +++ b/src/app/admin/datasets/[id]/entries/[entryId]/page.tsx @@ -3,6 +3,8 @@ import db from '@/lib/db'; import { dataset_entry } from '@/lib/model/dataset_entry'; import { eq } from 'drizzle-orm'; import AudioPlayer from '@/components/AudioPlayer'; +import { requireAdmin } from '@/lib/auth'; +import { redirect } from 'next/navigation'; interface DatasetEntryPageProps { params: { @@ -12,6 +14,16 @@ interface DatasetEntryPageProps { } export default async function DatasetEntryPage({ params }: DatasetEntryPageProps) { + const result = await requireAdmin(); + + if (!result.authenticated) { + redirect("/user/sign-in"); + } + + if (!result.admin) { + redirect("/"); + } + const { id: datasetId, entryId } = await params; const datasetIdNum = parseInt(datasetId, 10); const entryIdNum = parseInt(entryId, 10); diff --git a/src/app/admin/datasets/[id]/page.tsx b/src/app/admin/datasets/[id]/page.tsx index 7ea8918..e85d3db 100644 --- a/src/app/admin/datasets/[id]/page.tsx +++ b/src/app/admin/datasets/[id]/page.tsx @@ -6,6 +6,8 @@ import EditableDatasetHeader from '@/components/EditableDatasetHeader'; import DatasetEntriesList from '@/components/DatasetEntriesList'; import UploadDatasetEntriesModal from '@/components/UploadDatasetEntriesModal'; import RemoveAllEntriesButton from '@/components/RemoveAllEntriesButton'; +import { requireAdmin } from '@/lib/auth'; +import { redirect } from 'next/navigation'; interface DatasetPageProps { params: { @@ -14,6 +16,16 @@ interface DatasetPageProps { } export default async function DatasetPage({ params }: DatasetPageProps) { + const result = await requireAdmin(); + + if (!result.authenticated) { + redirect("/user/sign-in"); + } + + if (!result.admin) { + redirect("/"); + } + const {id} = await params; const datasetId = parseInt(id, 10); diff --git a/src/app/admin/page.tsx b/src/app/admin/page.tsx index bf337d0..2a718b0 100644 --- a/src/app/admin/page.tsx +++ b/src/app/admin/page.tsx @@ -1,22 +1,39 @@ import DatasetsList from "@/components/DatasetsList"; import CreateDatasetModal from "@/components/CreateDatasetModal"; -import { auth } from "@/lib/auth"; -import { headers } from "next/headers"; +import { requireAdmin } from "@/lib/auth"; import { redirect } from "next/navigation"; +import Link from "next/link"; export default async function AdminPage() { - const session = await auth.api.getSession({ - headers: await headers() - }) + const result = await requireAdmin(); - if(!session) { - redirect("/user/sign-in") + if (!result.authenticated) { + redirect("/user/sign-in"); + } + + if (!result.admin) { + redirect("/"); } return (
+
+

Admin Panel

+
+ + Manage Users + +
+
+ +
+

Datasets

+
); } diff --git a/src/app/admin/users/page.tsx b/src/app/admin/users/page.tsx new file mode 100644 index 0000000..51e4cc9 --- /dev/null +++ b/src/app/admin/users/page.tsx @@ -0,0 +1,39 @@ +import Link from "next/link"; +import UsersList from "@/components/UsersList"; +import { requireAdmin } from "@/lib/auth"; +import { redirect } from "next/navigation"; +import { getAllUsers } from "@/app/actions/manage-users"; + +export default async function UsersPage() { + const result = await requireAdmin(); + + if (!result.authenticated) { + redirect("/user/sign-in"); + } + + if (!result.admin) { + redirect("/"); + } + + const users = await getAllUsers(); + + return ( +
+
+ + ← Back to Admin + +

User Management

+
+ +
+
+ +
+
+
+ ); +} diff --git a/src/components/Header.tsx b/src/components/Header.tsx index a7c7b11..d2b7d63 100644 --- a/src/components/Header.tsx +++ b/src/components/Header.tsx @@ -31,6 +31,14 @@ export function Header() {

Willkommen zurück

{session.user?.email}

+ {(session.user as any)?.admin && ( + + Admin + + )}