f052_admin_area #27

Merged
jared merged 13 commits from f052_admin_area into development 2025-08-21 11:08:45 +02:00
4 changed files with 253 additions and 0 deletions
Showing only changes of commit 26b94938a9 - Show all commits

View File

@@ -0,0 +1,36 @@
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 = () => {
};

View File

@@ -42,6 +42,18 @@
<p class="mt-1 text-gray-600">Fügen Sie einem Tatort Bilder hinzu.</p>
</div>
{/if}
<div class="group relative rounded-lg p-6 text-sm leading-6 hover:bg-gray-50 w-1/4">
<div
class="flex h-11 w-11 items-center justify-center rounded-lg bg-gray-50 group-hover:bg-white"
>
<FileRect class=" group-hover:text-indigo-600" {outline} />
</div>
<a href="/user-management" class="mt-6 block font-semibold text-gray-900">
Benutzerverwaltung
<span class="absolute inset-0"></span>
</a>
<p class="mt-1 text-gray-600">Füge neue Benutzer hinzu oder entferne welche.</p>
</div>
</div>
</div>

View File

@@ -0,0 +1,180 @@
<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.id;
onMount(async () => {
userList = await getUsers();
})
async function getUsers() {
trachi93 marked this conversation as resolved Outdated
Outdated
Review

try catch block benutzen

try catch block benutzen
const URL = "/api/users"
trachi93 marked this conversation as resolved Outdated
Outdated
Review

sowas kann man ganz gut in eine constante auslagern also ein enum oder objekt, welches alle routen hält und ich diese dann nur referenzieren muss. das sind solche "magic strings"

sowas kann man ganz gut in eine constante auslagern also ein enum oder objekt, welches alle routen hält und ich diese dann nur referenzieren muss. das sind solche "magic strings"

müsste ganzheitlich getan werden -> Ticket angelegt

müsste ganzheitlich getan werden -> Ticket angelegt
const response = await fetch(URL);
return await response.json();
}
async function addUser() {
trachi93 marked this conversation as resolved Outdated
Outdated
Review

Try Catch block

Try Catch block
if (userName == "") {
alert("Der Benutzername darf nicht leer sein.")
return;
}
if (userPassword == "") {
alert("Das Passwort darf nicht leer sein.")
return;
}
const URL = "/api/users";
jared marked this conversation as resolved Outdated
Outdated
Review

s.o.

s.o.
const hashedUserPassword = new jsSHA('SHA-512', 'TEXT').update(userPassword).getHash('HEX');
trachi93 marked this conversation as resolved Outdated
Outdated
Review

Das hashen von Passwörtern solle stets im Backend erfolgen. Hier sollte auch auf eine sichere Methode geachtet werden die noch eine Salt wert einfügt z.B. bycrpty o.ä.

Das hashen von Passwörtern solle stets im Backend erfolgen. Hier sollte auch auf eine sichere Methode geachtet werden die noch eine Salt wert einfügt z.B. bycrpty o.ä.

manuelles hashen wurde durch bcrypt ausgetauscht.

manuelles hashen wurde durch bcrypt ausgetauscht.
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();
trachi93 marked this conversation as resolved Outdated
Outdated
Review

Performanter wäre es wenn du dir nach dem Erstellen des Users, dieses zurück liefern lässt. Damit reduzierst du Traffic

sowas wie
const newUser = await response.json();
userList = [...userList, newUser];

Performanter wäre es wenn du dir nach dem Erstellen des Users, dieses zurück liefern lässt. Damit reduzierst du Traffic sowas wie const newUser = await response.json(); userList = [...userList, newUser];
addUserSuccess = true;
resetInput();
} else {
addUserError = true;
}
}
function resetInput() {
userName = "";
userPassword = "";
addUserError = false;
setInterval(() => {
addUserSuccess = false;
}, 5000);
}
async function deleteUser() {
trachi93 marked this conversation as resolved Outdated
Outdated
Review

try catch

try catch
}
</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 user}
<tr class="border-t border-gray-200 dark:border-gray-600">
<td class="px-4 py-2 text-gray-800 dark:text-white">{user.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={() => removeUser(user.id)}
aria-label="Delete user"
>
{#if !(user.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>

View 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 users_list = getUsers();
return new Response(JSON.stringify(users_list));
}
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 });
}