Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 25037f4798 | |||
| 38eee8b38c |
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "jeopardy",
|
"name": "jeopardy",
|
||||||
"version": "1.0.4",
|
"version": "1.0.5",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "jeopardy",
|
"name": "jeopardy",
|
||||||
"version": "1.0.4",
|
"version": "1.0.5",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^1.12.2",
|
"axios": "^1.12.2",
|
||||||
"cookie": "^1.0.2"
|
"cookie": "^1.0.2"
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "jeopardy",
|
"name": "jeopardy",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "1.0.4",
|
"version": "1.0.5",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite dev",
|
"dev": "vite dev",
|
||||||
|
|||||||
11
src/app.css
11
src/app.css
@@ -17,6 +17,17 @@
|
|||||||
background-color: rgba(0, 0, 0, 0.1);
|
background-color: rgba(0, 0, 0, 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.btn:disabled {
|
||||||
|
color: grey !important;
|
||||||
|
border-color: gray !important;
|
||||||
|
cursor: unset !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn:disabled:hover {
|
||||||
|
background-color: unset !important;
|
||||||
|
cursor: unset !important;
|
||||||
|
}
|
||||||
|
|
||||||
.inputField {
|
.inputField {
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
name="viewport"
|
name="viewport"
|
||||||
content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0"
|
content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0"
|
||||||
/>
|
/>
|
||||||
|
<script src="https://kit.fontawesome.com/4115dc7344.js" crossorigin="anonymous"></script>
|
||||||
%sveltekit.head%
|
%sveltekit.head%
|
||||||
</head>
|
</head>
|
||||||
<body data-sveltekit-preload-data="hover" class="size-full">
|
<body data-sveltekit-preload-data="hover" class="size-full">
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import { env } from "$env/dynamic/public";
|
import { env } from "$env/dynamic/public";
|
||||||
import UserSvelte from "./User.svelte";
|
import UserSvelte, { type UserObj } from "./User.svelte";
|
||||||
import { goto } from "$app/navigation";
|
import { goto } from "$app/navigation";
|
||||||
|
|
||||||
export async function isAuthenticated() {
|
export async function isAuthenticated() {
|
||||||
@@ -8,13 +8,13 @@ export async function isAuthenticated() {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return axios
|
return axios
|
||||||
.get(
|
.get(`${env.PUBLIC_JEOPARDY_SERVER_PROTOCOL}://${env.PUBLIC_JEOPARDY_SERVER}/auth`, {
|
||||||
`${env.PUBLIC_JEOPARDY_SERVER_PROTOCOL}://${env.PUBLIC_JEOPARDY_SERVER}/user/username`,
|
withCredentials: true
|
||||||
{ withCredentials: true }
|
})
|
||||||
)
|
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
if (res.status === 200) {
|
if (res.status === 200) {
|
||||||
UserSvelte.username = res.data;
|
UserSvelte.user = res.data as UserObj;
|
||||||
|
console.log(UserSvelte.id, UserSvelte.username, UserSvelte.role);
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
goto("/login");
|
goto("/login");
|
||||||
|
|||||||
85
src/lib/Modal.svelte
Normal file
85
src/lib/Modal.svelte
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import type { Snippet } from "svelte";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
showModal: boolean;
|
||||||
|
header: Snippet;
|
||||||
|
children: Snippet;
|
||||||
|
actionButtons?: Snippet;
|
||||||
|
cancelFn?: () => void;
|
||||||
|
okFn: () => Promise<boolean>;
|
||||||
|
oncloseFn?: () => void;
|
||||||
|
[key: string]: unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
let {
|
||||||
|
showModal = $bindable(),
|
||||||
|
header,
|
||||||
|
children,
|
||||||
|
cancelFn,
|
||||||
|
okFn,
|
||||||
|
oncloseFn,
|
||||||
|
actionButtons
|
||||||
|
}: Props = $props();
|
||||||
|
|
||||||
|
let dialog: HTMLDialogElement | undefined = $state(); // HTMLDialogElement
|
||||||
|
|
||||||
|
$effect(() => {
|
||||||
|
if (showModal) dialog?.showModal();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!-- svelte-ignore a11y_click_events_have_key_events, a11y_no_noninteractive_element_interactions -->
|
||||||
|
<dialog
|
||||||
|
bind:this={dialog}
|
||||||
|
onclose={() => {
|
||||||
|
showModal = false;
|
||||||
|
if (oncloseFn) oncloseFn();
|
||||||
|
}}
|
||||||
|
onclick={(e) => {
|
||||||
|
if (e.target === dialog) dialog.close();
|
||||||
|
}}
|
||||||
|
class="rounded-md"
|
||||||
|
>
|
||||||
|
<div class="flex flex-col gap-4 p-4">
|
||||||
|
{@render header?.()}
|
||||||
|
{@render children?.()}
|
||||||
|
<!-- svelte-ignore a11y_autofocus -->
|
||||||
|
<div class="flex justify-end gap-4">
|
||||||
|
{@render actionButtons?.()}
|
||||||
|
<button
|
||||||
|
autofocus
|
||||||
|
onclick={() => {
|
||||||
|
dialog?.close();
|
||||||
|
if (cancelFn) cancelFn();
|
||||||
|
}}
|
||||||
|
class="btn">Abbrechen</button
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
autofocus
|
||||||
|
onclick={async () => {
|
||||||
|
if (await okFn()) {
|
||||||
|
dialog?.close();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
class="btn min-w-[64px]">Ok</button
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</dialog>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
dialog {
|
||||||
|
position: fixed;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
/* Move it back 50% relative to self */
|
||||||
|
-webkit-transform: translateX(-50%) translateY(-50%);
|
||||||
|
-moz-transform: translateX(-50%) translateY(-50%);
|
||||||
|
-ms-transform: translateX(-50%) translateY(-50%);
|
||||||
|
transform: translateX(-50%) translateY(-50%);
|
||||||
|
}
|
||||||
|
dialog::backdrop {
|
||||||
|
background: rgba(0, 0, 0, 0.5);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,4 +1,12 @@
|
|||||||
let username: string = "";
|
let username: string = $state("");
|
||||||
|
let role: string = $state("");
|
||||||
|
let id: string = $state("");
|
||||||
|
|
||||||
|
export type UserObj = {
|
||||||
|
username: string;
|
||||||
|
role: string;
|
||||||
|
_id: string;
|
||||||
|
};
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
get username(): string {
|
get username(): string {
|
||||||
@@ -6,5 +14,22 @@ export default {
|
|||||||
},
|
},
|
||||||
set username(uname: string) {
|
set username(uname: string) {
|
||||||
username = uname;
|
username = uname;
|
||||||
|
},
|
||||||
|
get role(): string {
|
||||||
|
return role;
|
||||||
|
},
|
||||||
|
set role(newrole: string) {
|
||||||
|
role = newrole;
|
||||||
|
},
|
||||||
|
get id(): string {
|
||||||
|
return id;
|
||||||
|
},
|
||||||
|
set id(newid: string) {
|
||||||
|
id = newid;
|
||||||
|
},
|
||||||
|
set user(userobj: UserObj) {
|
||||||
|
username = userobj.username;
|
||||||
|
role = userobj.role;
|
||||||
|
id = userobj._id;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { afterNavigate, goto } from "$app/navigation";
|
import { goto } from "$app/navigation";
|
||||||
import { isAuthenticated } from "$lib/Auth.svelte";
|
import { env } from "$env/dynamic/public";
|
||||||
|
import UserSvelte from "$lib/User.svelte";
|
||||||
import websocket, { SocketConnectionType } from "$lib/websocket.svelte";
|
import websocket, { SocketConnectionType } from "$lib/websocket.svelte";
|
||||||
|
import axios from "axios";
|
||||||
|
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
if (websocket.connectionType === SocketConnectionType.HOST) {
|
if (websocket.connectionType === SocketConnectionType.HOST) {
|
||||||
@@ -13,17 +15,72 @@
|
|||||||
goto("/connected/display");
|
goto("/connected/display");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function logout() {
|
||||||
|
cookieStore
|
||||||
|
.delete("jeopardytoken")
|
||||||
|
.then(() => {
|
||||||
|
goto("/login");
|
||||||
|
})
|
||||||
|
.catch((e) => {
|
||||||
|
alert("Logout fehlgeschlagen!");
|
||||||
|
goto("/login");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function logoutFromAllDevices() {
|
||||||
|
axios
|
||||||
|
.post(
|
||||||
|
`${env.PUBLIC_JEOPARDY_SERVER_PROTOCOL}://${env.PUBLIC_JEOPARDY_SERVER}/user/logout`,
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
withCredentials: true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.then(() => {
|
||||||
|
logout();
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
alert("Logout fehlgeschlagen!");
|
||||||
|
});
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="flex h-full flex-col">
|
<div class="flex h-full flex-col">
|
||||||
<h1 class="m-4 mb-8 text-7xl font-bold">Jeopardy</h1>
|
<div class="ms-4 me-4 flex items-center gap-4">
|
||||||
|
<h1 class="text-7xl font-bold">Jeopardy</h1>
|
||||||
|
<div class="grow"></div>
|
||||||
|
{#if UserSvelte.role === "admin"}
|
||||||
|
<button type="button" class="btn" onclick={() => goto("/admin")}>Administration</button>
|
||||||
|
{/if}
|
||||||
|
<button type="button" class="btn" onclick={() => goto("/settings")}>Einstellungen</button>
|
||||||
|
<button type="button" class="btn" onclick={logout}>Logout</button>
|
||||||
|
<button type="button" class="btn" onclick={logoutFromAllDevices}
|
||||||
|
>Logout von allen Geräten</button
|
||||||
|
>
|
||||||
|
<div class="btn profile ps-2 pe-2">
|
||||||
|
<i class="fa-regular fa-user"></i>
|
||||||
|
{UserSvelte.username}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="flex h-full grow items-center justify-around p-4">
|
<div class="flex h-full grow items-center justify-around p-4">
|
||||||
<button class="btn m-2 h-1/2 w-1/2 text-5xl" onclick={websocket.connectAsHost}
|
<button class="btn m-2 h-1/2 w-1/2 text-5xl" onclick={websocket.connectAsHost}
|
||||||
>Connect as Host</button
|
>Spiel hosten</button
|
||||||
>
|
>
|
||||||
<button class="btn m-2 h-1/2 w-1/2 text-5xl" onclick={websocket.connectAsDisplay}
|
<button class="btn m-2 h-1/2 w-1/2 text-5xl" onclick={websocket.connectAsDisplay}
|
||||||
>Connect as Display</button
|
>Spiel darstellen</button
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.profile {
|
||||||
|
border-color: gray;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile:hover {
|
||||||
|
background-color: unset !important;
|
||||||
|
cursor: unset !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
350
src/routes/admin/+page.svelte
Normal file
350
src/routes/admin/+page.svelte
Normal file
@@ -0,0 +1,350 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { afterNavigate, goto } from "$app/navigation";
|
||||||
|
import type { UserObj } from "$lib/User.svelte";
|
||||||
|
import { env } from "$env/dynamic/public";
|
||||||
|
import axios from "axios";
|
||||||
|
import { onMount } from "svelte";
|
||||||
|
import Modal from "$lib/Modal.svelte";
|
||||||
|
import UserSvelte from "$lib/User.svelte";
|
||||||
|
|
||||||
|
let users: UserObj[] = $state([]);
|
||||||
|
let roles: string[] = $state([]);
|
||||||
|
|
||||||
|
let showAddUser = $state(false);
|
||||||
|
let addUserName = $state("");
|
||||||
|
|
||||||
|
let showDeleteUser = $state(false);
|
||||||
|
let showResetPassword = $state(false);
|
||||||
|
let showChangeRole = $state(false);
|
||||||
|
let selectedUser: UserObj | undefined = $state(undefined);
|
||||||
|
let roleToChange: string = $state("");
|
||||||
|
|
||||||
|
let error = $state("");
|
||||||
|
|
||||||
|
async function loadUsers() {
|
||||||
|
console.log("loading users");
|
||||||
|
return axios
|
||||||
|
.get(
|
||||||
|
`${env.PUBLIC_JEOPARDY_SERVER_PROTOCOL}://${env.PUBLIC_JEOPARDY_SERVER}/admin/user/list`,
|
||||||
|
{
|
||||||
|
withCredentials: true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.then((response) => {
|
||||||
|
if (response.status === 200) {
|
||||||
|
users = response.data;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((e) => {
|
||||||
|
console.log(e);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadRoles() {
|
||||||
|
console.log("loading roles");
|
||||||
|
return axios
|
||||||
|
.get(
|
||||||
|
`${env.PUBLIC_JEOPARDY_SERVER_PROTOCOL}://${env.PUBLIC_JEOPARDY_SERVER}/admin/roles`,
|
||||||
|
{
|
||||||
|
withCredentials: true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.then((response) => {
|
||||||
|
if (response.status === 200) {
|
||||||
|
roles = response.data;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((e) => {
|
||||||
|
console.log(e);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
afterNavigate(() => {
|
||||||
|
if (UserSvelte.role !== "admin") goto("/");
|
||||||
|
});
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
if (UserSvelte.role !== "admin") goto("/");
|
||||||
|
loadRoles().then(loadUsers);
|
||||||
|
});
|
||||||
|
|
||||||
|
async function addUserOk(): Promise<boolean> {
|
||||||
|
error = "";
|
||||||
|
if (addUserName.length <= 0) {
|
||||||
|
error = "Gib einen Nutzernamen ein";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return axios
|
||||||
|
.put(
|
||||||
|
`${env.PUBLIC_JEOPARDY_SERVER_PROTOCOL}://${env.PUBLIC_JEOPARDY_SERVER}/admin/user`,
|
||||||
|
{
|
||||||
|
username: addUserName
|
||||||
|
},
|
||||||
|
{
|
||||||
|
withCredentials: true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.then((response) => {
|
||||||
|
if (response.status === 200) {
|
||||||
|
alert(`Passwort: ${response.data.password}`);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
throw false;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
error = "Etwas ist schief gelaufen";
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function deleteUserOk(): Promise<boolean> {
|
||||||
|
error = "";
|
||||||
|
|
||||||
|
if (selectedUser === undefined) {
|
||||||
|
error = "Etwas ist schief gelaufen. Kein Nutzer zum Löschen definiert.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return axios
|
||||||
|
.delete(
|
||||||
|
`${env.PUBLIC_JEOPARDY_SERVER_PROTOCOL}://${env.PUBLIC_JEOPARDY_SERVER}/admin/user`,
|
||||||
|
{
|
||||||
|
withCredentials: true,
|
||||||
|
data: {
|
||||||
|
userid: selectedUser._id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.then(() => {
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
error = "Nutzer konnte nicht gelöscht werden";
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function resetPasswordOk(): Promise<boolean> {
|
||||||
|
error = "";
|
||||||
|
|
||||||
|
if (selectedUser === undefined) {
|
||||||
|
error = "Etwas ist schief gelaufen. Kein Nutzer zum Löschen definiert.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return axios
|
||||||
|
.post(
|
||||||
|
`${env.PUBLIC_JEOPARDY_SERVER_PROTOCOL}://${env.PUBLIC_JEOPARDY_SERVER}/admin/user/resetpw`,
|
||||||
|
{
|
||||||
|
userid: selectedUser._id
|
||||||
|
},
|
||||||
|
{
|
||||||
|
withCredentials: true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.then((response) => {
|
||||||
|
if (response.status === 200) {
|
||||||
|
alert(`Passwort: ${response.data.password}`);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
throw false;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
error = "Etwas ist schief gelaufen";
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function changeRoleOk(): Promise<boolean> {
|
||||||
|
error = "";
|
||||||
|
|
||||||
|
if (selectedUser === undefined) {
|
||||||
|
error = "Etwas ist schief gelaufen. Kein Nutzer zum Löschen definiert.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (roleToChange === selectedUser.role) {
|
||||||
|
error = `${selectedUser.username} hat bereits die Rolle ${roleToChange}. Wähle eine andere Rolle aus.`;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return axios
|
||||||
|
.post(
|
||||||
|
`${env.PUBLIC_JEOPARDY_SERVER_PROTOCOL}://${env.PUBLIC_JEOPARDY_SERVER}/admin/user/changerole`,
|
||||||
|
{
|
||||||
|
userid: selectedUser._id,
|
||||||
|
role: roleToChange
|
||||||
|
},
|
||||||
|
{
|
||||||
|
withCredentials: true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.then((response) => {
|
||||||
|
if (response.status === 200) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
throw false;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
error = "Etwas ist schief gelaufen";
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function addUserCancel() {
|
||||||
|
error = "";
|
||||||
|
addUserName = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectedUserCancel() {
|
||||||
|
error = "";
|
||||||
|
selectedUser = undefined;
|
||||||
|
roleToChange = "";
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="flex flex-col gap-4 p-4">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<h2 class="text-4xl font-bold">Administration</h2>
|
||||||
|
<div class="grow"></div>
|
||||||
|
<button type="button" class="btn" onclick={() => goto("/")}>Zurück</button>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-col gap-2">
|
||||||
|
{#each users as user (user._id)}
|
||||||
|
<div class="flex justify-between gap-4 rounded-md border-1 p-2">
|
||||||
|
<div class="shrink-0 grow-0 text-center align-middle">{user._id}</div>
|
||||||
|
<div>{user.username}</div>
|
||||||
|
<div>{user.role}</div>
|
||||||
|
<div class="flex gap-2">
|
||||||
|
<!-- svelte-ignore a11y_consider_explicit_label -->
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="btn"
|
||||||
|
disabled={user._id === UserSvelte.id}
|
||||||
|
onclick={() => {
|
||||||
|
selectedUser = user;
|
||||||
|
roleToChange = selectedUser.role;
|
||||||
|
showChangeRole = true;
|
||||||
|
}}><i class="fa-solid fa-user"></i> Rolle ändern</button
|
||||||
|
>
|
||||||
|
<!-- svelte-ignore a11y_consider_explicit_label -->
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="btn"
|
||||||
|
disabled={user._id === UserSvelte.id}
|
||||||
|
onclick={() => {
|
||||||
|
selectedUser = user;
|
||||||
|
showResetPassword = true;
|
||||||
|
}}
|
||||||
|
><i class="fa-solid fa-arrow-rotate-right"></i> Passwort zurücksetzen</button
|
||||||
|
>
|
||||||
|
<!-- svelte-ignore a11y_consider_explicit_label -->
|
||||||
|
<button
|
||||||
|
disabled={user._id === UserSvelte.id}
|
||||||
|
type="button"
|
||||||
|
class="btn border-red-600 text-red-600"
|
||||||
|
onclick={() => {
|
||||||
|
selectedUser = user;
|
||||||
|
showDeleteUser = true;
|
||||||
|
}}><i class="fa-solid fa-trash"></i></button
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
<button type="button" class="btn" onclick={() => (showAddUser = true)}>Nutzer hinzufügen</button
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Modal bind:showModal={showAddUser} okFn={addUserOk} cancelFn={addUserCancel} oncloseFn={loadUsers}>
|
||||||
|
{#snippet header()}
|
||||||
|
<h2 class="text-3xl">Nutzer hinzufügen</h2>
|
||||||
|
{/snippet}
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label for="username" class="">Name</label>
|
||||||
|
<div>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
name="username"
|
||||||
|
id="username"
|
||||||
|
class="borders mt-2 mb-2 w-full"
|
||||||
|
bind:value={addUserName}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{#if error.length > 0}
|
||||||
|
<div class="text-red-700">{error}</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
|
||||||
|
<Modal
|
||||||
|
bind:showModal={showDeleteUser}
|
||||||
|
okFn={deleteUserOk}
|
||||||
|
cancelFn={selectedUserCancel}
|
||||||
|
oncloseFn={loadUsers}
|
||||||
|
>
|
||||||
|
{#snippet header()}
|
||||||
|
<h2 class="text-3xl">Nutzer löschen</h2>
|
||||||
|
{/snippet}
|
||||||
|
|
||||||
|
<div>Soll Nutzer {selectedUser?.username} wirklich gelöscht werden?</div>
|
||||||
|
{#if error.length > 0}
|
||||||
|
<div class="text-red-700">{error}</div>
|
||||||
|
{/if}
|
||||||
|
</Modal>
|
||||||
|
|
||||||
|
<Modal
|
||||||
|
bind:showModal={showResetPassword}
|
||||||
|
okFn={resetPasswordOk}
|
||||||
|
cancelFn={selectedUserCancel}
|
||||||
|
oncloseFn={loadUsers}
|
||||||
|
>
|
||||||
|
{#snippet header()}
|
||||||
|
<h2 class="text-3xl">Passwort zurücksetzen</h2>
|
||||||
|
{/snippet}
|
||||||
|
|
||||||
|
<div>
|
||||||
|
Soll das Passwort von Nutzer {selectedUser?.username} wirklich zurückgesetzt werden?
|
||||||
|
</div>
|
||||||
|
{#if error.length > 0}
|
||||||
|
<div class="text-red-700">{error}</div>
|
||||||
|
{/if}
|
||||||
|
</Modal>
|
||||||
|
|
||||||
|
<Modal
|
||||||
|
bind:showModal={showChangeRole}
|
||||||
|
okFn={changeRoleOk}
|
||||||
|
cancelFn={selectedUserCancel}
|
||||||
|
oncloseFn={loadUsers}
|
||||||
|
>
|
||||||
|
{#snippet header()}
|
||||||
|
<h2 class="text-3xl">Rolle von {selectedUser?.username} ändern</h2>
|
||||||
|
{/snippet}
|
||||||
|
|
||||||
|
<div>
|
||||||
|
{#if selectedUser}
|
||||||
|
<select name="roles" id="role-select" class="btn w-full" bind:value={roleToChange}>
|
||||||
|
{#each roles as role, index (index)}
|
||||||
|
<option value={role}>{role}</option>
|
||||||
|
{/each}
|
||||||
|
</select>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
{#if error.length > 0}
|
||||||
|
<div class="text-red-700">{error}</div>
|
||||||
|
{/if}
|
||||||
|
</Modal>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.borders {
|
||||||
|
border: 1px solid black;
|
||||||
|
border-radius: 5px;
|
||||||
|
display: flex;
|
||||||
|
padding: 2px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { goto } from "$app/navigation";
|
import { goto } from "$app/navigation";
|
||||||
import { env } from "$env/dynamic/public";
|
import { env } from "$env/dynamic/public";
|
||||||
import UserState from "$lib/User.svelte";
|
import UserState, { type UserObj } from "$lib/User.svelte";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
|
|
||||||
let username = $state("");
|
let username = $state("");
|
||||||
@@ -20,7 +20,8 @@
|
|||||||
)
|
)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
if (response.status === 200) {
|
if (response.status === 200) {
|
||||||
UserState.username = response.data;
|
UserState.user = response.data as UserObj;
|
||||||
|
console.log(UserState.id, UserState.username, UserState.role);
|
||||||
goto("/");
|
goto("/");
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
92
src/routes/settings/+page.svelte
Normal file
92
src/routes/settings/+page.svelte
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { goto } from "$app/navigation";
|
||||||
|
import { env } from "$env/dynamic/public";
|
||||||
|
import UserState, { type UserObj } from "$lib/User.svelte";
|
||||||
|
import axios from "axios";
|
||||||
|
|
||||||
|
let oldpassword = $state("");
|
||||||
|
let newpassword = $state("");
|
||||||
|
let reppassword = $state("");
|
||||||
|
let error = $state("");
|
||||||
|
|
||||||
|
async function changePassword() {
|
||||||
|
if (newpassword !== reppassword) {
|
||||||
|
error = "Passwörter stimmen nicht überein.";
|
||||||
|
} else {
|
||||||
|
error = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
axios
|
||||||
|
.post(
|
||||||
|
`${env.PUBLIC_JEOPARDY_SERVER_PROTOCOL}://${env.PUBLIC_JEOPARDY_SERVER}/user/changepw`,
|
||||||
|
{
|
||||||
|
old: oldpassword,
|
||||||
|
new: newpassword
|
||||||
|
},
|
||||||
|
{ withCredentials: true }
|
||||||
|
)
|
||||||
|
.then((response) => {
|
||||||
|
if (response.status === 200) {
|
||||||
|
goto("/login");
|
||||||
|
} else {
|
||||||
|
error = "Passwort ändern fehlgeschlagen";
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((e) => {
|
||||||
|
error = "Passwort ändern fehlgeschlagen";
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="flex flex-col gap-4 p-4">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<h2 class="text-4xl font-bold">Einstellungen</h2>
|
||||||
|
<div class="grow"></div>
|
||||||
|
<button type="button" class="btn" onclick={() => goto("/")}>Zurück</button>
|
||||||
|
</div>
|
||||||
|
<div class="rounded-md border-1 p-4">
|
||||||
|
<h4 class="font-bold">Passwort ändern</h4>
|
||||||
|
<div>
|
||||||
|
<label for="oldpassword" class="">Altes Passwort</label>
|
||||||
|
<input
|
||||||
|
type="password"
|
||||||
|
name="oldpassword"
|
||||||
|
id="oldpassword"
|
||||||
|
class="borders me-4 mt-2 mb-4"
|
||||||
|
bind:value={oldpassword}
|
||||||
|
/>
|
||||||
|
<label for="newpassword" class="">Neues Passwort</label>
|
||||||
|
<input
|
||||||
|
type="password"
|
||||||
|
name="newpassword"
|
||||||
|
id="newpassword"
|
||||||
|
class="borders me-4 mt-2 mb-4"
|
||||||
|
bind:value={newpassword}
|
||||||
|
/>
|
||||||
|
<label for="reppassword" class="">Neues Passwort wiederholen</label>
|
||||||
|
<input
|
||||||
|
type="password"
|
||||||
|
name="reppassword"
|
||||||
|
id="reppassword"
|
||||||
|
class="borders me-4 mt-2 mb-4"
|
||||||
|
bind:value={reppassword}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<button type="button" class="btn mb-2 w-fit ps-4 pe-4" onclick={changePassword}
|
||||||
|
>Passwort ändern</button
|
||||||
|
>
|
||||||
|
{#if error.length > 0}
|
||||||
|
<div class="text-red-700">{error}</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.borders {
|
||||||
|
border: 1px solid black;
|
||||||
|
border-radius: 5px;
|
||||||
|
display: flex;
|
||||||
|
font-size: larger;
|
||||||
|
padding: 2px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import tailwindcss from '@tailwindcss/vite';
|
import tailwindcss from "@tailwindcss/vite";
|
||||||
import { sveltekit } from '@sveltejs/kit/vite';
|
import { sveltekit } from "@sveltejs/kit/vite";
|
||||||
import { defineConfig } from 'vite';
|
import { defineConfig } from "vite";
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [tailwindcss(), sveltekit()]
|
plugins: [sveltekit(), tailwindcss()]
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user