From 0958ff21b6f91f3429772dcd8c04d5115316b7ab Mon Sep 17 00:00:00 2001
From: bytedream <bytedream@protonmail.com>
Date: Mon, 28 Aug 2023 04:31:58 +0200
Subject: [PATCH] add admin admin settings

---
 src/hooks.server.ts                        |   4 +-
 src/lib/components/Input/Badges.svelte     |  53 +++++
 src/lib/components/Input/Input.svelte      |  20 +-
 src/lib/components/Toast/ErrorToast.svelte |   2 +-
 src/lib/permissions.ts                     |  56 ++++++
 src/lib/server/database.ts                 |  29 ++-
 src/lib/server/session.ts                  |  29 ++-
 src/routes/admin/+layout.svelte            |  30 ++-
 src/routes/admin/+page.svelte              |   0
 src/routes/admin/admin/+layout.svelte      |   3 +
 src/routes/admin/admin/+page.server.ts     |  11 +
 src/routes/admin/admin/+page.svelte        | 222 +++++++++++++++++++++
 src/routes/admin/admin/+server.ts          |  80 ++++++++
 src/routes/admin/login/+page.svelte        |   4 +-
 src/routes/admin/login/+server.ts          |   9 +-
 15 files changed, 524 insertions(+), 28 deletions(-)
 create mode 100644 src/lib/components/Input/Badges.svelte
 create mode 100644 src/lib/permissions.ts
 create mode 100644 src/routes/admin/+page.svelte
 create mode 100644 src/routes/admin/admin/+layout.svelte
 create mode 100644 src/routes/admin/admin/+page.server.ts
 create mode 100644 src/routes/admin/admin/+page.svelte
 create mode 100644 src/routes/admin/admin/+server.ts

diff --git a/src/hooks.server.ts b/src/hooks.server.ts
index 6e4794d..d8db728 100644
--- a/src/hooks.server.ts
+++ b/src/hooks.server.ts
@@ -1,7 +1,7 @@
 import { sequelize } from '$lib/server/database';
 import type { Handle } from '@sveltejs/kit';
 import { env } from '$env/dynamic/public';
-import { hasSession } from '$lib/server/session';
+import { getSession } from '$lib/server/session';
 
 // make sure that the database and tables exist
 await sequelize.sync();
@@ -11,7 +11,7 @@ export const handle: Handle = async ({ event, resolve }) => {
 		event.url.pathname.startsWith(`${env.PUBLIC_BASE_PATH}/admin`) &&
 		event.url.pathname != `${env.PUBLIC_BASE_PATH}/admin/login`
 	) {
-		if (!hasSession(event.cookies.get('session') || '')) {
+		if (getSession(event.cookies.get('session') || '') == null) {
 			return new Response(null, {
 				status: 302,
 				headers: {
diff --git a/src/lib/components/Input/Badges.svelte b/src/lib/components/Input/Badges.svelte
new file mode 100644
index 0000000..fd5e789
--- /dev/null
+++ b/src/lib/components/Input/Badges.svelte
@@ -0,0 +1,53 @@
+<script lang="ts">
+	// eslint-disable-next-line no-undef
+	type T = $$Generic;
+
+	export let id: string | null = null;
+	export let name: string | null = null;
+	export let disabled = true;
+	export let available: string[] | { [key: string]: T } = {};
+	export let value: T[] = [];
+</script>
+
+<div class="flex items-center gap-4">
+	<select
+		{id}
+		{name}
+		class="select select-bordered select-xs"
+		disabled={disabled || available.length === 0}
+		on:change={(e) => {
+			// eslint-disable-next-line @typescript-eslint/ban-ts-comment
+			// @ts-ignore
+			value.push(Object.values(available)[Object.keys(available).indexOf(e.target.value)]);
+			value = value;
+			// eslint-disable-next-line @typescript-eslint/ban-ts-comment
+			// @ts-ignore
+			e.target.value = '-';
+		}}
+	>
+		<option selected hidden>-</option>
+		{#each Object.keys(available) as badge}
+			<option
+				hidden={value.find(
+					(v) => v === Object.values(available)[Object.keys(available).indexOf(badge)]
+				) !== undefined}>{badge}</option
+			>
+		{/each}
+	</select>
+	<div class="flex flow flex-wrap gap-2">
+		{#each value as badge, i}
+			{#if Object.values(available).indexOf(badge) !== -1}
+				<div class="badge badge-outline gap-1">
+					<button
+						{disabled}
+						on:click={() => {
+							value.splice(i, 1);
+							value = value;
+						}}>✕</button
+					>
+					{Object.keys(available)[Object.values(available).indexOf(badge)]}
+				</div>
+			{/if}
+		{/each}
+	</div>
+</div>
diff --git a/src/lib/components/Input/Input.svelte b/src/lib/components/Input/Input.svelte
index 7e4ee9e..8a9999a 100644
--- a/src/lib/components/Input/Input.svelte
+++ b/src/lib/components/Input/Input.svelte
@@ -3,10 +3,11 @@
 <script lang="ts">
 	import { IconSolid } from 'svelte-heros-v2';
 
-	export let id: string;
+	export let id: string | null = null;
 	export let name: string | null = null;
 	export let type: string;
 	export let value: string | null = null;
+	export let placeholder: string | null = null;
 	export let required = false;
 	export let disabled = false;
 
@@ -18,7 +19,7 @@
 <!-- the cursor-not-allowed class must be set here because a disabled button does not respect the 'cursor' css property -->
 <div class={type === 'submit' && disabled ? 'cursor-not-allowed' : ''}>
 	{#if type === 'submit'}
-		<input class="btn" {id} type="submit" {value} {disabled} bind:this={inputElement} />
+		<input class="btn" {id} type="submit" {disabled} bind:value bind:this={inputElement} />
 	{:else}
 		<div>
 			{#if $$slots.label}
@@ -31,24 +32,29 @@
 					</span>
 				</label>
 			{/if}
-			<div class="flex items-center" class:sm:max-w-[16rem]={type !== 'checkbox'}>
+			<div class="relative flex items-center" class:sm:max-w-[16rem]={type !== 'checkbox'}>
 				<input
 					class:checkbox={type === 'checkbox'}
-					class:input,input-bordered={type !== 'checkbox'}
-					class:w-[100%]={initialType !== 'password' && initialType !== 'checkbox'}
+					class:input,input-bordered,w-[100%]={type !== 'checkbox'}
 					class:pr-11={initialType === 'password'}
 					{id}
 					{name}
 					{type}
 					{value}
+					{placeholder}
 					{required}
 					{disabled}
-					autocomplete="off"
 					bind:this={inputElement}
+					autocomplete="off"
+					on:input={(e) => {
+						// eslint-disable-next-line @typescript-eslint/ban-ts-comment
+						// @ts-ignore
+						value = e.target?.value;
+					}}
 				/>
 				{#if initialType === 'password'}
 					<button
-						class="relative right-9"
+						class="absolute right-3"
 						type="button"
 						on:click={() => {
 							type = type === 'password' ? 'text' : 'password';
diff --git a/src/lib/components/Toast/ErrorToast.svelte b/src/lib/components/Toast/ErrorToast.svelte
index ce60aa2..0c270fc 100644
--- a/src/lib/components/Toast/ErrorToast.svelte
+++ b/src/lib/components/Toast/ErrorToast.svelte
@@ -46,7 +46,7 @@
 		<div class="alert alert-error border-none relative text-gray-900 overflow-hidden">
 			<div class="flex gap-2 z-10">
 				<IconOutline name="exclamation-circle-outline" />
-				<span>Nutzername oder Passwort falsch</span>
+				<slot />
 			</div>
 			<progress
 				class="progress progress-error absolute bottom-0 h-[3px] w-full bg-[rgba(0,0,0,0.6)]"
diff --git a/src/lib/permissions.ts b/src/lib/permissions.ts
new file mode 100644
index 0000000..4cb2866
--- /dev/null
+++ b/src/lib/permissions.ts
@@ -0,0 +1,56 @@
+export class Permissions {
+	static readonly AdminRead = 2;
+	static readonly AdminWrite = 4;
+	static readonly UserRead = 8;
+	static readonly UserWrite = 16;
+
+	readonly value: number;
+
+	constructor(value: number | number[]) {
+		if (typeof value == 'number') {
+			this.value = value;
+		} else {
+			let finalValue = 0;
+			for (const v of Object.values(value)) {
+				finalValue |= v;
+			}
+			this.value = finalValue;
+		}
+	}
+
+	toJSON() {
+		return this.value;
+	}
+
+	static allPermissions(): number[] {
+		return [
+			Permissions.AdminRead,
+			Permissions.AdminWrite,
+			Permissions.UserRead,
+			Permissions.UserWrite
+		];
+	}
+
+	adminRead(): boolean {
+		return (this.value & Permissions.AdminRead) != 0;
+	}
+	adminWrite(): boolean {
+		return (this.value & Permissions.AdminWrite) != 0;
+	}
+	userRead(): boolean {
+		return (this.value & Permissions.UserRead) != 0;
+	}
+	userWrite(): boolean {
+		return (this.value & Permissions.UserWrite) != 0;
+	}
+
+	asArray(): number[] {
+		const array = [];
+		for (const perm of Permissions.allPermissions()) {
+			if ((this.value & perm) != 0) {
+				array.push(perm);
+			}
+		}
+		return array;
+	}
+}
diff --git a/src/lib/server/database.ts b/src/lib/server/database.ts
index 71e601f..dc0b1ad 100644
--- a/src/lib/server/database.ts
+++ b/src/lib/server/database.ts
@@ -2,7 +2,16 @@ import { DataTypes } from 'sequelize';
 import { env } from '$env/dynamic/private';
 import { building, dev } from '$app/environment';
 import * as bcrypt from 'bcrypt';
-import { BeforeCreate, BeforeUpdate, Column, Model, Sequelize, Table } from 'sequelize-typescript';
+import {
+	BeforeCreate,
+	BeforeUpdate,
+	Column,
+	Model,
+	Sequelize,
+	Table,
+	Unique
+} from 'sequelize-typescript';
+import { Permissions } from '$lib/permissions';
 
 @Table({ modelName: 'user' })
 export class User extends Model {
@@ -26,18 +35,28 @@ export class User extends Model {
 
 @Table({ modelName: 'admin' })
 export class Admin extends Model {
-	@Column({ type: DataTypes.STRING, allowNull: false })
+	@Column({ type: DataTypes.STRING, allowNull: false, unique: true })
 	declare username: string;
 	@Column({ type: DataTypes.STRING, allowNull: false })
 	declare password: string;
-	@Column({ type: DataTypes.BIGINT, allowNull: false })
-	declare permissions: number;
+	@Column({
+		type: DataTypes.BIGINT,
+		allowNull: false,
+		get(this: Admin): Permissions | null {
+			const permissions = this.getDataValue('permissions');
+			return permissions != null ? new Permissions(permissions) : null;
+		},
+		set(this: Admin, value: Permissions) {
+			this.setDataValue('permissions', value.value);
+		}
+	})
+	declare permissions: Permissions;
 
 	@BeforeCreate
 	@BeforeUpdate
 	static hashPassword(instance: Admin) {
 		if (instance.password != null) {
-			instance.username = bcrypt.hashSync(instance.password, 10);
+			instance.password = bcrypt.hashSync(instance.password, 10);
 		}
 	}
 
diff --git a/src/lib/server/session.ts b/src/lib/server/session.ts
index f032e7f..d999953 100644
--- a/src/lib/server/session.ts
+++ b/src/lib/server/session.ts
@@ -1,11 +1,30 @@
-const sessions: string[] = [];
+import type { Permissions } from '$lib/permissions';
+import type { Cookies } from '@sveltejs/kit';
 
-export function addSession(): string {
+const sessions: Map<string, Permissions> = new Map();
+
+export function addSession(permissions: Permissions): string {
 	const session = 'AAA';
-	sessions.push(session);
+	sessions.set(session, permissions);
 	return session;
 }
 
-export function hasSession(session: string): boolean {
-	return sessions.find((v) => v == session) != undefined;
+export function getSession(session: string | Cookies, permissions?: number[]): Permissions | null {
+	let sess: Permissions | null;
+	if (typeof session == 'string') {
+		sess = sessions.get(session) || null;
+	} else {
+		const sessionId = session.get('session');
+		sess = sessionId ? sessions.get(sessionId) || null : null;
+	}
+
+	if (!sess) {
+		return null;
+	}
+	for (const perm of permissions || []) {
+		if ((sess.value & perm) == 0) {
+			return null;
+		}
+	}
+	return sess;
 }
diff --git a/src/routes/admin/+layout.svelte b/src/routes/admin/+layout.svelte
index 8dc00ce..e1a3e02 100644
--- a/src/routes/admin/+layout.svelte
+++ b/src/routes/admin/+layout.svelte
@@ -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}
diff --git a/src/routes/admin/+page.svelte b/src/routes/admin/+page.svelte
new file mode 100644
index 0000000..e69de29
diff --git a/src/routes/admin/admin/+layout.svelte b/src/routes/admin/admin/+layout.svelte
new file mode 100644
index 0000000..c281020
--- /dev/null
+++ b/src/routes/admin/admin/+layout.svelte
@@ -0,0 +1,3 @@
+<div class="flex justify-center items-center w-full">
+	<slot />
+</div>
diff --git a/src/routes/admin/admin/+page.server.ts b/src/routes/admin/admin/+page.server.ts
new file mode 100644
index 0000000..f90019b
--- /dev/null
+++ b/src/routes/admin/admin/+page.server.ts
@@ -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
+	};
+};
diff --git a/src/routes/admin/admin/+page.svelte b/src/routes/admin/admin/+page.svelte
new file mode 100644
index 0000000..8b7797b
--- /dev/null
+++ b/src/routes/admin/admin/+page.svelte
@@ -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>
diff --git a/src/routes/admin/admin/+server.ts b/src/routes/admin/admin/+server.ts
new file mode 100644
index 0000000..78ecd39
--- /dev/null
+++ b/src/routes/admin/admin/+server.ts
@@ -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;
diff --git a/src/routes/admin/login/+page.svelte b/src/routes/admin/login/+page.svelte
index 3a6a4ed..ac1285a 100644
--- a/src/routes/admin/login/+page.svelte
+++ b/src/routes/admin/login/+page.svelte
@@ -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>
diff --git a/src/routes/admin/login/+server.ts b/src/routes/admin/login/+server.ts
index 8287b9e..0254eff 100644
--- a/src/routes/admin/login/+server.ts
+++ b/src/routes/admin/login/+server.ts
@@ -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;