feat!: move to single route application for kiosk mode
continuous-integration/drone/push Build is passing Details

This commit is contained in:
Philipp Dormann 2023-04-19 18:02:33 +02:00
parent 58830c5db3
commit bf8b351b64
Signed by: philipp
GPG Key ID: 3BB9ADD52DCA4314
7 changed files with 203 additions and 249 deletions

View File

@ -2,93 +2,72 @@ import { AuthService, OpenAPI, type ResponseAuth } from '@odit/lfk-client-js';
import { writable } from 'svelte/store';
type UserState = {
access_token: string;
refresh_token: string;
isLoggedIn: boolean;
refreshInterval: NodeJS.Timer | undefined
access_token: string;
refresh_token: string;
isLoggedIn: boolean;
refreshInterval: NodeJS.Timer | undefined;
};
const userStore = () => {
const state: UserState = {
access_token: '',
refresh_token: '',
isLoggedIn: false,
refreshInterval: undefined
};
const { subscribe, set, update } = writable(state);
const methods = {
async login(resAuth: ResponseAuth) {
update((state: UserState) => {
if (!resAuth) {
return state;
}
state.access_token = resAuth.access_token;
state.refresh_token = resAuth.refresh_token;
state.isLoggedIn = true;
state.refreshInterval = setInterval(() => {
this.refreshAuth();
}, 2 * 60000)
localStorage.setItem('kiosk-userdata', JSON.stringify(state));
localStorage.setItem('kiosk-access_token', state.access_token);
OpenAPI.TOKEN = resAuth.access_token;
return state;
});
},
async refreshAuth() {
try {
const authRes = await AuthService.authControllerRefresh({ token: state.refresh_token }) as ResponseAuth;
OpenAPI.TOKEN = authRes.access_token;
} catch {
this.logout();
}
},
async loginFromStorage() {
console.log('loginFromStorage');
const access_token = localStorage.getItem('kiosk-access_token');
if (!access_token) {
throw new Error('Unauthorized');
}
const storagedata = localStorage.getItem('kiosk-userdata');
const userdata = JSON.parse(storagedata || '{}') as UserState;
update((state: UserState) => {
state.access_token = access_token;
state.refresh_token = userdata.refresh_token;
state.isLoggedIn = true;
state.refreshInterval = setInterval(() => {
this.refreshAuth();
}, 2 * 60000);
OpenAPI.TOKEN = userdata.access_token;
return state;
});
await this.refreshAuth();
},
async logout() {
update((state: UserState) => {
state.isLoggedIn = false;
state.access_token = '';
state.refresh_token = '';
state.refreshInterval = undefined;
localStorage.clear();
return state;
});
}
};
return {
subscribe,
set,
update,
state,
...methods
};
const state: UserState = {
access_token: '',
refresh_token: '',
isLoggedIn: false,
refreshInterval: undefined
};
const userState = writable(state);
async function auth_login(resAuth: ResponseAuth) {
if (!resAuth) {
return state;
}
export default userStore();
state.access_token = resAuth.access_token;
state.refresh_token = resAuth.refresh_token;
state.isLoggedIn = true;
state.refreshInterval = setInterval(() => {
refreshAuth();
}, 2 * 60000);
localStorage.setItem('kiosk-userdata', JSON.stringify(state));
localStorage.setItem('kiosk-access_token', state.access_token);
OpenAPI.TOKEN = resAuth.access_token;
userState.set(state);
return state;
}
async function refreshAuth() {
try {
const authRes = (await AuthService.authControllerRefresh({
token: state.refresh_token
})) as ResponseAuth;
OpenAPI.TOKEN = authRes.access_token;
} catch {
logout();
}
}
async function loginFromStorage() {
const access_token = localStorage.getItem('kiosk-access_token');
if (!access_token) {
throw new Error('Unauthorized');
}
const storagedata = localStorage.getItem('kiosk-userdata');
const userdata = JSON.parse(storagedata || '{}') as UserState;
state.access_token = access_token;
state.refresh_token = userdata.refresh_token;
state.isLoggedIn = true;
state.refreshInterval = setInterval(() => {
refreshAuth();
}, 2 * 60000);
OpenAPI.TOKEN = userdata.access_token;
userState.set(state);
return state;
}
async function logout() {
state.isLoggedIn = false;
state.access_token = '';
state.refresh_token = '';
state.refreshInterval = undefined;
localStorage.clear();
return state;
}
export { auth_login, logout, loginFromStorage, refreshAuth, userState };

View File

@ -2,10 +2,20 @@
import { OpenAPI } from '@odit/lfk-client-js';
import { env } from '$env/dynamic/public';
import '../app.postcss';
import "@fontsource/athiti"
import Footer from '../components/footer.svelte';
import '@fontsource/athiti';
import { onMount } from 'svelte';
OpenAPI.BASE = env.PUBLIC_BASE_URL || 'https://run.lauf-fuer-kaya.de';
onMount(() => {
window.addEventListener('keydown', (e) => {
if(e.altKey) e.preventDefault();
// console.log(e);
// const { key, altKey } = e;
// if (key === 'F4' && altKey) {
// e.preventDefault();
// }
});
});
</script>
<div class="dark:bg-slate-900 flex flex-col h-screen">

View File

@ -1,41 +1,128 @@
<!-- Hero -->
<div
class="relative overflow-hidden before:absolute before:top-0 before:left-1/2 before:bg-[url('/assets/polygon.svg')] before:bg-no-repeat before:bg-top before:bg-cover before:w-full before:h-full before:-z-[1] before:transform before:-translate-x-1/2 dark:before:bg-[url('/assets/polygon-dark.svg')]"
>
<div class="max-w-[85rem] mx-auto px-4 sm:px-6 lg:px-8 pt-24 pb-10">
<!-- Title -->
<div class="mt-5 max-w-2xl text-center mx-auto">
<h1 class="block font-bold text-gray-800 text-4xl md:text-5xl lg:text-6xl dark:text-gray-200">
LfK! Selfservice
<span class="bg-clip-text bg-gradient-to-tl from-blue-600 to-violet-600 text-transparent"
>Kiosk</span
<script lang="ts">
import { auth_login, loginFromStorage, userState } from '$lib/userstore';
import { AuthService } from '@odit/lfk-client-js';
import Register from './Register.svelte';
import { onMount } from 'svelte';
$: username = '';
$: password = '';
$: loginError = false;
onMount(() => {
loginFromStorage();
});
async function login() {
try {
const auth = (await AuthService.authControllerLogin({
username,
password
})) as import('@odit/lfk-client-js').ResponseAuth;
loginError = false;
auth_login(auth);
} catch (error) {
loginError = true;
}
}
</script>
{#if $userState.isLoggedIn}
<Register />
{:else}
<div
class="relative overflow-hidden before:absolute before:top-0 before:left-1/2 before:bg-[url('/assets/polygon.svg')] before:bg-no-repeat before:bg-top before:bg-cover before:w-full before:h-full before:-z-[1] before:transform before:-translate-x-1/2 dark:before:bg-[url('/assets/polygon-dark.svg')]"
>
<div class="max-w-[85rem] mx-auto px-4 sm:px-6 lg:px-8 pt-24 pb-10">
<!-- Title -->
<div class="mt-5 max-w-2xl text-center mx-auto">
<h1
class="block font-bold text-gray-800 text-4xl md:text-5xl lg:text-6xl dark:text-gray-200"
>
</h1>
</div>
<!-- End Title -->
LfK! Selfservice
<span class="bg-clip-text bg-gradient-to-tl from-blue-600 to-violet-600 text-transparent"
>Kiosk</span
>
</h1>
</div>
<!-- End Title -->
<div class="mt-5 max-w-3xl text-center mx-auto">
<p class="text-lg text-gray-600 dark:text-gray-400">Für die Anmeldung vor Ort</p>
</div>
<div class="mt-5 max-w-3xl text-center mx-auto">
<p class="text-lg text-gray-600 dark:text-gray-400">Für die Anmeldung vor Ort</p>
</div>
<div class="w-full max-w-md mx-auto p-6">
<div
class="mt-7 bg-white border border-gray-200 rounded-xl shadow-sm dark:bg-gray-800 dark:border-gray-700"
>
<div class="p-4 sm:p-7">
<div class="text-center mb-8">
<h1 class="block text-2xl font-bold text-gray-800 dark:text-white">Anmeldung</h1>
<p class="mt-2 text-sm text-gray-600 dark:text-gray-400">
Hierfür wird ein LfK Läufersystem Account benötigt
</p>
</div>
<!-- Buttons -->
<div class="mt-8 grid gap-3 w-full sm:inline-flex sm:justify-center">
<a
class="inline-flex justify-center items-center gap-x-3 text-center bg-gradient-to-tl from-blue-600 to-violet-600 hover:from-violet-600 hover:to-blue-600 border border-transparent text-white text-sm font-medium rounded-md focus:outline-none focus:ring-2 focus:ring-blue-600 focus:ring-offset-2 focus:ring-offset-white py-3 px-4 dark:focus:ring-offset-gray-800"
href="./login"
>
Kiosk starten
<svg class="w-3 h-3" width="16" height="16" viewBox="0 0 16 16" fill="none">
<path
d="M5.27921 2L10.9257 7.64645C11.1209 7.84171 11.1209 8.15829 10.9257 8.35355L5.27921 14"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
/>
</svg>
</a>
{#if loginError}
<div
class="bg-red-500 text-sm text-white text-center font-semibold rounded-md shadow-lg mb-8"
role="alert"
>
<div class="p-4">Falscher Nutzername oder falsches Passwort</div>
</div>
{/if}
<!-- Form -->
<form on:submit|preventDefault={login}>
<div class="grid gap-y-4">
<!-- Form Group -->
<div>
<label for="username" class="block text-sm mb-2 dark:text-white"
>Benutzername</label
>
<div class="relative">
<input
bind:value={username}
type="username"
id="username"
name="username"
class="py-3 px-4 block w-full border-gray-200 rounded-md text-sm focus:border-blue-500 focus:ring-blue-500 dark:bg-gray-800 dark:border-gray-700 dark:text-gray-400 border"
required
aria-describedby="username-error"
/>
</div>
</div>
<!-- End Form Group -->
<!-- Form Group -->
<div>
<div class="flex justify-between items-center">
<label for="password" class="block text-sm mb-2 dark:text-white">Passwort</label
>
</div>
<div class="relative">
<input
bind:value={password}
type="password"
id="password"
name="password"
class="py-3 px-4 block w-full border-gray-200 rounded-md text-sm focus:border-blue-500 focus:ring-blue-500 dark:bg-gray-800 dark:border-gray-700 dark:text-gray-400 border"
required
aria-describedby="password-error"
/>
</div>
</div>
<!-- End Form Group -->
<button
type="submit"
class="py-3 px-4 inline-flex justify-center items-center gap-2 rounded-md border border-transparent font-semibold bg-blue-500 text-white hover:bg-blue-600 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 transition-all text-sm dark:focus:ring-offset-gray-800"
>Anmelden</button
>
</div>
</form>
<!-- End Form -->
</div>
</div>
</div>
</div>
<!-- End Buttons -->
</div>
</div>
<!-- End Hero -->
{/if}

View File

@ -8,7 +8,6 @@
$: lastname = '';
$: doneButtonEnabled = false;
$: showResult = false;
$: showError = false;
let response: ResponseRunner;
const group = 1; //Default to Bürgerlauf
@ -37,7 +36,6 @@
lastname,
group
})) as ResponseRunner;
showError = false;
showResult = true;
setTimeout(() => {
doneButtonEnabled = true;
@ -47,8 +45,9 @@
}, 7500);
} catch (error) {
console.log(error);
showError = true;
showResult = false;
localStorage.clear();
location.reload();
}
firstname = '';
lastname = '';

View File

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 36 KiB

View File

@ -1,95 +0,0 @@
<script lang="ts">
import { goto } from '$app/navigation';
import userstore from '$lib/userstore';
import { AuthService } from '@odit/lfk-client-js';
$: username = '';
$: password = '';
$: loginError = false;
async function login() {
try {
const auth = (await AuthService.authControllerLogin({
username,
password
})) as import('@odit/lfk-client-js').ResponseAuth;
loginError=false;
await userstore.login(auth);
goto('./registration', {replaceState: true})
} catch (error) {
console.log(error);
loginError = true;
}
}
</script>
<div class="dark:bg-slate-900 bg-gray-100 flex h-full items-center py-16">
<div class="w-full max-w-md mx-auto p-6">
<div
class="mt-7 bg-white border border-gray-200 rounded-xl shadow-sm dark:bg-gray-800 dark:border-gray-700"
>
<div class="p-4 sm:p-7">
<div class="text-center mb-8">
<h1 class="block text-2xl font-bold text-gray-800 dark:text-white">Anmeldung</h1>
<p class="mt-2 text-sm text-gray-600 dark:text-gray-400">
Hierfür wird ein LfK Läufersystem Account benötigt
</p>
</div>
{#if loginError}
<div class="bg-red-500 text-sm text-white text-center font-semibold rounded-md shadow-lg mb-8" role="alert">
<div class="p-4">Falscher Nutzername oder falsches Passwort</div>
</div>
{/if}
<!-- Form -->
<form on:submit|preventDefault={login}>
<div class="grid gap-y-4">
<!-- Form Group -->
<div>
<label for="username" class="block text-sm mb-2 dark:text-white">Benutzername</label>
<div class="relative">
<input
bind:value={username}
type="username"
id="username"
name="username"
class="py-3 px-4 block w-full border-gray-200 rounded-md text-sm focus:border-blue-500 focus:ring-blue-500 dark:bg-gray-800 dark:border-gray-700 dark:text-gray-400 border"
required
aria-describedby="username-error"
/>
</div>
</div>
<!-- End Form Group -->
<!-- Form Group -->
<div>
<div class="flex justify-between items-center">
<label for="password" class="block text-sm mb-2 dark:text-white">Passwort</label>
</div>
<div class="relative">
<input
bind:value={password}
type="password"
id="password"
name="password"
class="py-3 px-4 block w-full border-gray-200 rounded-md text-sm focus:border-blue-500 focus:ring-blue-500 dark:bg-gray-800 dark:border-gray-700 dark:text-gray-400 border"
required
aria-describedby="password-error"
/>
</div>
</div>
<!-- End Form Group -->
<button
type="submit"
class="py-3 px-4 inline-flex justify-center items-center gap-2 rounded-md border border-transparent font-semibold bg-blue-500 text-white hover:bg-blue-600 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 transition-all text-sm dark:focus:ring-offset-gray-800"
>Anmelden</button
>
</div>
</form>
<!-- End Form -->
</div>
</div>
</div>
</div>

View File

@ -1,26 +0,0 @@
<script lang="ts">
import { goto } from '$app/navigation';
import userstore from '$lib/userstore';
import { onMount } from 'svelte';
$: loginRendered = false;
onMount(async () => {
try {
await userstore.loginFromStorage();
loginRendered = true;
} catch (error) {
console.log(error);
userstore.logout();
goto('./login', {replaceState: true})
}
});
</script>
{#if loginRendered}
<slot />
{:else}
<div class="mx-auto mt-20">
<h1 class="block text-center text-2xl font-bold text-gray-800 dark:text-white">Lade Anmeldedaten...</h1>
</div>
{/if}