add admin admin settings
This commit is contained in:
@ -1,3 +1,27 @@
|
||||
<div class="h-full">
|
||||
<slot />
|
||||
</div>
|
||||
<script lang="ts">
|
||||
import { page } from '$app/stores';
|
||||
import { env } from '$env/dynamic/public';
|
||||
import { IconOutline } from 'svelte-heros-v2';
|
||||
</script>
|
||||
|
||||
{#if $page.url.pathname !== `${env.PUBLIC_BASE_PATH}/admin/login`}
|
||||
<div class="flex h-screen">
|
||||
<div class="h-full">
|
||||
<ul class="menu p-4 w-fit h-full bg-base-200 text-base-content">
|
||||
<li>
|
||||
<a href="{env.PUBLIC_BASE_PATH}/admin/admin">
|
||||
<IconOutline name="users-outline" />
|
||||
<span class="ml-1">Website Admins</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="h-full w-full">
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="h-full w-full">
|
||||
<slot />
|
||||
</div>
|
||||
{/if}
|
||||
|
0
src/routes/admin/+page.svelte
Normal file
0
src/routes/admin/+page.svelte
Normal file
3
src/routes/admin/admin/+layout.svelte
Normal file
3
src/routes/admin/admin/+layout.svelte
Normal file
@ -0,0 +1,3 @@
|
||||
<div class="flex justify-center items-center w-full">
|
||||
<slot />
|
||||
</div>
|
11
src/routes/admin/admin/+page.server.ts
Normal file
11
src/routes/admin/admin/+page.server.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import type { PageServerLoad } from './$types';
|
||||
import { Admin } from '$lib/server/database';
|
||||
import { getSession } from '$lib/server/session';
|
||||
|
||||
export const load: PageServerLoad = async ({ cookies }) => {
|
||||
const admins = await Admin.findAll({ attributes: { exclude: ['password'] } });
|
||||
return {
|
||||
admins: JSON.parse(JSON.stringify(admins)),
|
||||
permissions: getSession(cookies.get('session') || '')!.value
|
||||
};
|
||||
};
|
222
src/routes/admin/admin/+page.svelte
Normal file
222
src/routes/admin/admin/+page.svelte
Normal file
@ -0,0 +1,222 @@
|
||||
<script lang="ts">
|
||||
import type { PageData } from './$types';
|
||||
import Badges from '$lib/components/Input/Badges.svelte';
|
||||
import { IconOutline } from 'svelte-heros-v2';
|
||||
import Input from '$lib/components/Input/Input.svelte';
|
||||
import { Permissions } from '$lib/permissions';
|
||||
import { env } from '$env/dynamic/public';
|
||||
import ErrorToast from '$lib/components/Toast/ErrorToast.svelte';
|
||||
|
||||
let allPermissionBadges = {
|
||||
'Admin Read': Permissions.AdminRead,
|
||||
'Admin Write': Permissions.AdminWrite,
|
||||
'User Read': Permissions.UserRead,
|
||||
'User Write': Permissions.UserWrite
|
||||
};
|
||||
|
||||
let newAdminUsername: string;
|
||||
let newAdminPassword: string;
|
||||
let newAdminPermissions: number[];
|
||||
|
||||
async function buttonTriggeredRequest<T>(e: MouseEvent, promise: Promise<T>) {
|
||||
(e.target as HTMLButtonElement).disabled = true;
|
||||
await promise;
|
||||
(e.target as HTMLButtonElement).disabled = false;
|
||||
}
|
||||
|
||||
async function addAdmin(username: string, password: string, permissions: Permissions) {
|
||||
const response = await fetch(`${env.PUBLIC_BASE_PATH}/admin/admin`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
username: username,
|
||||
password: password,
|
||||
permissions: permissions.value
|
||||
})
|
||||
});
|
||||
if (response.ok) {
|
||||
data.admins.push(await response.json());
|
||||
data.admins = data.admins;
|
||||
} else {
|
||||
throw new Error();
|
||||
}
|
||||
}
|
||||
|
||||
async function updateAdmin(
|
||||
id: number,
|
||||
username: string | null,
|
||||
password: string | null,
|
||||
permissions: Permissions | null
|
||||
) {
|
||||
const response = await fetch(`${env.PUBLIC_BASE_PATH}/admin/admin`, {
|
||||
method: 'PATCH',
|
||||
body: JSON.stringify({
|
||||
id: id,
|
||||
username: username,
|
||||
password: password,
|
||||
permissions: permissions?.value
|
||||
})
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error();
|
||||
}
|
||||
}
|
||||
|
||||
async function deleteAdmin(id: number) {
|
||||
const response = await fetch(`${env.PUBLIC_BASE_PATH}/admin/admin`, {
|
||||
method: 'DELETE',
|
||||
body: JSON.stringify({
|
||||
id: id
|
||||
})
|
||||
});
|
||||
if (response.ok) {
|
||||
data.admins.splice(
|
||||
data.admins.find((v: typeof data.admins) => v.id == id),
|
||||
1
|
||||
);
|
||||
data.admins = data.admins;
|
||||
} else {
|
||||
throw new Error();
|
||||
}
|
||||
}
|
||||
|
||||
let errorMessage = '';
|
||||
|
||||
export let data: PageData;
|
||||
let permissions = new Permissions(data.permissions);
|
||||
</script>
|
||||
|
||||
<table class="table table-zebra">
|
||||
<colgroup>
|
||||
<col span="1" style="width: 5%" />
|
||||
<col span="1" style="width: 25%" />
|
||||
<col span="1" style="width: 25%" />
|
||||
<col span="1" style="width: 30%" />
|
||||
<col span="1" style="width: 15%" />
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th />
|
||||
<th>Benutzername</th>
|
||||
<th>Passwort</th>
|
||||
<th>Berechtigungen</th>
|
||||
<th />
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{#each data.admins as admin, i}
|
||||
<tr>
|
||||
<td>{i}</td>
|
||||
<td
|
||||
><Input
|
||||
type="text"
|
||||
value={admin.username}
|
||||
disabled={!permissions.adminWrite() || !admin.edit}
|
||||
/></td
|
||||
>
|
||||
<td
|
||||
><Input
|
||||
type="password"
|
||||
placeholder="Neues Passwort..."
|
||||
disabled={!permissions.adminWrite() || !admin.edit}
|
||||
/></td
|
||||
>
|
||||
<td
|
||||
><Badges
|
||||
value={new Permissions(admin.permissions).asArray()}
|
||||
available={allPermissionBadges}
|
||||
disabled={!permissions.adminWrite() || !admin.edit}
|
||||
/></td
|
||||
>
|
||||
<td>
|
||||
<div>
|
||||
{#if admin.edit}
|
||||
<button
|
||||
class="btn btn-square"
|
||||
disabled={!permissions.adminWrite()}
|
||||
on:click={async (e) => {
|
||||
await buttonTriggeredRequest(
|
||||
e,
|
||||
updateAdmin(
|
||||
admin.id,
|
||||
admin.username,
|
||||
admin.password,
|
||||
new Permissions(admin.permissions)
|
||||
)
|
||||
);
|
||||
admin.edit = false;
|
||||
}}
|
||||
>
|
||||
<IconOutline name="check-outline" width="24" height="24" />
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-square"
|
||||
disabled={!permissions.adminWrite()}
|
||||
on:click={() => {
|
||||
admin.username = admin.before.username;
|
||||
admin.permissions = admin.before.permissions;
|
||||
admin.edit = false;
|
||||
}}
|
||||
>
|
||||
<IconOutline name="no-symbol-outline" width="24" height="24" />
|
||||
</button>
|
||||
{:else}
|
||||
<button
|
||||
class="btn btn-square"
|
||||
disabled={!permissions.adminWrite()}
|
||||
on:click={() => {
|
||||
admin.edit = true;
|
||||
admin.before = {
|
||||
username: admin.username,
|
||||
permissions: admin.permissions
|
||||
};
|
||||
}}
|
||||
>
|
||||
<IconOutline name="pencil-square-outline" width="24" height="24" />
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-square"
|
||||
disabled={!permissions.adminWrite()}
|
||||
on:click={(e) => buttonTriggeredRequest(e, deleteAdmin(admin.id))}
|
||||
>
|
||||
<IconOutline name="trash-outline" width="24" height="24" />
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{/each}
|
||||
<tr>
|
||||
<td>{data.admins.length}</td>
|
||||
<td><Input type="text" bind:value={newAdminUsername} /></td>
|
||||
<td><Input type="password" bind:value={newAdminPassword} /></td>
|
||||
<td
|
||||
><Badges
|
||||
bind:value={newAdminPermissions}
|
||||
available={allPermissionBadges}
|
||||
disabled={!permissions.adminWrite()}
|
||||
/></td
|
||||
>
|
||||
<td>
|
||||
<button
|
||||
class="btn btn-square"
|
||||
disabled={!permissions.adminWrite() || !newAdminUsername || !newAdminPassword}
|
||||
on:click={async (e) => {
|
||||
await buttonTriggeredRequest(
|
||||
e,
|
||||
addAdmin(newAdminUsername, newAdminPassword, new Permissions(newAdminPermissions))
|
||||
);
|
||||
newAdminUsername = '';
|
||||
newAdminPassword = '';
|
||||
newAdminPermissions = [];
|
||||
}}
|
||||
>
|
||||
<IconOutline name="user-plus-outline" width="24" height="24" />
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<ErrorToast show={errorMessage !== ''}>
|
||||
<span />
|
||||
</ErrorToast>
|
80
src/routes/admin/admin/+server.ts
Normal file
80
src/routes/admin/admin/+server.ts
Normal file
@ -0,0 +1,80 @@
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
import { Permissions } from '$lib/permissions';
|
||||
import { getSession } from '$lib/server/session';
|
||||
import { Admin } from '$lib/server/database';
|
||||
|
||||
export const POST = (async ({ request, cookies }) => {
|
||||
if (getSession(cookies, [Permissions.AdminWrite]) == null) {
|
||||
return new Response(null, {
|
||||
status: 401
|
||||
});
|
||||
}
|
||||
|
||||
const data = await request.json();
|
||||
const username = data['username'] as string | null;
|
||||
const password = data['password'] as string | null;
|
||||
const permissions = data['permissions'] as number | null;
|
||||
|
||||
if (username == null || password == null || permissions == null) {
|
||||
return new Response(null, {
|
||||
status: 400
|
||||
});
|
||||
}
|
||||
|
||||
const admin = await Admin.create({
|
||||
username: username,
|
||||
password: password,
|
||||
permissions: new Permissions(permissions)
|
||||
});
|
||||
|
||||
return new Response(JSON.stringify(admin), {
|
||||
status: 201
|
||||
});
|
||||
}) satisfies RequestHandler;
|
||||
|
||||
export const PATCH = (async ({ request, cookies }) => {
|
||||
if (getSession(cookies, [Permissions.AdminWrite]) == null) {
|
||||
return new Response(null, {
|
||||
status: 401
|
||||
});
|
||||
}
|
||||
|
||||
const data = await request.json();
|
||||
const id = data['id'] as string | null;
|
||||
|
||||
if (id == null) {
|
||||
return new Response(null, {
|
||||
status: 400
|
||||
});
|
||||
}
|
||||
|
||||
const updatePayload: { [key: string]: any } = {};
|
||||
if (data['username']) updatePayload.username = data['username'];
|
||||
if (data['password']) updatePayload.password = data['password'];
|
||||
if (data['permissions']) updatePayload.permissions = data['permissions'];
|
||||
|
||||
await Admin.update(updatePayload, { where: { id: id } });
|
||||
|
||||
return new Response();
|
||||
}) satisfies RequestHandler;
|
||||
|
||||
export const DELETE = (async ({ request, cookies }) => {
|
||||
if (getSession(cookies, [Permissions.AdminWrite]) == null) {
|
||||
return new Response(null, {
|
||||
status: 401
|
||||
});
|
||||
}
|
||||
|
||||
const data = await request.json();
|
||||
const id = data['id'] as string | null;
|
||||
|
||||
if (id == null) {
|
||||
return new Response(null, {
|
||||
status: 400
|
||||
});
|
||||
}
|
||||
|
||||
await Admin.destroy({ where: { id: id } });
|
||||
|
||||
return new Response();
|
||||
}) satisfies RequestHandler;
|
@ -83,4 +83,6 @@
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<ErrorToast timeout={2000} bind:show={showError} bind:this={errorToastElement} />
|
||||
<ErrorToast timeout={2000} bind:show={showError} bind:this={errorToastElement}>
|
||||
<span>Nutzername oder Passwort falsch</span>
|
||||
</ErrorToast>
|
||||
|
@ -3,6 +3,7 @@ import { Admin } from '$lib/server/database';
|
||||
import { env as publicEnv } from '$env/dynamic/public';
|
||||
import { env } from '$env/dynamic/private';
|
||||
import { addSession } from '$lib/server/session';
|
||||
import { Permissions } from '$lib/permissions';
|
||||
|
||||
export const POST = (async ({ request, cookies }) => {
|
||||
const data = await request.formData();
|
||||
@ -11,7 +12,7 @@ export const POST = (async ({ request, cookies }) => {
|
||||
|
||||
if (username == null || password == null) {
|
||||
return new Response(null, {
|
||||
status: 403
|
||||
status: 401
|
||||
});
|
||||
}
|
||||
|
||||
@ -21,7 +22,7 @@ export const POST = (async ({ request, cookies }) => {
|
||||
username == env.ADMIN_USER &&
|
||||
password == env.ADMIN_PASSWORD
|
||||
) {
|
||||
cookies.set('session', addSession(), {
|
||||
cookies.set('session', addSession(new Permissions(Permissions.allPermissions())), {
|
||||
path: `${publicEnv.PUBLIC_BASE_PATH}/admin`,
|
||||
maxAge: 60 * 60 * 24 * 90,
|
||||
httpOnly: true,
|
||||
@ -32,7 +33,7 @@ export const POST = (async ({ request, cookies }) => {
|
||||
|
||||
const user = await Admin.findOne({ where: { username: username } });
|
||||
if (user && user.validatePassword(password)) {
|
||||
cookies.set('session', addSession(), {
|
||||
cookies.set('session', addSession(user.permissions), {
|
||||
path: `${publicEnv.PUBLIC_BASE_PATH}/admin`,
|
||||
maxAge: 60 * 60 * 24 * 90,
|
||||
httpOnly: true,
|
||||
@ -41,7 +42,7 @@ export const POST = (async ({ request, cookies }) => {
|
||||
return new Response();
|
||||
} else {
|
||||
return new Response(null, {
|
||||
status: 403
|
||||
status: 401
|
||||
});
|
||||
}
|
||||
}) satisfies RequestHandler;
|
||||
|
Reference in New Issue
Block a user