use zod for register verification

This commit is contained in:
bytedream 2024-10-19 15:27:29 +02:00
parent 18135a0816
commit aa91eaf82a
5 changed files with 58 additions and 47 deletions

13
package-lock.json generated
View File

@ -44,7 +44,8 @@
"tslib": "^2.8.0", "tslib": "^2.8.0",
"typescript": "^5.6.3", "typescript": "^5.6.3",
"vite": "^5.4.9", "vite": "^5.4.9",
"vitest": "^2.1.3" "vitest": "^2.1.3",
"zod": "^3.23.8"
} }
}, },
"node_modules/@alloc/quick-lru": { "node_modules/@alloc/quick-lru": {
@ -7618,6 +7619,16 @@
"funding": { "funding": {
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
},
"node_modules/zod": {
"version": "3.23.8",
"resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz",
"integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==",
"dev": true,
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/colinhacks"
}
} }
} }
} }

View File

@ -41,7 +41,8 @@
"tslib": "^2.8.0", "tslib": "^2.8.0",
"typescript": "^5.6.3", "typescript": "^5.6.3",
"vite": "^5.4.9", "vite": "^5.4.9",
"vitest": "^2.1.3" "vitest": "^2.1.3",
"zod": "^3.23.8"
}, },
"type": "module", "type": "module",
"dependencies": { "dependencies": {

View File

@ -1,6 +1,7 @@
import { ApiError, getJavaUuid, getNoAuthUuid, UserNotFoundError } from '$lib/server/minecraft'; import { ApiError, getJavaUuid, getNoAuthUuid, UserNotFoundError } from '$lib/server/minecraft';
import { error, type RequestHandler } from '@sveltejs/kit'; import { error, type RequestHandler } from '@sveltejs/kit';
import { Settings, User } from '$lib/server/database'; import { Settings, User } from '$lib/server/database';
import { RegisterSchema } from './schema';
export const POST = (async ({ request }) => { export const POST = (async ({ request }) => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment
@ -9,48 +10,33 @@ export const POST = (async ({ request }) => {
throw error(400, 'Anmeldung geschlossen'); throw error(400, 'Anmeldung geschlossen');
} }
const data = await request.formData(); try {
const firstname = data.get('firstname') as string | null; var data = RegisterSchema.parse(await request.json());
const lastname = data.get('lastname') as string | null; } catch (e) {
const birthday: number = Date.parse(data.get('birthday') as string); console.error(e);
const telephone = data.get('telephone') as string | null;
const username = data.get('username') as string | null;
const playertype = data.get('playertype') as string | null;
const password = data.get('password') as string | null;
if (
firstname == null ||
lastname == null ||
Number.isNaN(birthday) ||
username == null ||
playertype == null
) {
throw error(400, 'Ungültige Parameter'); throw error(400, 'Ungültige Parameter');
} }
let uuid: string | null; let uuid: string | null;
try { try {
// available playertypes are 'java', 'bedrock' and 'noauth' switch (data.playertype) {
switch (data.get('playertype')) {
case 'java': case 'java':
uuid = await getJavaUuid(username); uuid = await getJavaUuid(data.username);
break; break;
case 'bedrock': case 'bedrock':
uuid = null; uuid = null;
// uuid = await getBedrockUuid(username); // uuid = await getBedrockUuid(username);
break; break;
case 'noauth': case 'noauth':
uuid = getNoAuthUuid(username); uuid = getNoAuthUuid(data.username);
break; break;
default:
throw new Error(`invalid player type (${playertype})`);
} }
} catch (e) { } catch (e) {
if (e instanceof UserNotFoundError) { if (e instanceof UserNotFoundError) {
throw error( throw error(
400, 400,
"Der Spielername '" + "Der Spielername '" +
username + data.username +
"' existiert nicht. Hast Du Deinen Spielernamen korrekt geschrieben " + "' existiert nicht. Hast Du Deinen Spielernamen korrekt geschrieben " +
'und besitzt Du einen Minecraft-Account?\n\nKontaktiere bitte einen Admin, falls Du Dich trotz korrekter ' + 'und besitzt Du einen Minecraft-Account?\n\nKontaktiere bitte einen Admin, falls Du Dich trotz korrekter ' +
'Angabe nicht registrieren kannst.' 'Angabe nicht registrieren kannst.'
@ -74,9 +60,9 @@ export const POST = (async ({ request }) => {
} else if ( } else if (
await User.findOne({ await User.findOne({
where: { where: {
firstname: firstname, firstname: data.firstname,
lastname: lastname, lastname: data.lastname,
birthday: new Date(birthday).toUTCString() birthday: data.birthday.toUTCString()
} }
}) })
) { ) {
@ -89,13 +75,13 @@ export const POST = (async ({ request }) => {
} }
await User.create({ await User.create({
firstname: firstname, firstname: data.firstname,
lastname: lastname, lastname: data.lastname,
birthday: new Date(birthday).toUTCString(), birthday: data.birthday.toUTCString(),
telephone: telephone, telephone: data.telephone,
username: username, username: data.username,
playertype: playertype, playertype: data.playertype,
password: password, password: null,
uuid: uuid uuid: uuid
}); });

View File

@ -4,6 +4,7 @@
import { createEventDispatcher, onMount } from 'svelte'; import { createEventDispatcher, onMount } from 'svelte';
import { env } from '$env/dynamic/public'; import { env } from '$env/dynamic/public';
import { rulesShort } from '$lib/rules'; import { rulesShort } from '$lib/rules';
import { RegisterSchema } from './schema';
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher();
@ -40,21 +41,18 @@
async function sendRegister() { async function sendRegister() {
// eslint-disable-next-line no-async-promise-executor // eslint-disable-next-line no-async-promise-executor
registerRequest = new Promise<void>(async (resolve, reject) => { registerRequest = new Promise<void>(async (resolve, reject) => {
if ( const parseResult = RegisterSchema.safeParse(
(birthdayInput.valueAsDate || new Date()) > Object.fromEntries(new FormData(document.forms[0]))
new Date(Date.now() - 1000 * 60 * 60 * 24 * 365 * 6) );
) {
reject( if (!parseResult.success) {
Error( reject(Error(parseResult.error.issues.map((i) => i.message).join('\n\n')));
'Bitte gib Deinen vollständigen Geburtstag und die korrekte Jahreszahl an. Du musst mindestens 6 Jahre alt sein.\n'
)
);
return; return;
} }
const response = await fetch(`${env.PUBLIC_BASE_PATH}/register`, { const response = await fetch(`${env.PUBLIC_BASE_PATH}/register`, {
method: 'POST', method: 'POST',
body: new FormData(document.forms[0]) body: JSON.stringify(Object.fromEntries(new FormData(document.forms[0])))
}); });
if (response.ok) { if (response.ok) {
dispatch('submit', { dispatch('submit', {
@ -80,7 +78,7 @@
let rulesAccepted = false; let rulesAccepted = false;
let rulesModal: HTMLDialogElement; let rulesModal: HTMLDialogElement;
let rulesModalSecondsOpened = 0; let rulesModalSecondsOpened = 0;
let rulesModalTimer: number | null = null; let rulesModalTimer: number | NodeJS.Timeout | undefined = undefined;
let inputsInvalidMessage: string | null = 'Bitte fülle alle erforderlichen Felder aus'; let inputsInvalidMessage: string | null = 'Bitte fülle alle erforderlichen Felder aus';
let registerRequest: Promise<void> | null = null; let registerRequest: Promise<void> | null = null;
@ -248,7 +246,7 @@
class="modal" class="modal"
on:close={() => { on:close={() => {
clearInterval(rulesModalTimer); clearInterval(rulesModalTimer);
rulesModalTimer = null; rulesModalTimer = undefined;
}} }}
bind:this={rulesModal} bind:this={rulesModal}
> >

View File

@ -0,0 +1,15 @@
import { z } from 'zod';
export const RegisterSchema = z.object({
firstname: z.string(),
lastname: z.string(),
birthday: z.coerce
.date()
.max(
new Date(Date.now() - 1000 * 60 * 60 * 24 * 365 * 6),
'Bitte gib Deinen vollständigen Geburtstag und die korrekte Jahreszahl an. Du musst mindestens 6 Jahre alt sein.'
),
telephone: z.string().optional(),
username: z.string(),
playertype: z.enum(['java', 'bedrock', 'noauth'])
});