add frontend and swap whole stack to sveltekit/nodejs

This commit is contained in:
2023-08-10 13:36:04 +02:00
parent d423c41ec7
commit ad90b440f5
124 changed files with 6665 additions and 11046 deletions

View File

@@ -0,0 +1,3 @@
<div class="flex justify-center w-full">
<slot />
</div>

View 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>

View 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;

View 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>

View 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>