Browse Source

feat: admin settings

Timothy J. Baek 1 year ago
parent
commit
511e939b5d

+ 12 - 1
backend/apps/web/main.py

@@ -11,7 +11,15 @@ from apps.web.routers import (
     configs,
     utils,
 )
-from config import WEBUI_VERSION, WEBUI_AUTH, DEFAULT_MODELS, DEFAULT_PROMPT_SUGGESTIONS, ENABLE_SIGNUP
+from config import (
+    WEBUI_VERSION,
+    WEBUI_AUTH,
+    DEFAULT_MODELS,
+    DEFAULT_PROMPT_SUGGESTIONS,
+    DEFAULT_USER_ROLE,
+    ENABLE_SIGNUP,
+    USER_PERMISSIONS,
+)
 
 app = FastAPI()
 
@@ -20,6 +28,9 @@ origins = ["*"]
 app.state.ENABLE_SIGNUP = ENABLE_SIGNUP
 app.state.DEFAULT_MODELS = DEFAULT_MODELS
 app.state.DEFAULT_PROMPT_SUGGESTIONS = DEFAULT_PROMPT_SUGGESTIONS
+app.state.DEFAULT_USER_ROLE = DEFAULT_USER_ROLE
+app.state.USER_PERMISSIONS = USER_PERMISSIONS
+
 
 app.add_middleware(
     CORSMiddleware,

+ 40 - 4
backend/apps/web/routers/auths.py

@@ -19,7 +19,12 @@ from apps.web.models.auths import (
 )
 from apps.web.models.users import Users
 
-from utils.utils import get_password_hash, get_current_user, get_admin_user, create_token
+from utils.utils import (
+    get_password_hash,
+    get_current_user,
+    get_admin_user,
+    create_token,
+)
 from utils.misc import get_gravatar_url, validate_email_format
 from constants import ERROR_MESSAGES
 
@@ -116,16 +121,24 @@ async def signin(form_data: SigninForm):
 @router.post("/signup", response_model=SigninResponse)
 async def signup(request: Request, form_data: SignupForm):
     if not request.app.state.ENABLE_SIGNUP:
-        raise HTTPException(status.HTTP_403_FORBIDDEN, detail=ERROR_MESSAGES.ACCESS_PROHIBITED)
+        raise HTTPException(
+            status.HTTP_403_FORBIDDEN, detail=ERROR_MESSAGES.ACCESS_PROHIBITED
+        )
 
     if not validate_email_format(form_data.email.lower()):
-        raise HTTPException(status.HTTP_400_BAD_REQUEST, detail=ERROR_MESSAGES.INVALID_EMAIL_FORMAT)
+        raise HTTPException(
+            status.HTTP_400_BAD_REQUEST, detail=ERROR_MESSAGES.INVALID_EMAIL_FORMAT
+        )
 
     if Users.get_user_by_email(form_data.email.lower()):
         raise HTTPException(400, detail=ERROR_MESSAGES.EMAIL_TAKEN)
 
     try:
-        role = "admin" if Users.get_num_users() == 0 else "pending"
+        role = (
+            "admin"
+            if Users.get_num_users() == 0
+            else request.app.state.DEFAULT_USER_ROLE
+        )
         hashed = get_password_hash(form_data.password)
         user = Auths.insert_new_auth(
             form_data.email.lower(), hashed, form_data.name, role
@@ -164,3 +177,26 @@ async def get_sign_up_status(request: Request, user=Depends(get_admin_user)):
 async def toggle_sign_up(request: Request, user=Depends(get_admin_user)):
     request.app.state.ENABLE_SIGNUP = not request.app.state.ENABLE_SIGNUP
     return request.app.state.ENABLE_SIGNUP
+
+
+############################
+# Default User Role
+############################
+
+
+@router.get("/signup/user/role")
+async def get_default_user_role(request: Request, user=Depends(get_admin_user)):
+    return request.app.state.DEFAULT_USER_ROLE
+
+
+class UpdateRoleForm(BaseModel):
+    role: str
+
+
+@router.post("/signup/user/role")
+async def update_default_user_role(
+    request: Request, form_data: UpdateRoleForm, user=Depends(get_admin_user)
+):
+    if form_data.role in ["pending", "user", "admin"]:
+        request.app.state.DEFAULT_USER_ROLE = form_data.role
+    return request.app.state.DEFAULT_USER_ROLE

+ 11 - 1
backend/apps/web/routers/chats.py

@@ -165,7 +165,17 @@ async def update_chat_by_id(
 
 
 @router.delete("/{id}", response_model=bool)
-async def delete_chat_by_id(id: str, user=Depends(get_current_user)):
+async def delete_chat_by_id(request: Request, id: str, user=Depends(get_current_user)):
+
+    if (
+        user.role == "user"
+        and not request.app.state.USER_PERMISSIONS["chat"]["deletion"]
+    ):
+        raise HTTPException(
+            status_code=status.HTTP_401_UNAUTHORIZED,
+            detail=ERROR_MESSAGES.ACCESS_PROHIBITED,
+        )
+
     result = Chats.delete_chat_by_id_and_user_id(id, user.id)
     return result
 

+ 19 - 1
backend/apps/web/routers/users.py

@@ -1,4 +1,4 @@
-from fastapi import Response
+from fastapi import Response, Request
 from fastapi import Depends, FastAPI, HTTPException, status
 from datetime import datetime, timedelta
 from typing import List, Union, Optional
@@ -26,6 +26,24 @@ async def get_users(skip: int = 0, limit: int = 50, user=Depends(get_admin_user)
     return Users.get_users(skip, limit)
 
 
+############################
+# User Permissions
+############################
+
+
+@router.get("/permissions/user")
+async def get_user_permissions(request: Request, user=Depends(get_admin_user)):
+    return request.app.state.USER_PERMISSIONS
+
+
+@router.post("/permissions/user")
+async def update_user_permissions(
+    request: Request, form_data: dict, user=Depends(get_admin_user)
+):
+    request.app.state.USER_PERMISSIONS = form_data
+    return request.app.state.USER_PERMISSIONS
+
+
 ############################
 # UpdateUserRole
 ############################

+ 3 - 0
backend/config.py

@@ -93,6 +93,9 @@ DEFAULT_PROMPT_SUGGESTIONS = os.environ.get(
         },
     ],
 )
+DEFAULT_USER_ROLE = "pending"
+USER_PERMISSIONS = {"chat": {"deletion": True}}
+
 
 ####################################
 # WEBUI_VERSION

+ 57 - 0
src/lib/apis/auths/index.ts

@@ -178,6 +178,63 @@ export const getSignUpEnabledStatus = async (token: string) => {
 	return res;
 };
 
+export const getDefaultUserRole = async (token: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/auths/signup/user/role`, {
+		method: 'GET',
+		headers: {
+			'Content-Type': 'application/json',
+			Authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			error = err.detail;
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const updateDefaultUserRole = async (token: string, role: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/auths/signup/user/role`, {
+		method: 'POST',
+		headers: {
+			'Content-Type': 'application/json',
+			Authorization: `Bearer ${token}`
+		},
+		body: JSON.stringify({
+			role: role
+		})
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			error = err.detail;
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
 export const toggleSignUpEnabledStatus = async (token: string) => {
 	let error = null;
 

+ 1 - 1
src/lib/apis/chats/index.ts

@@ -272,7 +272,7 @@ export const deleteChatById = async (token: string, id: string) => {
 			return json;
 		})
 		.catch((err) => {
-			error = err;
+			error = err.detail;
 
 			console.log(err);
 			return null;

+ 57 - 0
src/lib/apis/users/index.ts

@@ -1,5 +1,62 @@
 import { WEBUI_API_BASE_URL } from '$lib/constants';
 
+export const getUserPermissions = async (token: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/users/permissions/user`, {
+		method: 'GET',
+		headers: {
+			'Content-Type': 'application/json',
+			Authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			error = err.detail;
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const updateUserPermissions = async (token: string, permissions: object) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/users/permissions/user`, {
+		method: 'POST',
+		headers: {
+			'Content-Type': 'application/json',
+			Authorization: `Bearer ${token}`
+		},
+		body: JSON.stringify({
+			...permissions
+		})
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			error = err.detail;
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
 export const updateUserRole = async (token: string, id: string, role: string) => {
 	let error = null;
 

+ 108 - 0
src/lib/components/admin/Settings/General.svelte

@@ -0,0 +1,108 @@
+<script lang="ts">
+	import {
+		getDefaultUserRole,
+		getSignUpEnabledStatus,
+		toggleSignUpEnabledStatus,
+		updateDefaultUserRole
+	} from '$lib/apis/auths';
+	import { onMount } from 'svelte';
+
+	export let saveHandler: Function;
+	let signUpEnabled = true;
+	let defaultUserRole = 'pending';
+
+	const toggleSignUpEnabled = async () => {
+		signUpEnabled = await toggleSignUpEnabledStatus(localStorage.token);
+	};
+
+	const updateDefaultUserRoleHandler = async (role) => {
+		defaultUserRole = await updateDefaultUserRole(localStorage.token, role);
+	};
+
+	onMount(async () => {
+		signUpEnabled = await getSignUpEnabledStatus(localStorage.token);
+		defaultUserRole = await getDefaultUserRole(localStorage.token);
+	});
+</script>
+
+<form
+	class="flex flex-col h-full justify-between space-y-3 text-sm"
+	on:submit|preventDefault={() => {
+		// console.log('submit');
+		saveHandler();
+	}}
+>
+	<div class=" space-y-3 pr-1.5 overflow-y-scroll max-h-80">
+		<div>
+			<div class=" mb-2 text-sm font-medium">General Settings</div>
+
+			<div class="  flex w-full justify-between">
+				<div class=" self-center text-xs font-medium">Enable New Sign Ups</div>
+
+				<button
+					class="p-1 px-3 text-xs flex rounded transition"
+					on:click={() => {
+						toggleSignUpEnabled();
+					}}
+					type="button"
+				>
+					{#if signUpEnabled}
+						<svg
+							xmlns="http://www.w3.org/2000/svg"
+							viewBox="0 0 16 16"
+							fill="currentColor"
+							class="w-4 h-4"
+						>
+							<path
+								d="M11.5 1A3.5 3.5 0 0 0 8 4.5V7H2.5A1.5 1.5 0 0 0 1 8.5v5A1.5 1.5 0 0 0 2.5 15h7a1.5 1.5 0 0 0 1.5-1.5v-5A1.5 1.5 0 0 0 9.5 7V4.5a2 2 0 1 1 4 0v1.75a.75.75 0 0 0 1.5 0V4.5A3.5 3.5 0 0 0 11.5 1Z"
+							/>
+						</svg>
+						<span class="ml-2 self-center">Enabled</span>
+					{:else}
+						<svg
+							xmlns="http://www.w3.org/2000/svg"
+							viewBox="0 0 16 16"
+							fill="currentColor"
+							class="w-4 h-4"
+						>
+							<path
+								fill-rule="evenodd"
+								d="M8 1a3.5 3.5 0 0 0-3.5 3.5V7A1.5 1.5 0 0 0 3 8.5v5A1.5 1.5 0 0 0 4.5 15h7a1.5 1.5 0 0 0 1.5-1.5v-5A1.5 1.5 0 0 0 11.5 7V4.5A3.5 3.5 0 0 0 8 1Zm2 6V4.5a2 2 0 1 0-4 0V7h4Z"
+								clip-rule="evenodd"
+							/>
+						</svg>
+
+						<span class="ml-2 self-center">Disabled</span>
+					{/if}
+				</button>
+			</div>
+
+			<div class=" flex w-full justify-between">
+				<div class=" self-center text-xs font-medium">Default User Role</div>
+				<div class="flex items-center relative">
+					<select
+						class="w-fit pr-8 rounded py-2 px-2 text-xs bg-transparent outline-none text-right"
+						bind:value={defaultUserRole}
+						placeholder="Select a theme"
+						on:change={(e) => {
+							updateDefaultUserRoleHandler(e.target.value);
+						}}
+					>
+						<option value="pending">Pending</option>
+						<option value="user">User</option>
+						<option value="admin">Admin</option>
+					</select>
+				</div>
+			</div>
+		</div>
+	</div>
+
+	<div class="flex justify-end pt-3 text-sm font-medium">
+		<button
+			class=" px-4 py-2 bg-emerald-600 hover:bg-emerald-700 text-gray-100 transition rounded"
+			type="submit"
+		>
+			Save
+		</button>
+	</div>
+</form>

+ 82 - 0
src/lib/components/admin/Settings/Users.svelte

@@ -0,0 +1,82 @@
+<script lang="ts">
+	import { getSignUpEnabledStatus, toggleSignUpEnabledStatus } from '$lib/apis/auths';
+	import { getUserPermissions, updateUserPermissions } from '$lib/apis/users';
+	import { onMount } from 'svelte';
+
+	export let saveHandler: Function;
+
+	let permissions = {
+		chat: {
+			deletion: true
+		}
+	};
+
+	onMount(async () => {
+		permissions = await getUserPermissions(localStorage.token);
+	});
+</script>
+
+<form
+	class="flex flex-col h-full justify-between space-y-3 text-sm"
+	on:submit|preventDefault={async () => {
+		// console.log('submit');
+		await updateUserPermissions(localStorage.token, permissions);
+		saveHandler();
+	}}
+>
+	<div class=" space-y-3 pr-1.5 overflow-y-scroll max-h-80">
+		<div>
+			<div class=" mb-2 text-sm font-medium">User Permission</div>
+
+			<div class="  flex w-full justify-between">
+				<div class=" self-center text-xs font-medium">Allow Chat Deletion</div>
+
+				<button
+					class="p-1 px-3 text-xs flex rounded transition"
+					on:click={() => {
+						permissions.chat.deletion = !permissions.chat.deletion;
+					}}
+					type="button"
+				>
+					{#if permissions.chat.deletion}
+						<svg
+							xmlns="http://www.w3.org/2000/svg"
+							viewBox="0 0 16 16"
+							fill="currentColor"
+							class="w-4 h-4"
+						>
+							<path
+								d="M11.5 1A3.5 3.5 0 0 0 8 4.5V7H2.5A1.5 1.5 0 0 0 1 8.5v5A1.5 1.5 0 0 0 2.5 15h7a1.5 1.5 0 0 0 1.5-1.5v-5A1.5 1.5 0 0 0 9.5 7V4.5a2 2 0 1 1 4 0v1.75a.75.75 0 0 0 1.5 0V4.5A3.5 3.5 0 0 0 11.5 1Z"
+							/>
+						</svg>
+						<span class="ml-2 self-center">Allow</span>
+					{:else}
+						<svg
+							xmlns="http://www.w3.org/2000/svg"
+							viewBox="0 0 16 16"
+							fill="currentColor"
+							class="w-4 h-4"
+						>
+							<path
+								fill-rule="evenodd"
+								d="M8 1a3.5 3.5 0 0 0-3.5 3.5V7A1.5 1.5 0 0 0 3 8.5v5A1.5 1.5 0 0 0 4.5 15h7a1.5 1.5 0 0 0 1.5-1.5v-5A1.5 1.5 0 0 0 11.5 7V4.5A3.5 3.5 0 0 0 8 1Zm2 6V4.5a2 2 0 1 0-4 0V7h4Z"
+								clip-rule="evenodd"
+							/>
+						</svg>
+
+						<span class="ml-2 self-center">Don't Allow</span>
+					{/if}
+				</button>
+			</div>
+		</div>
+	</div>
+
+	<div class="flex justify-end pt-3 text-sm font-medium">
+		<button
+			class=" px-4 py-2 bg-emerald-600 hover:bg-emerald-700 text-gray-100 transition rounded"
+			type="submit"
+		>
+			Save
+		</button>
+	</div>
+</form>

+ 107 - 0
src/lib/components/admin/SettingsModal.svelte

@@ -0,0 +1,107 @@
+<script>
+	import Modal from '../common/Modal.svelte';
+
+	import General from './Settings/General.svelte';
+	import Users from './Settings/Users.svelte';
+
+	export let show = false;
+
+	let selectedTab = 'general';
+</script>
+
+<Modal bind:show>
+	<div>
+		<div class=" flex justify-between dark:text-gray-300 px-5 py-4">
+			<div class=" text-lg font-medium self-center">Admin Settings</div>
+			<button
+				class="self-center"
+				on:click={() => {
+					show = false;
+				}}
+			>
+				<svg
+					xmlns="http://www.w3.org/2000/svg"
+					viewBox="0 0 20 20"
+					fill="currentColor"
+					class="w-5 h-5"
+				>
+					<path
+						d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z"
+					/>
+				</svg>
+			</button>
+		</div>
+		<hr class=" dark:border-gray-800" />
+
+		<div class="flex flex-col md:flex-row w-full p-4 md:space-x-4">
+			<div
+				class="tabs flex flex-row overflow-x-auto space-x-1 md:space-x-0 md:space-y-1 md:flex-col flex-1 md:flex-none md:w-40 dark:text-gray-200 text-xs text-left mb-3 md:mb-0"
+			>
+				<button
+					class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab ===
+					'general'
+						? 'bg-gray-200 dark:bg-gray-700'
+						: ' hover:bg-gray-300 dark:hover:bg-gray-800'}"
+					on:click={() => {
+						selectedTab = 'general';
+					}}
+				>
+					<div class=" self-center mr-2">
+						<svg
+							xmlns="http://www.w3.org/2000/svg"
+							viewBox="0 0 16 16"
+							fill="currentColor"
+							class="w-4 h-4"
+						>
+							<path
+								fill-rule="evenodd"
+								d="M6.955 1.45A.5.5 0 0 1 7.452 1h1.096a.5.5 0 0 1 .497.45l.17 1.699c.484.12.94.312 1.356.562l1.321-1.081a.5.5 0 0 1 .67.033l.774.775a.5.5 0 0 1 .034.67l-1.08 1.32c.25.417.44.873.561 1.357l1.699.17a.5.5 0 0 1 .45.497v1.096a.5.5 0 0 1-.45.497l-1.699.17c-.12.484-.312.94-.562 1.356l1.082 1.322a.5.5 0 0 1-.034.67l-.774.774a.5.5 0 0 1-.67.033l-1.322-1.08c-.416.25-.872.44-1.356.561l-.17 1.699a.5.5 0 0 1-.497.45H7.452a.5.5 0 0 1-.497-.45l-.17-1.699a4.973 4.973 0 0 1-1.356-.562L4.108 13.37a.5.5 0 0 1-.67-.033l-.774-.775a.5.5 0 0 1-.034-.67l1.08-1.32a4.971 4.971 0 0 1-.561-1.357l-1.699-.17A.5.5 0 0 1 1 8.548V7.452a.5.5 0 0 1 .45-.497l1.699-.17c.12-.484.312-.94.562-1.356L2.629 4.107a.5.5 0 0 1 .034-.67l.774-.774a.5.5 0 0 1 .67-.033L5.43 3.71a4.97 4.97 0 0 1 1.356-.561l.17-1.699ZM6 8c0 .538.212 1.026.558 1.385l.057.057a2 2 0 0 0 2.828-2.828l-.058-.056A2 2 0 0 0 6 8Z"
+								clip-rule="evenodd"
+							/>
+						</svg>
+					</div>
+					<div class=" self-center">General</div>
+				</button>
+
+				<button
+					class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab ===
+					'users'
+						? 'bg-gray-200 dark:bg-gray-700'
+						: ' hover:bg-gray-300 dark:hover:bg-gray-800'}"
+					on:click={() => {
+						selectedTab = 'users';
+					}}
+				>
+					<div class=" self-center mr-2">
+						<svg
+							xmlns="http://www.w3.org/2000/svg"
+							viewBox="0 0 16 16"
+							fill="currentColor"
+							class="w-4 h-4"
+						>
+							<path
+								d="M8 8a2.5 2.5 0 1 0 0-5 2.5 2.5 0 0 0 0 5ZM3.156 11.763c.16-.629.44-1.21.813-1.72a2.5 2.5 0 0 0-2.725 1.377c-.136.287.102.58.418.58h1.449c.01-.077.025-.156.045-.237ZM12.847 11.763c.02.08.036.16.046.237h1.446c.316 0 .554-.293.417-.579a2.5 2.5 0 0 0-2.722-1.378c.374.51.653 1.09.813 1.72ZM14 7.5a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0ZM3.5 9a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3ZM5 13c-.552 0-1.013-.455-.876-.99a4.002 4.002 0 0 1 7.753 0c.136.535-.324.99-.877.99H5Z"
+							/>
+						</svg>
+					</div>
+					<div class=" self-center">Users</div>
+				</button>
+			</div>
+			<div class="flex-1 md:min-h-[380px]">
+				{#if selectedTab === 'general'}
+					<General
+						saveHandler={() => {
+							show = false;
+						}}
+					/>
+				{:else if selectedTab === 'users'}
+					<Users
+						saveHandler={() => {
+							show = false;
+						}}
+					/>
+				{/if}
+			</div>
+		</div>
+	</div>
+</Modal>

+ 11 - 3
src/lib/components/layout/Sidebar.svelte

@@ -15,6 +15,7 @@
 		getChatListByTagName,
 		updateChatById
 	} from '$lib/apis/chats';
+	import toast from 'svelte-french-toast';
 
 	let show = false;
 	let navElement;
@@ -64,10 +65,17 @@
 	};
 
 	const deleteChat = async (id) => {
-		goto('/');
+		const res = await deleteChatById(localStorage.token, id).catch((error) => {
+			toast.error(error);
+			chatDeleteId = null;
 
-		await deleteChatById(localStorage.token, id);
-		await chats.set(await getChatList(localStorage.token));
+			return null;
+		});
+
+		if (res) {
+			goto('/');
+			await chats.set(await getChatList(localStorage.token));
+		}
 	};
 
 	const saveSettings = async (updated) => {

+ 19 - 41
src/routes/(app)/admin/+page.svelte

@@ -9,13 +9,14 @@
 	import { updateUserRole, getUsers, deleteUserById } from '$lib/apis/users';
 	import { getSignUpEnabledStatus, toggleSignUpEnabledStatus } from '$lib/apis/auths';
 	import EditUserModal from '$lib/components/admin/EditUserModal.svelte';
+	import SettingsModal from '$lib/components/admin/SettingsModal.svelte';
 
 	let loaded = false;
 	let users = [];
 
 	let selectedUser = null;
 
-	let signUpEnabled = true;
+	let showSettingsModal = false;
 	let showEditUserModal = false;
 
 	const updateRoleHandler = async (id, role) => {
@@ -50,17 +51,11 @@
 		}
 	};
 
-	const toggleSignUpEnabled = async () => {
-		signUpEnabled = await toggleSignUpEnabledStatus(localStorage.token);
-	};
-
 	onMount(async () => {
 		if ($user?.role !== 'admin') {
 			await goto('/');
 		} else {
 			users = await getUsers(localStorage.token);
-
-			signUpEnabled = await getSignUpEnabledStatus(localStorage.token);
 		}
 		loaded = true;
 	});
@@ -77,6 +72,8 @@
 	/>
 {/key}
 
+<SettingsModal bind:show={showSettingsModal} />
+
 <div
 	class=" bg-white dark:bg-gray-900 dark:text-gray-100 min-h-screen w-full flex justify-center font-mona"
 >
@@ -91,42 +88,23 @@
 								class="flex items-center space-x-1 border border-gray-200 dark:border-gray-600 px-3 py-1 rounded-lg"
 								type="button"
 								on:click={() => {
-									toggleSignUpEnabled();
+									showSettingsModal = !showSettingsModal;
 								}}
 							>
-								{#if signUpEnabled}
-									<svg
-										xmlns="http://www.w3.org/2000/svg"
-										viewBox="0 0 16 16"
-										fill="currentColor"
-										class="w-4 h-4"
-									>
-										<path
-											d="M11.5 1A3.5 3.5 0 0 0 8 4.5V7H2.5A1.5 1.5 0 0 0 1 8.5v5A1.5 1.5 0 0 0 2.5 15h7a1.5 1.5 0 0 0 1.5-1.5v-5A1.5 1.5 0 0 0 9.5 7V4.5a2 2 0 1 1 4 0v1.75a.75.75 0 0 0 1.5 0V4.5A3.5 3.5 0 0 0 11.5 1Z"
-										/>
-									</svg>
-
-									<div class=" text-xs">
-										New Sign Up <span class=" font-semibold">Enabled</span>
-									</div>
-								{:else}
-									<svg
-										xmlns="http://www.w3.org/2000/svg"
-										viewBox="0 0 16 16"
-										fill="currentColor"
-										class="w-4 h-4"
-									>
-										<path
-											fill-rule="evenodd"
-											d="M8 1a3.5 3.5 0 0 0-3.5 3.5V7A1.5 1.5 0 0 0 3 8.5v5A1.5 1.5 0 0 0 4.5 15h7a1.5 1.5 0 0 0 1.5-1.5v-5A1.5 1.5 0 0 0 11.5 7V4.5A3.5 3.5 0 0 0 8 1Zm2 6V4.5a2 2 0 1 0-4 0V7h4Z"
-											clip-rule="evenodd"
-										/>
-									</svg>
-
-									<div class=" text-xs">
-										New Sign Up <span class=" font-semibold">Disabled</span>
-									</div>
-								{/if}
+								<svg
+									xmlns="http://www.w3.org/2000/svg"
+									viewBox="0 0 16 16"
+									fill="currentColor"
+									class="w-4 h-4"
+								>
+									<path
+										fill-rule="evenodd"
+										d="M6.955 1.45A.5.5 0 0 1 7.452 1h1.096a.5.5 0 0 1 .497.45l.17 1.699c.484.12.94.312 1.356.562l1.321-1.081a.5.5 0 0 1 .67.033l.774.775a.5.5 0 0 1 .034.67l-1.08 1.32c.25.417.44.873.561 1.357l1.699.17a.5.5 0 0 1 .45.497v1.096a.5.5 0 0 1-.45.497l-1.699.17c-.12.484-.312.94-.562 1.356l1.082 1.322a.5.5 0 0 1-.034.67l-.774.774a.5.5 0 0 1-.67.033l-1.322-1.08c-.416.25-.872.44-1.356.561l-.17 1.699a.5.5 0 0 1-.497.45H7.452a.5.5 0 0 1-.497-.45l-.17-1.699a4.973 4.973 0 0 1-1.356-.562L4.108 13.37a.5.5 0 0 1-.67-.033l-.774-.775a.5.5 0 0 1-.034-.67l1.08-1.32a4.971 4.971 0 0 1-.561-1.357l-1.699-.17A.5.5 0 0 1 1 8.548V7.452a.5.5 0 0 1 .45-.497l1.699-.17c.12-.484.312-.94.562-1.356L2.629 4.107a.5.5 0 0 1 .034-.67l.774-.774a.5.5 0 0 1 .67-.033L5.43 3.71a4.97 4.97 0 0 1 1.356-.561l.17-1.699ZM6 8c0 .538.212 1.026.558 1.385l.057.057a2 2 0 0 0 2.828-2.828l-.058-.056A2 2 0 0 0 6 8Z"
+										clip-rule="evenodd"
+									/>
+								</svg>
+
+								<div class=" text-xs">Admin Settings</div>
 							</button>
 						</div>
 					</div>