add frontend and swap whole stack to sveltekit/nodejs
This commit is contained in:
3
src/routes/register/+layout.svelte
Normal file
3
src/routes/register/+layout.svelte
Normal file
@@ -0,0 +1,3 @@
|
||||
<div class="flex justify-center w-full">
|
||||
<slot />
|
||||
</div>
|
||||
23
src/routes/register/+page.svelte
Normal file
23
src/routes/register/+page.svelte
Normal file
@@ -0,0 +1,23 @@
|
||||
<script lang="ts">
|
||||
import { fly } from 'svelte/transition';
|
||||
import RegistrationComplete from './RegistrationComplete.svelte';
|
||||
import Register from './Register.svelte';
|
||||
|
||||
let registered = false;
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>Craftattack - Anmeldung</title>
|
||||
</svelte:head>
|
||||
|
||||
<div class="absolute top-12 grid card w-11/12 xl:w-2/3 2xl:w-1/2 p-6 shadow-lg overflow-hidden">
|
||||
{#if !registered}
|
||||
<div class="col-[1] row-[1]" transition:fly={{ x: -200, duration: 300 }}>
|
||||
<Register on:submit={() => (registered = true)} />
|
||||
</div>
|
||||
{:else}
|
||||
<div class="col-[1] row-[1]" transition:fly={{ x: 200, duration: 300 }}>
|
||||
<RegistrationComplete on:close={() => (registered = false)} />
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
49
src/routes/register/+server.ts
Normal file
49
src/routes/register/+server.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import {
|
||||
getBedrockUuid,
|
||||
getCrackedUuid,
|
||||
getJavaUuid,
|
||||
UserNotFoundError
|
||||
} from '$lib/server/minecraft';
|
||||
import { error, type RequestHandler } from '@sveltejs/kit';
|
||||
import { User } from '$lib/server/database';
|
||||
|
||||
export const POST = (async ({ request }) => {
|
||||
const data = await request.formData();
|
||||
|
||||
let uuid: string;
|
||||
try {
|
||||
// available playertypes are 'java', 'bedrock' and 'cracked'
|
||||
switch (data.get('playertype')) {
|
||||
case 'java':
|
||||
uuid = await getJavaUuid(data.get('username') as string);
|
||||
break;
|
||||
case 'bedrock':
|
||||
uuid = await getBedrockUuid(data.get('username') as string);
|
||||
break;
|
||||
case 'cracked':
|
||||
uuid = getCrackedUuid(data.get('username') as string);
|
||||
break;
|
||||
default:
|
||||
throw new Error(`invalid player type (${data.get('playertype')})`);
|
||||
}
|
||||
} catch (e) {
|
||||
if (e instanceof UserNotFoundError) {
|
||||
throw error(400, e.message);
|
||||
}
|
||||
console.error((e as Error).message);
|
||||
return new Response();
|
||||
}
|
||||
|
||||
await User.create({
|
||||
firstname: data.get('firstname'),
|
||||
lastname: data.get('lastname'),
|
||||
birthday: data.get('birthday'),
|
||||
telephone: data.get('telephone'),
|
||||
username: data.get('username'),
|
||||
playertype: data.get('playertype'),
|
||||
password: data.get('password'),
|
||||
uuid: uuid
|
||||
});
|
||||
|
||||
return new Response();
|
||||
}) satisfies RequestHandler;
|
||||
221
src/routes/register/Register.svelte
Normal file
221
src/routes/register/Register.svelte
Normal file
@@ -0,0 +1,221 @@
|
||||
<script lang="ts">
|
||||
import Select from '$lib/components/Input/Select.svelte';
|
||||
import Input from '$lib/components/Input/Input.svelte';
|
||||
import { createEventDispatcher, onMount } from 'svelte';
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||
let checkInputs = () => {};
|
||||
let playertype = 'java';
|
||||
let firstnameInput: HTMLInputElement;
|
||||
let lastnameInput: HTMLInputElement;
|
||||
let birthdayInput: HTMLInputElement;
|
||||
let usernameInput: HTMLInputElement;
|
||||
let privacyInput: HTMLInputElement;
|
||||
let logsInput: HTMLInputElement;
|
||||
let rulesInput: HTMLInputElement;
|
||||
onMount(() => {
|
||||
checkInputs = () => {
|
||||
let allInputs = [
|
||||
firstnameInput,
|
||||
lastnameInput,
|
||||
birthdayInput,
|
||||
usernameInput,
|
||||
privacyInput,
|
||||
logsInput,
|
||||
rulesInput
|
||||
];
|
||||
if (!allInputs.every((v) => v.value || v.checked)) {
|
||||
inputsInvalidMessage = 'Bitte fülle alle erforderlichen Felder aus';
|
||||
} else {
|
||||
inputsInvalidMessage = null;
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
async function sendRegister() {
|
||||
// eslint-disable-next-line no-async-promise-executor
|
||||
registerRequest = new Promise(async (resolve, reject) => {
|
||||
const response = await fetch('/register', {
|
||||
method: 'POST',
|
||||
body: new FormData(document.forms[0])
|
||||
});
|
||||
if (response.ok) {
|
||||
dispatch('submit', {});
|
||||
resolve();
|
||||
} else if (response.status < 500) {
|
||||
reject(Error((await response.json()).message));
|
||||
} else {
|
||||
reject(Error(`${response.statusText} (${response.status})`));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let inputsInvalidMessage: string | null = 'Bitte fülle alle erforderlichen Felder aus';
|
||||
let registerRequest: Promise<void> | null = null;
|
||||
</script>
|
||||
|
||||
<h1 class="text-center text-3xl lg:text-5xl">Anmeldung</h1>
|
||||
<form id="form" on:input={checkInputs} on:submit|preventDefault={sendRegister}>
|
||||
<div class="divider">Persönliche Angaben</div>
|
||||
<div class="mx-2 grid grid-cols-1 sm:grid-cols-2 gap-y-4">
|
||||
<Input
|
||||
id="firstname"
|
||||
name="firstname"
|
||||
type="text"
|
||||
required={true}
|
||||
bind:inputElement={firstnameInput}
|
||||
>
|
||||
<span slot="label">Vorname</span>
|
||||
</Input>
|
||||
<Input
|
||||
id="lastname"
|
||||
name="lastname"
|
||||
type="text"
|
||||
required={true}
|
||||
bind:inputElement={lastnameInput}
|
||||
>
|
||||
<span slot="label">Nachname</span>
|
||||
</Input>
|
||||
<Input
|
||||
id="birthday"
|
||||
name="birthday"
|
||||
type="date"
|
||||
required={true}
|
||||
bind:inputElement={birthdayInput}
|
||||
>
|
||||
<span slot="label">Geburtstag</span>
|
||||
<span slot="notice">Die Angabe hat keine Auswirkungen auf das Spielgeschehen</span>
|
||||
</Input>
|
||||
<Input id="telephone" name="telephone" type="tel">
|
||||
<span slot="label">Telefonnummer</span>
|
||||
<p slot="notice">
|
||||
Diese nutzen wir, um Dich in der Whatsapp-Gruppe zuzuordnen und kontaktieren zu können.
|
||||
<br />
|
||||
<b>Die Angabe ist freiwillig, hilft den Administratoren jedoch sehr!</b>
|
||||
</p>
|
||||
</Input>
|
||||
</div>
|
||||
<div class="divider">Spiel</div>
|
||||
<div class="mx-2 grid grid-cols-1 sm:grid-cols-2 gap-y-4">
|
||||
<Input
|
||||
id="username"
|
||||
name="username"
|
||||
type="text"
|
||||
required={true}
|
||||
bind:inputElement={usernameInput}
|
||||
>
|
||||
<span slot="label">Minecraft-Spielername</span>
|
||||
</Input>
|
||||
<Select
|
||||
id="playertype"
|
||||
name="playertype"
|
||||
label="Edition"
|
||||
bind:value={playertype}
|
||||
required={true}
|
||||
>
|
||||
<option value="java">Java Edition</option>
|
||||
<option value="bedrock">Bedrock Edition</option>
|
||||
<option value="cracked">Java cracked</option>
|
||||
</Select>
|
||||
{#if playertype === 'cracked'}
|
||||
<div class="sm:col-span-2">
|
||||
<Input id="password" name="password" type="password" required={true}>
|
||||
<span slot="label">Passwort</span>
|
||||
<span slot="notice">
|
||||
Da Du cracked spielst, musst Du ein Passwort festlegen, mit welchem Du Dich auf dem
|
||||
Server authentifizierst! Das Passwort wird im Klartext gespeichert und ist in deinen
|
||||
Clientlogs sowie in Serverlogs für Admins sichtbar. Verwende daher ein neues Passwort,
|
||||
welches Du nirgends sonst verwendest! Merke Dir das Passwort gut, ohne kannst Du Dich
|
||||
nicht auf dem Server einloggen
|
||||
</span>
|
||||
</Input>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="divider" />
|
||||
<div class="mx-2 grid gap-y-3 mb-6">
|
||||
<div class="flex gap-4">
|
||||
<Input
|
||||
id="privacy"
|
||||
name="privacy"
|
||||
type="checkbox"
|
||||
required={true}
|
||||
bind:inputElement={privacyInput}
|
||||
/>
|
||||
<label for="privacy">
|
||||
<span>
|
||||
Ich bin mit der Speicherung meiner in der Anmeldung angegebenen, persönlichen Daten
|
||||
einverstanden. Siehe <a class="link" href="https://mhsl.eu/id.html">Datenschutz</a>
|
||||
</span>
|
||||
<span class="text-red-700">*</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="flex gap-4">
|
||||
<Input id="logs" name="logs" type="checkbox" required={true} bind:inputElement={logsInput} />
|
||||
<label for="logs">
|
||||
<span>
|
||||
Ich bin mit der Speicherung in Form von Logs aller meiner, beim Spielen anfallenden,
|
||||
persönlichen Daten durch den Server einverstanden
|
||||
</span>
|
||||
<span class="text-red-700">*</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="flex gap-4">
|
||||
<Input
|
||||
id="rules"
|
||||
name="rules"
|
||||
type="checkbox"
|
||||
required={true}
|
||||
bind:inputElement={rulesInput}
|
||||
/>
|
||||
<label for="rules">
|
||||
Ich bin mit den <a class="link" href="/rules">Regeln</a> einverstanden und achte sie
|
||||
<span class="text-red-700">*</span>
|
||||
<br />
|
||||
<p class="text-[.75rem]">
|
||||
Dies betrifft jede Interaktion im Spiel und zugehörige Daten wie z.B. Chatnachrichten
|
||||
welche vom Minecraft Client an den Server übermittelt werden
|
||||
</p>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class={inputsInvalidMessage !== null ? 'tooltip tooltip-top' : 'grid w-min'}
|
||||
data-tip={inputsInvalidMessage}
|
||||
>
|
||||
<div class="row-[1] col-[1]">
|
||||
<Input
|
||||
id="submit"
|
||||
type="submit"
|
||||
value="Anmeldung absenden"
|
||||
disabled={inputsInvalidMessage !== null || registerRequest !== null}
|
||||
/>
|
||||
</div>
|
||||
{#key registerRequest}
|
||||
{#if registerRequest}
|
||||
{#await registerRequest}
|
||||
<span
|
||||
class="relative top-[calc(50%-12px)] left-[calc(50%-12px)] row-[1] col-[1] loading loading-ring"
|
||||
/>
|
||||
{:catch error}
|
||||
<dialog
|
||||
class="modal"
|
||||
on:close={() => setTimeout(() => (registerRequest = null), 200)}
|
||||
open
|
||||
>
|
||||
<form method="dialog" class="modal-box">
|
||||
<button class="btn btn-sm btn-circle btn-ghost absolute right-2 top-2">✕</button>
|
||||
<h3 class="font-bold text-lg">Error</h3>
|
||||
<p class="py-4">{error.message}</p>
|
||||
</form>
|
||||
<form method="dialog" class="modal-backdrop bg-[rgba(0,0,0,.2)]">
|
||||
<button>close</button>
|
||||
</form>
|
||||
</dialog>
|
||||
{/await}
|
||||
{/if}
|
||||
{/key}
|
||||
</div>
|
||||
</form>
|
||||
32
src/routes/register/RegistrationComplete.svelte
Normal file
32
src/routes/register/RegistrationComplete.svelte
Normal file
@@ -0,0 +1,32 @@
|
||||
<script lang="ts">
|
||||
import { IconSolid } from 'svelte-heros-v2';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import { PUBLIC_START_DATE } from '$env/static/public';
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
let startDayOptions: Intl.DateTimeFormatOptions = {
|
||||
day: '2-digit',
|
||||
month: 'long',
|
||||
year: 'numeric'
|
||||
};
|
||||
let startTimeOptions: Intl.DateTimeFormatOptions = {
|
||||
hour: '2-digit',
|
||||
minute: '2-digit'
|
||||
};
|
||||
</script>
|
||||
|
||||
<div class="flex items-center h-12 mb-2">
|
||||
<button class="sm:absolute btn btn-sm btn-square" on:click={() => dispatch('close')}>
|
||||
<IconSolid name="chevron-left-solid" />
|
||||
</button>
|
||||
<h1 class="text-center text-xl sm:text-3xl m-auto">Registrierung erfolgreich</h1>
|
||||
</div>
|
||||
<p>
|
||||
<b>Du hast dich erfolgreich für Craftattack 6 registriert</b>. Spielstart ist am {new Date(
|
||||
PUBLIC_START_DATE
|
||||
).toLocaleString('de-DE', startDayOptions)} um {new Date(PUBLIC_START_DATE).toLocaleString(
|
||||
'de-DE',
|
||||
startTimeOptions
|
||||
)} Uhr.
|
||||
</p>
|
||||
Reference in New Issue
Block a user