From 8cb1e8bec5977132f823768888df244a8d82e3b3 Mon Sep 17 00:00:00 2001 From: bytedream Date: Tue, 3 Dec 2024 14:04:15 +0100 Subject: [PATCH] add admin tools page --- src/lib/permissions.ts | 7 ++- src/routes/admin/+layout.server.ts | 1 + src/routes/admin/+layout.svelte | 10 +++- src/routes/admin/admin/+page.svelte | 3 +- src/routes/admin/tools/+layout.svelte | 3 + src/routes/admin/tools/+page.server.ts | 11 ++++ src/routes/admin/tools/+page.svelte | 15 +++++ src/routes/admin/tools/+server.ts | 71 ++++++++++++++++++++++++ src/routes/admin/tools/UuidFinder.svelte | 44 +++++++++++++++ src/routes/admin/tools/schema.ts | 6 ++ src/routes/admin/tools/tools.ts | 22 ++++++++ 11 files changed, 190 insertions(+), 3 deletions(-) create mode 100644 src/routes/admin/tools/+layout.svelte create mode 100644 src/routes/admin/tools/+page.server.ts create mode 100644 src/routes/admin/tools/+page.svelte create mode 100644 src/routes/admin/tools/+server.ts create mode 100644 src/routes/admin/tools/UuidFinder.svelte create mode 100644 src/routes/admin/tools/schema.ts create mode 100644 src/routes/admin/tools/tools.ts diff --git a/src/lib/permissions.ts b/src/lib/permissions.ts index c1f441c..74bc61e 100644 --- a/src/lib/permissions.ts +++ b/src/lib/permissions.ts @@ -4,6 +4,7 @@ export class Permissions { static readonly Reports = 2 << 2; static readonly Feedback = 2 << 3; static readonly Settings = 2 << 4; + static readonly Tools = 2 << 5; readonly value: number; @@ -31,7 +32,8 @@ export class Permissions { Permissions.Users, Permissions.Reports, Permissions.Feedback, - Permissions.Settings + Permissions.Settings, + Permissions.Tools ]; } @@ -51,6 +53,9 @@ export class Permissions { settings(): boolean { return (this.value & Permissions.Reports) != 0; } + tools(): boolean { + return (this.value & Permissions.Tools) != 0; + } asArray(): number[] { const array = []; diff --git a/src/routes/admin/+layout.server.ts b/src/routes/admin/+layout.server.ts index 1b59655..15bef1b 100644 --- a/src/routes/admin/+layout.server.ts +++ b/src/routes/admin/+layout.server.ts @@ -21,6 +21,7 @@ export const load: LayoutServerLoad = async ({ route, cookies }) => { : null, adminCount: session?.permissions.admin() ? await Admin.count() : null, settingsRead: session?.permissions.settings(), + toolsRead: session?.permissions.tools(), self: session ? JSON.parse(JSON.stringify(await Admin.findOne({ where: { id: session.userId } }))) : null diff --git a/src/routes/admin/+layout.svelte b/src/routes/admin/+layout.svelte index c8534c5..4d0bac7 100644 --- a/src/routes/admin/+layout.svelte +++ b/src/routes/admin/+layout.svelte @@ -8,7 +8,8 @@ Flag, UserGroup, Users, - BookOpen + BookOpen, + WrenchScrewdriver } from 'svelte-heros-v2'; import { buttonTriggeredRequest } from '$lib/components/utils'; import { goto } from '$app/navigation'; @@ -69,6 +70,13 @@ name: 'Website Einstellungen', badge: null, enabled: data.settingsRead + }, + { + path: `${env.PUBLIC_BASE_PATH}/admin/tools`, + icon: WrenchScrewdriver, + name: 'Tools', + badge: null, + enabled: data.toolsRead } ]; diff --git a/src/routes/admin/admin/+page.svelte b/src/routes/admin/admin/+page.svelte index 4797198..b3d3588 100644 --- a/src/routes/admin/admin/+page.svelte +++ b/src/routes/admin/admin/+page.svelte @@ -21,7 +21,8 @@ Users: Permissions.Users, Reports: Permissions.Reports, Feedback: Permissions.Feedback, - Settings: Permissions.Settings + Settings: Permissions.Settings, + Tools: Permissions.Tools }; let newAdminUsername = $state(''); diff --git a/src/routes/admin/tools/+layout.svelte b/src/routes/admin/tools/+layout.svelte new file mode 100644 index 0000000..ff4d0e4 --- /dev/null +++ b/src/routes/admin/tools/+layout.svelte @@ -0,0 +1,3 @@ +
+ +
diff --git a/src/routes/admin/tools/+page.server.ts b/src/routes/admin/tools/+page.server.ts new file mode 100644 index 0000000..65fdaef --- /dev/null +++ b/src/routes/admin/tools/+page.server.ts @@ -0,0 +1,11 @@ +import type { PageServerLoad } from './$types'; +import { getSession } from '$lib/server/session'; +import { Permissions } from '$lib/permissions'; +import { redirect } from '@sveltejs/kit'; +import { env } from '$env/dynamic/public'; + +export const load: PageServerLoad = async ({ cookies }) => { + if (getSession(cookies, { permissions: [Permissions.Settings] }) == null) { + throw redirect(302, `${env.PUBLIC_BASE_PATH}/admin`); + } +}; diff --git a/src/routes/admin/tools/+page.svelte b/src/routes/admin/tools/+page.svelte new file mode 100644 index 0000000..cdd0fd5 --- /dev/null +++ b/src/routes/admin/tools/+page.svelte @@ -0,0 +1,15 @@ + + +
+ {#each tools as tool} + {@const Component = tool.component} +
+ {tool.label} + +
+ {/each} +
diff --git a/src/routes/admin/tools/+server.ts b/src/routes/admin/tools/+server.ts new file mode 100644 index 0000000..d0fc726 --- /dev/null +++ b/src/routes/admin/tools/+server.ts @@ -0,0 +1,71 @@ +import { getSession } from '$lib/server/session'; +import { Permissions } from '$lib/permissions'; +import type { RequestHandler } from '@sveltejs/kit'; +import { + ApiError, + getBedrockUuid, + getJavaUuid, + RateLimitError, + UserNotFoundError +} from '$lib/server/minecraft'; +import type { ZodType } from 'zod'; +import { FindUuidSchema } from './schema'; + +export const POST = (async ({ url, request, cookies }) => { + if (getSession(cookies, { permissions: [Permissions.Tools] }) == null) { + return new Response(null, { status: 401 }); + } + + const action = url.searchParams.get('action'); + if (!action) { + return new Response(null, { status: 400 }); + } + + try { + switch (action) { + case 'getuuid': { + const data = await parseData(FindUuidSchema, await request.json()); + return new Response(await findUuid(data.username, data.edition), { status: 200 }); + } + } + return new Response(null, { status: 400 }); + } catch (error) { + return new Response(JSON.stringify({ error: error }), { status: 400 }); + } +}) satisfies RequestHandler; + +async function parseData(schema: ZodType, json: string): Promise { + const parseResult = await schema.safeParseAsync(json); + if (!parseResult.success) throw new Error(parseResult.error.toString()); + + return parseResult.data; +} + +async function findUuid(username: string, edition: 'java' | 'bedrock'): Promise { + let uuid = ''; + + try { + switch (edition) { + case 'java': + uuid = await getJavaUuid(username); + break; + case 'bedrock': + uuid = await getBedrockUuid(username); + break; + } + } catch (e) { + if (e instanceof UserNotFoundError) { + throw `Der Spielername ${username} existiert nicht`; + } else if (e instanceof ApiError) { + throw (e as Error).message; + } else if (e instanceof RateLimitError) { + throw 'Rate limit exceeded, bitte versuche es erneut'; + } else { + throw e; + } + } + + return JSON.stringify({ + uuid: uuid + }); +} diff --git a/src/routes/admin/tools/UuidFinder.svelte b/src/routes/admin/tools/UuidFinder.svelte new file mode 100644 index 0000000..57533c9 --- /dev/null +++ b/src/routes/admin/tools/UuidFinder.svelte @@ -0,0 +1,44 @@ + + +
+
+ + {#snippet label()} + Username + {/snippet} + + +
+ + sendRequest('getuuid', { username, edition }) + .then((data) => (uuid = data.uuid)) + .catch(() => (uuid = ''))} + /> +
+ +
+
diff --git a/src/routes/admin/tools/schema.ts b/src/routes/admin/tools/schema.ts new file mode 100644 index 0000000..2c08c14 --- /dev/null +++ b/src/routes/admin/tools/schema.ts @@ -0,0 +1,6 @@ +import { z } from 'zod'; + +export const FindUuidSchema = z.object({ + username: z.string(), + edition: z.enum(['java', 'bedrock']) +}); diff --git a/src/routes/admin/tools/tools.ts b/src/routes/admin/tools/tools.ts new file mode 100644 index 0000000..4c43fac --- /dev/null +++ b/src/routes/admin/tools/tools.ts @@ -0,0 +1,22 @@ +import { env } from '$env/dynamic/public'; +import { errorMessage } from '$lib/stores'; + +type Actions = 'getuuid'; + +export async function sendRequest(action: Actions, data: any): Promise { + const response = await fetch(`${env.PUBLIC_BASE_PATH}/admin/tools?action=${action}`, { + method: 'POST', + body: JSON.stringify(data) + }); + if (!response.ok) { + const data = await response.json(); + if (Object.hasOwn(data, 'error')) { + errorMessage.set(data['error']); + } else { + errorMessage.set('Ein Fehler ist aufgetreten'); + } + throw new Error(); + } + + return response.json(); +}