Compare commits
18 Commits
e45b0e34c9
...
cbea96f892
| Author | SHA1 | Date | |
|---|---|---|---|
| cbea96f892 | |||
| 7e87a01c59 | |||
| 4f0526c71f | |||
| dd06c93b1c | |||
| 26b94938a9 | |||
| b5ff4a72fb | |||
| eea1621339 | |||
| c53400d7b7 | |||
| ef1ddb58a0 | |||
| 8c4f95d52e | |||
| 5b408b096e | |||
| aae8237aae | |||
| 54e3a64dac | |||
| 5927f1dacd | |||
| f39debd0b8 | |||
| 7d265341cc | |||
| d6f2956bcb | |||
| e1cf6dc16f |
54
src/lib/server/userService.ts
Normal file
54
src/lib/server/userService.ts
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
import { db } from '$lib/server/dbService';
|
||||||
|
|
||||||
|
export const getUsers = (): { userId: string; userName: string }[] => {
|
||||||
|
const getUsersSQLStmt = `SELECT id, name
|
||||||
|
FROM users;`;
|
||||||
|
const statement = db.prepare(getUsersSQLStmt);
|
||||||
|
const result = statement.all() as { id: string; name: string }[];
|
||||||
|
const userList: { userId: string; userName: string }[] = [];
|
||||||
|
|
||||||
|
for (const resultItem of result) {
|
||||||
|
const user = { userId: resultItem.id, userName: resultItem.name };
|
||||||
|
userList.push(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
return userList;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const addUser = (userName: string, userPassword: string): number => {
|
||||||
|
const addUserSQLStmt = `INSERT into users(name, pw)
|
||||||
|
values (?, ?)`;
|
||||||
|
const statement = db.prepare(addUserSQLStmt);
|
||||||
|
|
||||||
|
let rowCount;
|
||||||
|
try {
|
||||||
|
const info = statement.run(userName, userPassword);
|
||||||
|
rowCount = info.changes;
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
rowCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rowCount;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const deleteUser = (userId: string) => {
|
||||||
|
// make sure to not delete the last entry
|
||||||
|
const deleteUserSQLStmt = `DELETE
|
||||||
|
FROM users
|
||||||
|
WHERE id = ?
|
||||||
|
AND (SELECT COUNT(*) FROM users) > 1;`;
|
||||||
|
|
||||||
|
const statement = db.prepare(deleteUserSQLStmt);
|
||||||
|
|
||||||
|
let rowCount;
|
||||||
|
try {
|
||||||
|
const info = statement.run(userId);
|
||||||
|
rowCount = info.changes;
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
rowCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rowCount;
|
||||||
|
};
|
||||||
@@ -48,11 +48,11 @@
|
|||||||
>
|
>
|
||||||
<FileRect class=" group-hover:text-indigo-600" {outline} />
|
<FileRect class=" group-hover:text-indigo-600" {outline} />
|
||||||
</div>
|
</div>
|
||||||
<a href="/view" class="mt-6 block font-semibold text-gray-900">
|
<a href="/user-management" class="mt-6 block font-semibold text-gray-900">
|
||||||
Ansicht
|
Benutzerverwaltung
|
||||||
<span class="absolute inset-0"></span>
|
<span class="absolute inset-0"></span>
|
||||||
</a>
|
</a>
|
||||||
<p class="mt-1 text-gray-600">Schau Dir einen Tatort in der 3D Ansicht an.</p>
|
<p class="mt-1 text-gray-600">Füge neue Benutzer hinzu oder entferne welche.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { getVorgaenge } from '$lib/server/vorgangService';
|
|||||||
import type { PageServerLoad } from '../../(token-based)/view/$types';
|
import type { PageServerLoad } from '../../(token-based)/view/$types';
|
||||||
|
|
||||||
export const load: PageServerLoad = async () => {
|
export const load: PageServerLoad = async () => {
|
||||||
const vorgangList = await getVorgaenge();
|
const vorgangList = getVorgaenge();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
vorgangList
|
vorgangList
|
||||||
|
|||||||
@@ -1,37 +0,0 @@
|
|||||||
import { client } from '$lib/minio';
|
|
||||||
import { fail } from '@sveltejs/kit';
|
|
||||||
|
|
||||||
import vorgangNumberOccupied from '$lib/helper/vorgangNumberOccupied.js';
|
|
||||||
|
|
||||||
/** @type {import('./$types').Actions} */
|
|
||||||
export const actions = {
|
|
||||||
default: async ({ request }: {request: Request}) => {
|
|
||||||
const data = await request.formData();
|
|
||||||
const vorgangNumber = data.get('vorgangNumber');
|
|
||||||
const description = data.get('description');
|
|
||||||
|
|
||||||
if (!vorgangNumber) {
|
|
||||||
return fail(400, {
|
|
||||||
vorgangNumber,
|
|
||||||
description,
|
|
||||||
error: { vorgangNumber: 'Es muss eine Vorgangsnummer vorhanden sein.' }
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (await vorgangNumberOccupied(`${vorgangNumber}`)) {
|
|
||||||
return fail(400, {
|
|
||||||
vorgangNumber,
|
|
||||||
description,
|
|
||||||
error: { vorgangNumber: 'Die Vorgangsnummer wurde im System bereits angelegt.' }
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const config = `${JSON.stringify({ vorgangNumber, description, version: 1 })}\n`;
|
|
||||||
|
|
||||||
await client.putObject('tatort', `${vorgangNumber}/config.json`, config, undefined, {
|
|
||||||
'Content-Type': 'application/json'
|
|
||||||
});
|
|
||||||
|
|
||||||
return { success: true };
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@@ -1,115 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import Alert from '$lib/components/Alert.svelte';
|
|
||||||
import Button from '$lib/components/Button.svelte';
|
|
||||||
import Modal from '$lib/components/Modal/Modal.svelte';
|
|
||||||
import ModalTitle from '$lib/components/Modal/ModalTitle.svelte';
|
|
||||||
import ModalContent from '$lib/components/Modal/ModalContent.svelte';
|
|
||||||
import ModalFooter from '$lib/components/Modal/ModalFooter.svelte';
|
|
||||||
import Exclamation from '$lib/icons/Exclamation.svelte';
|
|
||||||
|
|
||||||
export let form;
|
|
||||||
|
|
||||||
let open = false;
|
|
||||||
$: open = form?.success ?? false;
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div class="mx-auto max-w-2xl">
|
|
||||||
<div class="flex flex-col items-center justify-center w-full">
|
|
||||||
<h1 class="text-xl">Neuer Vorgang</h1>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<form method="POST">
|
|
||||||
<div class="space-y-12">
|
|
||||||
<div class="border-b border-gray-900/10 pb-12">
|
|
||||||
<p class="mt-8 text-sm leading-6 text-gray-600">
|
|
||||||
This information will be displayed publicly so be careful what you share.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div class="mt-10 grid grid-cols-1 gap-x-6 gap-y-8">
|
|
||||||
<div>
|
|
||||||
<label for="vorgangNumber" class="block text-sm font-medium leading-6 text-gray-900"
|
|
||||||
><span class="flex"
|
|
||||||
>{#if form?.error?.vorgangNumber}
|
|
||||||
<span class="inline-block mr-1"><Exclamation /></span>
|
|
||||||
{/if} Vorgangs-Nr.</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
|
|
||||||
value={form?.vorgangNumber ?? ''}
|
|
||||||
type="text"
|
|
||||||
name="vorgangNumber"
|
|
||||||
id="vorgangNumber"
|
|
||||||
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"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{#if form?.error?.vorgangNumber}
|
|
||||||
<p class="block text-sm leading-6 text-red-900 mt-2">{form.error.vorgangNumber}</p>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<label for="description" class="block text-sm font-medium leading-6 text-gray-900"
|
|
||||||
><span class="flex"
|
|
||||||
>{#if form?.description}
|
|
||||||
<span class="inline-block mr-1"><Exclamation /></span>
|
|
||||||
{/if} Beschreibung</span
|
|
||||||
></label
|
|
||||||
>
|
|
||||||
<div class="mt-2">
|
|
||||||
<textarea
|
|
||||||
value={form?.description?.toString() ?? ''}
|
|
||||||
id="description"
|
|
||||||
name="description"
|
|
||||||
rows="3"
|
|
||||||
class="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
|
|
||||||
></textarea>
|
|
||||||
</div>
|
|
||||||
{#if form?.error}
|
|
||||||
<p class="block text-sm leading-6 text-red-900 mt-2">{form.description}</p>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<label for="vorgang-token">
|
|
||||||
<span >Zugangstoken (optional) </span>
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<div class="mt-2">
|
|
||||||
<div
|
|
||||||
>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
id="vorgang-token"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="mt-6 flex items-center justify-end gap-x-6">
|
|
||||||
<button type="button" class="text-sm font-semibold leading-6 text-gray-900">Cancel</button>
|
|
||||||
<Button
|
|
||||||
type="submit"
|
|
||||||
class="rounded-md bg-indigo-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
|
|
||||||
>Save</Button
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<Modal {open}
|
|
||||||
><ModalTitle>vorgang anlegen</ModalTitle><ModalContent>
|
|
||||||
{#if form?.success}
|
|
||||||
<Alert class="w-full">Vorgang erfolgreich angelegt</Alert>
|
|
||||||
{:else}
|
|
||||||
<Alert class="w-full" type="error">Fehler beim Upload</Alert>
|
|
||||||
{/if}
|
|
||||||
</ModalContent>
|
|
||||||
<ModalFooter><Button on:click={() => (open = false)}>Ok</Button></ModalFooter>
|
|
||||||
</Modal>
|
|
||||||
</div>
|
|
||||||
@@ -17,11 +17,11 @@ const isRequiredFieldValid = (value: unknown) => {
|
|||||||
export const actions = {
|
export const actions = {
|
||||||
url: async ({ request }: { request: Request }) => {
|
url: async ({ request }: { request: Request }) => {
|
||||||
const data = await request.formData();
|
const data = await request.formData();
|
||||||
const vorgangName = data.get('vorgang');
|
const vorgangName: string | null = data.get('vorgang') as string;
|
||||||
const crimeName = data.get('name');
|
const crimeName: string | null = data.get('name') as string;
|
||||||
const type = data.get('type');
|
const type: string | null = data.get('type') as string;
|
||||||
const vorgangPIN = data.get('vorgangPIN');
|
const vorgangPIN: string | null = data.get('vorgangPIN') as string;
|
||||||
const fileName = data.get('fileName');
|
const fileName: string | null = data.get('fileName') as string;
|
||||||
|
|
||||||
const vorgangExists = vorgangNameExists(vorgangName);
|
const vorgangExists = vorgangNameExists(vorgangName);
|
||||||
let vorgangToken;
|
let vorgangToken;
|
||||||
|
|||||||
193
src/routes/(angemeldet)/user-management/+page.svelte
Normal file
193
src/routes/(angemeldet)/user-management/+page.svelte
Normal file
@@ -0,0 +1,193 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import {onMount} from 'svelte';
|
||||||
|
import Button from "$lib/components/Button.svelte";
|
||||||
|
|
||||||
|
import jsSHA from 'jssha'
|
||||||
|
|
||||||
|
const {data} = $props();
|
||||||
|
|
||||||
|
let userName = $state('')
|
||||||
|
let userPassword = $state('')
|
||||||
|
let userList = $state([])
|
||||||
|
let addUserError = $state(false);
|
||||||
|
let addUserSuccess = $state(false);
|
||||||
|
const currentUser: string = data.user.id;
|
||||||
|
|
||||||
|
onMount(async () => {
|
||||||
|
userList = await getUsers();
|
||||||
|
})
|
||||||
|
|
||||||
|
async function getUsers() {
|
||||||
|
const URL = "/api/users"
|
||||||
|
const response = await fetch(URL);
|
||||||
|
|
||||||
|
return await response.json();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function addUser() {
|
||||||
|
if (userName == "") {
|
||||||
|
alert("Der Benutzername darf nicht leer sein.")
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (userPassword == "") {
|
||||||
|
alert("Das Passwort darf nicht leer sein.")
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const URL = "/api/users";
|
||||||
|
const hashedUserPassword = new jsSHA('SHA-512', 'TEXT').update(userPassword).getHash('HEX');
|
||||||
|
const userData = {userName: userName, userPassword: hashedUserPassword}
|
||||||
|
|
||||||
|
const response = await fetch(URL, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify(userData)
|
||||||
|
})
|
||||||
|
|
||||||
|
if (response.ok) {
|
||||||
|
userList = await getUsers();
|
||||||
|
addUserSuccess = true;
|
||||||
|
resetInput();
|
||||||
|
} else {
|
||||||
|
addUserError = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetInput() {
|
||||||
|
userName = "";
|
||||||
|
userPassword = "";
|
||||||
|
addUserError = false;
|
||||||
|
setInterval(() => {
|
||||||
|
addUserSuccess = false;
|
||||||
|
}, 5000);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function deleteUser(userId: number) {
|
||||||
|
const URL = `/api/users/${userId}`;
|
||||||
|
|
||||||
|
const response = await fetch(URL, {
|
||||||
|
method: 'DELETE',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (response.status == 204) {
|
||||||
|
userList = await getUsers();
|
||||||
|
} else {
|
||||||
|
alert("Nutzer konnte nicht gelöscht werden")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<h1 class="flex justify-center text-3xl md:text-4xl font-bold text-gray-800 dark:text-white tracking-tight mb-4">
|
||||||
|
Benutzerverwaltung
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<h1 class="flex justify-center text-lg md:text-xl font-medium text-gray-800 dark:text-white tracking-tight mb-2">
|
||||||
|
Benutzerliste
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<div class="w-1/4 mx-auto">
|
||||||
|
<table class="min-w-full border border-gray-300 rounded overflow-hidden">
|
||||||
|
<thead class="bg-gray-100 dark:bg-gray-700">
|
||||||
|
<tr>
|
||||||
|
<th class="text-left px-4 py-2">Benutzername</th>
|
||||||
|
<th class="text-center px-4 py-2">Entfernen</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{#each userList as userItem}
|
||||||
|
<tr class="border-t border-gray-200 dark:border-gray-600">
|
||||||
|
<td class="px-4 py-2 text-gray-800 dark:text-white">{userItem.userName}</td>
|
||||||
|
<td class="px-4 py-2 text-center">
|
||||||
|
<button
|
||||||
|
class="text-red-600 hover:text-red-800 focus:outline-none focus:ring-2 focus:ring-red-500 rounded-full p-1 transition"
|
||||||
|
on:click={() => deleteUser(userItem.userId)}
|
||||||
|
aria-label="Delete user"
|
||||||
|
>
|
||||||
|
{#if (userItem.userName != currentUser)}
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24"
|
||||||
|
stroke="currentColor">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||||
|
d="M6 18L18 6M6 6l12 12"/>
|
||||||
|
</svg>
|
||||||
|
{/if}
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{/each}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h1 style="margin-top: 50px;"
|
||||||
|
class="flex justify-center text-lg md:text-xl font-medium text-gray-800 dark:text-white tracking-tight mb-2">
|
||||||
|
Neuer Nutzer
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<div class="mx-auto flex justify-center">
|
||||||
|
<form>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="username">Benutzername</label>
|
||||||
|
<input bind:value={userName} type="text" id="username" placeholder="Namen eingeben"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="password">Passwort</label>
|
||||||
|
<input bind:value={userPassword} type="password" id="password" placeholder="Passwort vergeben"/>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mx-auto flex flex-col items-center space-y-4" style="margin-top: 20px;">
|
||||||
|
{#if addUserError}
|
||||||
|
<div class="flex items-center bg-yellow-100 border-l-4 border-yellow-500 text-yellow-700 p-4 rounded shadow-sm"
|
||||||
|
role="alert">
|
||||||
|
<svg class="h-5 w-5 mr-3 text-yellow-500" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
|
||||||
|
stroke="currentColor">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||||
|
d="M12 9v2m0 4h.01M12 5a7 7 0 100 14 7 7 0 000-14z"/>
|
||||||
|
</svg>
|
||||||
|
<span class="text-sm font-medium">Der Benutzer konnte nicht hinzugefügt werden.</span>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
{#if addUserSuccess}
|
||||||
|
<div class="flex items-center bg-yellow-100 border-l-4 border-yellow-500 text-yellow-700 p-4 rounded shadow-sm"
|
||||||
|
role="alert">
|
||||||
|
<svg class="h-5 w-5 mr-3 text-yellow-500" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
|
||||||
|
stroke="currentColor">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||||
|
d="M12 9v2m0 4h.01M12 5a7 7 0 100 14 7 7 0 000-14z"/>
|
||||||
|
</svg>
|
||||||
|
<span class="text-sm font-medium">Der Benutzer wurde hinzugefügt.</span>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<Button on:click={addUser}>Benutzer hinzufügen</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.form-group {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
margin-bottom: 5px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="text"],
|
||||||
|
input[type="password"] {
|
||||||
|
padding: 8px;
|
||||||
|
font-size: 16px;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -59,8 +59,8 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handle_input(ev: KeyboardEvent, i: number) {
|
async function handleEditFieldInput(ev: KeyboardEvent, listItemIndex: number) {
|
||||||
let item = crimesList[i];
|
let item = crimesList[listItemIndex];
|
||||||
if (ev.key == 'Escape') {
|
if (ev.key == 'Escape') {
|
||||||
let text_field_id = `label__${item.name}`;
|
let text_field_id = `label__${item.name}`;
|
||||||
|
|
||||||
@@ -93,7 +93,7 @@
|
|||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
|
|
||||||
// construct PUT URL
|
// construct PUT URL
|
||||||
const url = $page.url;
|
const url = $page.url.pathname.split('?')[0];
|
||||||
|
|
||||||
let data_obj: { new_name: string; old_name: string } = { new_name: '', old_name: '' };
|
let data_obj: { new_name: string; old_name: string } = { new_name: '', old_name: '' };
|
||||||
data_obj['new_name'] = new_name;
|
data_obj['new_name'] = new_name;
|
||||||
@@ -129,13 +129,22 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function setClipboard(text) {
|
function constructMailToLink() {
|
||||||
const type = "text/plain";
|
const subject = "Link zum Tatvorgang";
|
||||||
const clipboardItemData = {
|
const link = $page.url.toString().split('?')[0];
|
||||||
[type]: text,
|
const body = `Hallo,
|
||||||
};
|
|
||||||
const clipboardItem = new ClipboardItem(clipboardItemData);
|
hier ist der Link zum Tatvorgang:
|
||||||
await navigator.clipboard.write([clipboardItem]);
|
${link}
|
||||||
|
|
||||||
|
Der Zugangs-PIN wird zur Sicherheit über einen zweiten Kommunikationskanal übermittelt.
|
||||||
|
|
||||||
|
Mit freundlichen Grüßen,
|
||||||
|
`;
|
||||||
|
|
||||||
|
const mailtoLink = `mailto:?subject=${encodeURIComponent(subject)}&body=${encodeURIComponent(body)}`;
|
||||||
|
|
||||||
|
return mailtoLink;
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -144,12 +153,12 @@
|
|||||||
<h1 class="text-xl">Vorgang {vorgang.name}</h1>
|
<h1 class="text-xl">Vorgang {vorgang.name}</h1>
|
||||||
{#if data?.user?.admin}
|
{#if data?.user?.admin}
|
||||||
Zugangs-PIN: {vorgang.pin}
|
Zugangs-PIN: {vorgang.pin}
|
||||||
<Button on:click={() => setClipboard($page.url.toString().split('?')[0])}>Copy Link</Button>
|
<a href="{constructMailToLink()}"><Button>Share Link</Button></a>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
<div class="mx-auto flex justify-center max-w-7xl h-full">
|
<div class="mx-auto flex justify-center max-w-7xl h-full">
|
||||||
<ul class="divide-y divide-gray-100">
|
<ul class="divide-y divide-gray-100">
|
||||||
{#each crimesList as item, i}
|
{#each crimesList as item, crimeListItemIndex}
|
||||||
<li>
|
<li>
|
||||||
<a
|
<a
|
||||||
href="/view/{$page.params.vorgang}/{item.name}?pin={vorgangPIN}"
|
href="/view/{$page.params.vorgang}/{item.name}?pin={vorgangPIN}"
|
||||||
@@ -168,12 +177,12 @@
|
|||||||
tabindex="0"
|
tabindex="0"
|
||||||
aria-label="Dateiname bearbeiten"
|
aria-label="Dateiname bearbeiten"
|
||||||
on:focusout={() => {
|
on:focusout={() => {
|
||||||
defocus_element(i);
|
defocus_element(crimeListItemIndex);
|
||||||
}}
|
}}
|
||||||
on:keydown|stopPropagation={// event needed to identify ID
|
on:keydown|stopPropagation={// event needed to identify ID
|
||||||
// TO-DO: check if event is needed or if index is sufficient
|
// TO-DO: check if event is needed or if index is sufficient
|
||||||
async (ev) => {
|
async (ev) => {
|
||||||
handle_input(ev, i);
|
handleEditFieldInput(ev, crimeListItemIndex);
|
||||||
}}>{item.name}</span
|
}}>{item.name}</span
|
||||||
>
|
>
|
||||||
|
|
||||||
|
|||||||
@@ -2,21 +2,20 @@ import { client } from '$lib/minio';
|
|||||||
import { json } from '@sveltejs/kit';
|
import { json } from '@sveltejs/kit';
|
||||||
|
|
||||||
|
|
||||||
// rename operation
|
// rename operation for crimes
|
||||||
export async function PUT({ request }: {request: Request}) {
|
export async function PUT({ request }: {request: Request}) {
|
||||||
const data = await request.json();
|
const data = await request.json();
|
||||||
|
|
||||||
|
|
||||||
// Vorgang
|
// Vorgang
|
||||||
const vorgang = request.url.split('/').at(-1);
|
const vorgangToken = request.url.split('/').at(-1);
|
||||||
|
|
||||||
// prepare copy, incl. check if new name exists already
|
// prepare copy, incl. check if new name exists already
|
||||||
const old_name = data["old_name"];
|
const crimeOldName = data["old_name"];
|
||||||
const src_full_path = `/tatort/${vorgang}/${old_name}`;
|
const crimeS3FullBucketPathOld = `/tatort/${vorgangToken}/${crimeOldName}`;
|
||||||
const new_name = `${vorgang}/${data["new_name"]}`;
|
const crimeNewName = `${vorgangToken}/${data["new_name"]}`;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await client.statObject('tatort', new_name);
|
await client.statObject('tatort', crimeNewName);
|
||||||
return json({ msg: 'Die Datei existiert bereits.' }, { status: 400 });
|
return json({ msg: 'Die Datei existiert bereits.' }, { status: 400 });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// continue operation
|
// continue operation
|
||||||
@@ -24,10 +23,10 @@ export async function PUT({ request }: {request: Request}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// actual copy operation
|
// actual copy operation
|
||||||
await client.copyObject('tatort', new_name, src_full_path)
|
await client.copyObject('tatort', crimeNewName, crimeS3FullBucketPathOld)
|
||||||
|
|
||||||
// delete
|
// delete
|
||||||
await client.removeObject('tatort', `${vorgang}/${old_name}`)
|
await client.removeObject('tatort', `${vorgangToken}/${crimeOldName}`)
|
||||||
|
|
||||||
// return success or failure
|
// return success or failure
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +0,0 @@
|
|||||||
import { redirect } from '@sveltejs/kit';
|
|
||||||
|
|
||||||
export const actions = {
|
|
||||||
default: async ({request}: {request: Request}) => {
|
|
||||||
const data = await request.formData();
|
|
||||||
const vorgangId = data.get('vorgang-id');
|
|
||||||
const vorgangToken = data.get('vorgang-token');
|
|
||||||
|
|
||||||
if( vorgangId && vorgangToken) throw redirect(303, `/list/${vorgangId}?token=${vorgangToken}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import BaseInputField from '$lib/components/BaseInputField.svelte';
|
|
||||||
import Button from '$lib/components/Button.svelte';
|
|
||||||
import ArrowRight from '$lib/icons/Arrow-right.svelte';
|
|
||||||
|
|
||||||
export let form;
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div class="mx-auto max-w-2xl">
|
|
||||||
<div class="flex flex-col items-center justify-center w-full">
|
|
||||||
<h1 class="text-xl">Vorgang ansehen</h1>
|
|
||||||
</div>
|
|
||||||
<p class="mt-8 mb-8 text-sm leading-6 text-gray-600">
|
|
||||||
Anhand der Vorgangsnummer werden Sie zu den Dateien des Vorgangs weitergeleitet und können sich
|
|
||||||
den Vorgang dann ansehen.
|
|
||||||
</p>
|
|
||||||
<form method="POST">
|
|
||||||
<BaseInputField
|
|
||||||
id="vorgang-id"
|
|
||||||
name="vorgang-id"
|
|
||||||
label="Vorgangskennung"
|
|
||||||
type="text"
|
|
||||||
value={form?.vorgangId}
|
|
||||||
/>
|
|
||||||
<div class="mt-5">
|
|
||||||
<BaseInputField
|
|
||||||
id="vorgang-token"
|
|
||||||
name="vorgang-token"
|
|
||||||
label="Zugangstoken"
|
|
||||||
type="text"
|
|
||||||
value={form?.vorgangToken}
|
|
||||||
error={form?.error?.message}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="flex justify-end pt-4">
|
|
||||||
<Button type="submit"><ArrowRight /></Button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
25
src/routes/api/users/+server.ts
Normal file
25
src/routes/api/users/+server.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { json } from '@sveltejs/kit';
|
||||||
|
import { addUser, getUsers } from '$lib/server/userService';
|
||||||
|
|
||||||
|
export function GET({ locals }) {
|
||||||
|
if (!locals.user) {
|
||||||
|
return json({ error: 'Unauthorized' }, { status: 401 });
|
||||||
|
}
|
||||||
|
|
||||||
|
const userList = getUsers();
|
||||||
|
|
||||||
|
return new Response(JSON.stringify(userList));
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function POST({ request, locals }) {
|
||||||
|
if (!locals.user) {
|
||||||
|
return json({ error: 'Unauthorized' }, { status: 401 });
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await request.json();
|
||||||
|
const userName = data.userName;
|
||||||
|
const userPassword = data.userPassword;
|
||||||
|
const rowCount = addUser(userName, userPassword);
|
||||||
|
|
||||||
|
return new Response(null, { status: rowCount == 1 ? 200 : 400 });
|
||||||
|
}
|
||||||
13
src/routes/api/users/[user]/+server.ts
Normal file
13
src/routes/api/users/[user]/+server.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import { json } from '@sveltejs/kit';
|
||||||
|
import { deleteUser } from '$lib/server/userService';
|
||||||
|
|
||||||
|
export async function DELETE({ params, locals }) {
|
||||||
|
if (!locals.user) {
|
||||||
|
return json({ error: 'Unauthorized' }, { status: 401 });
|
||||||
|
}
|
||||||
|
|
||||||
|
const userId = params.user;
|
||||||
|
const rowCount = deleteUser(userId);
|
||||||
|
|
||||||
|
return new Response(null, { status: rowCount == 1 ? 204 : 400 });
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user