diff --git a/src/actions/index.ts b/src/actions/index.ts index 44c2968..496749e 100644 --- a/src/actions/index.ts +++ b/src/actions/index.ts @@ -6,6 +6,7 @@ import { team } from './team.ts'; import { settings } from './settings.ts'; import { feedback } from './feedback.ts'; import { report } from './report.ts'; +import { tools } from './tools.ts'; export const server = { admin, @@ -15,5 +16,6 @@ export const server = { user, report, feedback, - settings + settings, + tools }; diff --git a/src/actions/tools.ts b/src/actions/tools.ts new file mode 100644 index 0000000..3afc5ca --- /dev/null +++ b/src/actions/tools.ts @@ -0,0 +1,57 @@ +import { ActionError, defineAction } from 'astro:actions'; +import { Session } from '@util/session.ts'; +import { Permissions } from '@util/permissions.ts'; +import { z } from 'astro:schema'; +import { getBedrockUuid, getJavaUuid } from '@util/minecraft.ts'; + +export const tools = { + uuidFromUsername: defineAction({ + input: z.object({ + edition: z.enum(['java', 'bedrock']), + username: z.string() + }), + handler: async (input, context) => { + Session.actionSessionFromCookies(context.cookies, Permissions.Tools); + + let uuid = null; + switch (input.edition) { + case 'java': + try { + uuid = await getJavaUuid(input.username); + } catch (_) { + throw new ActionError({ + code: 'NOT_FOUND', + message: `Der Username ${input.username} existiert nicht` + }); + } + if (uuid == null) { + throw new ActionError({ + code: 'BAD_REQUEST', + message: `Während der Anfrage zur Mojang API ist ein Fehler aufgetreten` + }); + } + break; + case 'bedrock': + try { + uuid = await getBedrockUuid(input.username); + } catch (_) { + throw new ActionError({ + code: 'NOT_FOUND', + message: `Der Username ${input.username} existiert nicht` + }); + } + if (uuid == null) { + throw new ActionError({ + code: 'BAD_REQUEST', + message: `Während der Anfrage zum Username Resolver ist ein Fehler aufgetreten` + }); + } + break; + } + + return { + uuid: uuid + }; + } + }) +}; diff --git a/src/app/admin/tools/AccountUuidFinder.svelte b/src/app/admin/tools/AccountUuidFinder.svelte new file mode 100644 index 0000000..c341328 --- /dev/null +++ b/src/app/admin/tools/AccountUuidFinder.svelte @@ -0,0 +1,29 @@ + + +
+ Account UUID finder +
+
+ + +
+
diff --git a/src/app/admin/tools/Tools.svelte b/src/app/admin/tools/Tools.svelte new file mode 100644 index 0000000..1e98dba --- /dev/null +++ b/src/app/admin/tools/Tools.svelte @@ -0,0 +1,7 @@ + + +
+ +
diff --git a/src/app/admin/tools/tools.ts b/src/app/admin/tools/tools.ts new file mode 100644 index 0000000..9b9d622 --- /dev/null +++ b/src/app/admin/tools/tools.ts @@ -0,0 +1,12 @@ +import { actions } from 'astro:actions'; +import { actionErrorPopup } from '@util/action.ts'; + +export async function uuidFromUsername(edition: 'java' | 'bedrock', username: string) { + const { data, error } = await actions.tools.uuidFromUsername({ edition: edition, username: username }); + if (error) { + actionErrorPopup(error); + return null; + } + + return data.uuid; +} diff --git a/src/layouts/admin/AdminLayout.astro b/src/layouts/admin/AdminLayout.astro index 20ab456..83ed98c 100644 --- a/src/layouts/admin/AdminLayout.astro +++ b/src/layouts/admin/AdminLayout.astro @@ -72,6 +72,12 @@ const adminTabs = [ name: 'Einstellungen', icon: 'heroicons:adjustments-horizontal', enabled: session?.permissions.settings + }, + { + href: 'admin/tools', + name: 'Tools', + icon: 'heroicons:wrench-screwdriver', + enabled: session?.permissions.tools } ]; --- diff --git a/src/pages/admin/tools/index.astro b/src/pages/admin/tools/index.astro new file mode 100644 index 0000000..da7dc1d --- /dev/null +++ b/src/pages/admin/tools/index.astro @@ -0,0 +1,14 @@ +--- +import { Session } from '@util/session'; +import { Permissions } from '@util/permissions'; +import { BASE_PATH } from 'astro:env/server'; +import AdminLayout from '@layouts/admin/AdminLayout.astro'; +import Tools from '@app/admin/tools/Tools.svelte'; + +const session = Session.sessionFromCookies(Astro.cookies, Permissions.Tools); +if (!session) return Astro.redirect(`${BASE_PATH}/admin`); +--- + + + + diff --git a/src/util/minecraft.ts b/src/util/minecraft.ts index a14fa3a..c6239a0 100644 --- a/src/util/minecraft.ts +++ b/src/util/minecraft.ts @@ -12,3 +12,46 @@ export async function getJavaUuid(username: string) { // prettier-ignore return `${id.substring(0, 8)}-${id.substring(8, 12)}-${id.substring(12, 16)}-${id.substring(16, 20)}-${id.substring(20)}`; } + +// https://github.com/carlop3333/XUIDGrabber/blob/main/grabber.js +export async function getBedrockUuid(username: string): Promise { + const initialPageResponse = await fetch('https://cxkes.me/xbox/xuid'); + const initialPageContent = await initialPageResponse.text(); + const token = /name="_token"\svalue="(?\w+)"/.exec(initialPageContent)?.groups?.token; + + const cookies = initialPageResponse.headers.get('set-cookie')?.split(' '); + if (token === undefined || cookies === undefined || cookies.length < 11) return null; + + const requestBody = new URLSearchParams(); + requestBody.set('_token', token); + requestBody.set('gamertag', username); + + const resultPageResponse = await fetch('https://cxkes.me/xbox/xuid', { + method: 'post', + body: requestBody, + // prettier-ignore + headers: { + 'Host': 'www.cxkes.me', + 'Accept-Encoding': 'gzip, deflate,br', + 'Content-Length': Buffer.byteLength(requestBody.toString()).toString(), + 'Origin': 'https://www.cxkes.me', + 'DNT': '1', + 'Connection': 'keep-alive', + 'Referer': 'https://www.cxkes.me/xbox/xuid', + 'Cookie': `${cookies[0]} ${cookies[10].slice(0, cookies[10].length - 1)}`, + 'Upgrade-Insecure-Requests': '1', + 'Sec-Fectch-Dest': 'document', + 'Sec-Fetch-Mode': 'navigate', + 'Sec-Fetch-Site': 'same-origin', + 'Sec-Fetch-User': '?1', + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/115.0', + 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8', + 'Accept-Language': 'en-US,es;q=0.8,en-US;q=0.5,en;q=0.3', + 'Content-Type': 'application/x-www-form-urlencoded' + } + }); + const resultPageContent = await resultPageResponse.text(); + let xuid: string | undefined; + if ((xuid = /id="xuidHex">(?\w+)