refactor login page, change routes to token-based, add service classes
This commit is contained in:
32
src/lib/components/BaseInputField.svelte
Normal file
32
src/lib/components/BaseInputField.svelte
Normal file
@@ -0,0 +1,32 @@
|
||||
<script lang="ts">
|
||||
import Exclamation from '$lib/icons/Exclamation.svelte';
|
||||
|
||||
let { label = '', error = null, value, name, id, type = 'text'} = $props();
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<label for={name} class="block text-sm font-medium leading-6 text-gray-900"
|
||||
><span class="flex"
|
||||
>{#if error}
|
||||
<span class="inline-block mr-1"><Exclamation /></span>
|
||||
{/if}
|
||||
{label}</span
|
||||
></label
|
||||
>
|
||||
<div class="mt-2">
|
||||
<div
|
||||
class="flex rounded-md shadow-sm ring-1 ring-inset ring-gray-300 focus-within:ring-2 focus-within:ring-inset focus-within:ring-indigo-600"
|
||||
>
|
||||
<input
|
||||
class="block flex-1 border-0 bg-transparent py-1.5 pl-1 text-gray-900 placeholder:text-gray-400 focus:ring-0 text-sm leading-6"
|
||||
bind:value
|
||||
{type}
|
||||
{name}
|
||||
{id}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{#if error}
|
||||
<p class="block text-sm leading-6 text-red-900 mt-2">{error}</p>
|
||||
{/if}
|
||||
</div>
|
||||
@@ -14,8 +14,8 @@
|
||||
<script lang="ts">
|
||||
import { page } from '$app/stores';
|
||||
import Trash from '$lib/icons/Trash.svelte';
|
||||
import Panel from '$lib/components/ui/Panel.svelte';
|
||||
import Button from '$lib/components/ui/Button.svelte';
|
||||
import Panel from '$lib/components/Panel.svelte';
|
||||
import Button from '$lib/components/Button.svelte';
|
||||
import { clickOutside } from '$lib/helpers/clickOutside.js';
|
||||
const { adminMode, prediction, predictionRemove } = $page.data;
|
||||
|
||||
37
src/lib/components/Footer.svelte
Normal file
37
src/lib/components/Footer.svelte
Normal file
@@ -0,0 +1,37 @@
|
||||
<script>
|
||||
import Profile from "$lib/icons/Profile.svelte";
|
||||
|
||||
export let data;
|
||||
</script>
|
||||
|
||||
<div class="flex-none">
|
||||
<footer class="justify-end">
|
||||
<div class="bg-gray-100">
|
||||
<div class="mx-auto max-w-7xl px-6 lg:px-8">
|
||||
<div class="flex justify-between divide-x divide-gray-900/5 border-x border-gray-900/5">
|
||||
<a
|
||||
href="/list"
|
||||
class="px-4 py-1 -ml-4 flex items-center justify-center gap-x-2.5 text-sm font-semibold leading-6 text-gray-500 hover:bg-gray-200 hover:text-gray-700"
|
||||
>
|
||||
© 2023 Innovation Hub Niedersachen
|
||||
</a>
|
||||
<a
|
||||
href="/"
|
||||
class="px-4 py-1 flex items-center justify-center gap-x-2.5 text-sm font-semibold leading-6 text-gray-500 hover:bg-gray-200 hover:text-gray-700"
|
||||
>
|
||||
back
|
||||
</a>
|
||||
<a
|
||||
href="/"
|
||||
class="px-4 py-1 -mr-4 flex items-center justify-center gap-x-2.5 text-sm font-semibold leading-6 text-gray-500 hover:bg-gray-200 hover:text-gray-700"
|
||||
>
|
||||
<!--icon-->
|
||||
<Profile />
|
||||
|
||||
{data?.user?.id}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
36
src/lib/components/Header.svelte
Normal file
36
src/lib/components/Header.svelte
Normal file
@@ -0,0 +1,36 @@
|
||||
<script lang="ts">
|
||||
import Chevron from '$lib/icons/Chevron-right.svelte';
|
||||
|
||||
export let data;
|
||||
</script>
|
||||
|
||||
<div class="flex flex-col">
|
||||
<header class="flex-none relative isolate z-10 bg-white px-8">
|
||||
<nav
|
||||
class="mx-auto flex max-w-7xl items-center justify-between p-6 lg:px-8"
|
||||
aria-label="Global"
|
||||
>
|
||||
<div class="flex w-48">
|
||||
<a href="/" class="-m-1.5 p-1.5 w-10">
|
||||
<span class="sr-only">Tatort Niedersachen</span>
|
||||
<img class="h-8 w-auto" src="/Landeswappen_NI.svg" alt="Landeswappen Niedersachsen" />
|
||||
</a>
|
||||
</div>
|
||||
<h1 class="text-3xl text-slate-400 font-bold">Tatort</h1>
|
||||
<div class="lg:flex lg:justify-end w-48">
|
||||
{#if data.user}
|
||||
<form method="POST" action="/anmeldung?/logout">
|
||||
<input type="hidden" />
|
||||
<button type="submit" class="text-sm font-semibold leading-6 text-gray-900"
|
||||
><span
|
||||
><span class="align-middle inline-block">Abmelden</span><span
|
||||
class="align-middle inline-block"><Chevron /></span
|
||||
></span
|
||||
></button
|
||||
>
|
||||
</form>
|
||||
{/if}
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
</div>
|
||||
@@ -1,22 +0,0 @@
|
||||
import { client } from '$lib/minio';
|
||||
|
||||
/**
|
||||
* Check if caseNumber is used
|
||||
* @param {string} caseNumber
|
||||
* @returns {Promise<boolean>}
|
||||
*/
|
||||
export default async function caseNumberOccupied(caseNumber) {
|
||||
const prefix = `${caseNumber}`;
|
||||
const promise = new Promise((resolve) => {
|
||||
let stream = client.listObjectsV2('tatort', prefix, false, '');
|
||||
stream.on('data', () => {
|
||||
stream.destroy();
|
||||
resolve(true);
|
||||
});
|
||||
stream.on('end', () => {
|
||||
resolve(false);
|
||||
});
|
||||
});
|
||||
|
||||
return promise;
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import { client } from '$lib/minio';
|
||||
|
||||
export default async function caseNumberOccupied (caseNumber: string): Promise<boolean> {
|
||||
const prefix = `${caseNumber}/config.json`;
|
||||
const prefix = `${caseNumber}`;
|
||||
const promise: Promise<boolean> = new Promise((resolve) => {
|
||||
const stream = client.listObjectsV2('tatort', prefix, false, '');
|
||||
stream.on('data', () => {
|
||||
|
||||
29
src/lib/server/authService.ts
Normal file
29
src/lib/server/authService.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import { dev } from '$app/environment';
|
||||
import { fail, redirect, type Cookies, type RequestEvent } from '@sveltejs/kit';
|
||||
import { authenticate } from '$lib/auth';
|
||||
|
||||
const COOKIE_NAME = 'session';
|
||||
|
||||
export const loginUser = async ({ request, cookies }: { request: Request; cookies: Cookies }) => {
|
||||
const data = await request.formData();
|
||||
const user = data.get('user');
|
||||
const password = data.get('password');
|
||||
|
||||
const token = authenticate(user, password);
|
||||
|
||||
if (!token) return fail(400, { user, incorrect: true });
|
||||
|
||||
cookies.set(COOKIE_NAME, token, {
|
||||
path: '/',
|
||||
httpOnly: true,
|
||||
sameSite: 'strict',
|
||||
secure: !dev
|
||||
});
|
||||
return redirect(303, '/');
|
||||
};
|
||||
|
||||
export const logoutUser = async (event: RequestEvent) => {
|
||||
event.cookies.delete(COOKIE_NAME, { path: '/' });
|
||||
event.locals.user = null;
|
||||
return { success: true };
|
||||
};
|
||||
58
src/lib/server/s3ClientService.ts
Normal file
58
src/lib/server/s3ClientService.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
import { client } from '$lib/minio';
|
||||
|
||||
const BUCKET = 'tatort';
|
||||
|
||||
export const getVorgang = ({ params }) => {
|
||||
const prefix = params.vorgang ? `${params.vorgang}/` : '';
|
||||
const stream = client.listObjectsV2('tatort', prefix, false, '');
|
||||
const result = new ReadableStream({
|
||||
start(controller) {
|
||||
stream.on('data', (data) => {
|
||||
if (prefix === '') {
|
||||
if (data.prefix)
|
||||
controller.enqueue(`${JSON.stringify({ ...data, name: data.prefix.slice(0, -1) })}\n`);
|
||||
return;
|
||||
}
|
||||
|
||||
const name = data.name.slice(prefix.length);
|
||||
if (name === 'config.json') return;
|
||||
// zugangscode datei
|
||||
if (name === '__perm__') return;
|
||||
|
||||
controller.enqueue(`${JSON.stringify({ ...data, name, prefix })}\n`);
|
||||
});
|
||||
stream.on('end', () => {
|
||||
controller.close();
|
||||
});
|
||||
},
|
||||
cancel() {
|
||||
stream.destroy();
|
||||
}
|
||||
});
|
||||
|
||||
return new Response(result, {
|
||||
headers: {
|
||||
'content-type': 'text/event-stream'
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
export const checkIfExactDirectoryExists = (dir: string): Promise<boolean> => {
|
||||
return new Promise<boolean>((resolve, reject) => {
|
||||
const prefix = dir.endsWith('/') ? dir : `${dir}/`;
|
||||
|
||||
const stream = client.listObjectsV2(BUCKET, prefix, false, '');
|
||||
|
||||
stream.on('data', (obj) => {
|
||||
if (obj.prefix === undefined && obj.name.startsWith(prefix)) {
|
||||
stream.destroy();
|
||||
resolve(true);
|
||||
}
|
||||
});
|
||||
|
||||
stream.on('error', (err) => reject(err));
|
||||
|
||||
stream.on('end', () => resolve(false));
|
||||
});
|
||||
}
|
||||
68
src/lib/server/vorgangService.ts
Normal file
68
src/lib/server/vorgangService.ts
Normal file
@@ -0,0 +1,68 @@
|
||||
import { fail, redirect } from '@sveltejs/kit';
|
||||
import { client } from '$lib/minio';
|
||||
import { checkIfExactDirectoryExists } from './s3ClientService';
|
||||
|
||||
/**
|
||||
*
|
||||
* @param request
|
||||
* @returns
|
||||
*/
|
||||
export const getVorgangByCaseNumber = async ( request: Request) => {
|
||||
const data = await request.formData();
|
||||
const caseNumber = data.get('caseNumber');
|
||||
const user_token = data.get('token');
|
||||
|
||||
if (!caseNumber) {
|
||||
return fail(400, {
|
||||
success: false,
|
||||
caseNumber,
|
||||
error: { message: 'Die Vorgangsnummer darf nicht leer sein.' }
|
||||
});
|
||||
}
|
||||
|
||||
if (typeof caseNumber === 'string' && !(await checkIfExactDirectoryExists(caseNumber))) {
|
||||
return fail(400, {
|
||||
success: false,
|
||||
caseNumber,
|
||||
error: { message: 'Die Vorgangsnummer existiert in dieser Anwendung nicht.' }
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
const token = await getTokenOrNull(caseNumber);
|
||||
|
||||
if (token && token != user_token) {
|
||||
return fail(400, {
|
||||
success: false,
|
||||
caseNumber,
|
||||
error: { message: 'Der Token ist falsch.' }
|
||||
});
|
||||
}
|
||||
|
||||
redirect(303, `/list/${caseNumber}`);
|
||||
}
|
||||
|
||||
|
||||
const getTokenOrNull = async (vorgang) => {
|
||||
const code_name = '__perm__';
|
||||
const obj_path = `${vorgang}/${code_name}`;
|
||||
|
||||
let resp = null;
|
||||
let code_saved = '';
|
||||
|
||||
try {
|
||||
resp = await client.getObject('tatort', obj_path);
|
||||
|
||||
code_saved = await new Response(resp).text();
|
||||
} catch (error) {
|
||||
if (error.name == 'S3Error') {
|
||||
resp = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (resp != null) {
|
||||
return code_saved;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user