<template> <div class="grow flex items-center justify-center py-12 px-4 sm:px-6 lg:px-8"> <div class="max-w-md w-full space-y-8 pb-20"> <div class="flex flex-col items-center gap-4"> <AppLogo /> <h2 class="text-center text-4xl font-extrabold text-gray-900"> {{ clubName }} </h2> </div> <form class="flex flex-col gap-2" @submit.prevent="submit"> <div class="-space-y-px"> <div class="relative"> <input id="username" name="username" type="text" required placeholder="Benutzer" :class="routine == '' ? '' : 'rounded-b-none!'" :value="username" :disabled="username != ''" /> <div v-if="usernameStatus" class="h-full flex items-center justify-center w-5 absolute top-0 right-2"> <Spinner v-if="usernameStatus == 'loading'" class="my-auto" /> <SuccessCheckmark v-else-if="usernameStatus == 'success'" /> <FailureXMark v-else-if="usernameStatus == 'failed'" /> </div> </div> <div v-if="routine != ''"> <input v-if="routine == 'totp'" id="totp" name="totp" type="text" required placeholder="TOTP" class="rounded-t-none!" autocomplete="off" /> <input v-else id="password" name="password" type="password" required placeholder="Passwort" class="rounded-t-none!" autocomplete="current-password" /> </div> </div> <p v-if="username != ''" class="w-fit self-end text-primary cursor-pointer" @click="resetRoutine"> Benutzer wechseln </p> <RouterLink :to="{ name: 'reset-start' }" class="w-fit self-end text-primary">Zugang verloren</RouterLink> <div class="flex flex-row gap-2"> <button type="submit" primary :disabled="loginStatus == 'loading' || loginStatus == 'success'"> {{ routine == "" ? "Benutzer prüfen" : "anmelden" }} </button> <Spinner v-if="loginStatus == 'loading'" class="my-auto" /> <SuccessCheckmark v-else-if="loginStatus == 'success'" /> <FailureXMark v-else-if="loginStatus == 'failed'" /> </div> <p v-if="loginError" class="text-center">{{ loginError }}</p> </form> <FormBottomBar /> </div> </div> </template> <script setup lang="ts"> import { defineComponent } from "vue"; import Spinner from "@/components/Spinner.vue"; import SuccessCheckmark from "@/components/SuccessCheckmark.vue"; import FailureXMark from "@/components/FailureXMark.vue"; import { resetAllPiniaStores } from "@/helpers/piniaReset"; import FormBottomBar from "@/components/FormBottomBar.vue"; import AppLogo from "@/components/AppLogo.vue"; import { mapState } from "pinia"; import { useConfigurationStore } from "@/stores/configuration"; import { hashString } from "../helpers/crypto"; </script> <script lang="ts"> export default defineComponent({ data() { return { loginStatus: undefined as undefined | "loading" | "success" | "failed", usernameStatus: undefined as undefined | "loading" | "success" | "failed", loginError: "" as string, username: "" as string, routine: "" as string, }; }, computed: { ...mapState(useConfigurationStore, ["clubName"]), }, mounted() { resetAllPiniaStores(); this.username = localStorage.getItem("username") ?? ""; this.routine = localStorage.getItem("routine") ?? ""; if (this.username != "") { this.$http .post(`/auth/kickof`, { username: this.username, }) .then((result) => { this.usernameStatus = "success"; this.routine = result.data.routine; localStorage.setItem("routine", result.data.routine); }) .catch((err) => { this.usernameStatus = "failed"; this.loginError = err.response?.data; }); } }, methods: { resetRoutine() { this.routine = ""; this.username = ""; localStorage.removeItem("routine"); localStorage.removeItem("username"); }, submit(e: any) { if (this.routine == "") this.kickof(e); else this.login(e); }, kickof(e: any) { let formData = e.target.elements; let username = formData.username.value; this.usernameStatus = "loading"; this.loginError = ""; this.$http .post(`/auth/kickof`, { username: username, }) .then((result) => { this.usernameStatus = "success"; this.routine = result.data.routine; this.username = username; localStorage.setItem("routine", result.data.routine); localStorage.setItem("username", username); }) .catch((err) => { this.usernameStatus = "failed"; this.loginError = err.response?.data; }) .finally(() => { setTimeout(() => { this.usernameStatus = undefined; this.loginError = ""; }, 2000); }); }, async login(e: any) { let formData = e.target.elements; this.loginStatus = "loading"; this.loginError = ""; let secret = ""; if (this.routine == "totp") { secret = formData.totp.value; } else { secret = await hashString(formData.password.value); } this.$http .post(`/auth/login`, { username: formData.username.value, secret: secret, }) .then((result) => { this.loginStatus = "success"; localStorage.setItem("accessToken", result.data.accessToken); localStorage.setItem("refreshToken", result.data.refreshToken); setTimeout(() => { this.$router.push(`/admin`); }, 1000); }) .catch((err) => { this.loginStatus = "failed"; this.loginError = err.response?.data; }); }, }, }); </script>