Forráskód Böngészése

feat: admin details in account pending overlay

Timothy J. Baek 11 hónapja
szülő
commit
25336f85f3

+ 7 - 0
backend/apps/webui/main.py

@@ -14,6 +14,8 @@ from apps.webui.routers import (
 )
 from config import (
     WEBUI_BUILD_HASH,
+    SHOW_ADMIN_DETAILS,
+    ADMIN_EMAIL,
     WEBUI_AUTH,
     DEFAULT_MODELS,
     DEFAULT_PROMPT_SUGGESTIONS,
@@ -37,6 +39,11 @@ app.state.config = AppConfig()
 app.state.config.ENABLE_SIGNUP = ENABLE_SIGNUP
 app.state.config.JWT_EXPIRES_IN = JWT_EXPIRES_IN
 
+
+app.state.config.SHOW_ADMIN_DETAILS = SHOW_ADMIN_DETAILS
+app.state.config.ADMIN_EMAIL = ADMIN_EMAIL
+
+
 app.state.config.DEFAULT_MODELS = DEFAULT_MODELS
 app.state.config.DEFAULT_PROMPT_SUGGESTIONS = DEFAULT_PROMPT_SUGGESTIONS
 app.state.config.DEFAULT_USER_ROLE = DEFAULT_USER_ROLE

+ 60 - 45
backend/apps/webui/routers/auths.py

@@ -270,72 +270,87 @@ async def add_user(form_data: AddUserForm, user=Depends(get_admin_user)):
 
 
 ############################
-# ToggleSignUp
+# GetAdminDetails
 ############################
 
 
-@router.get("/signup/enabled", response_model=bool)
-async def get_sign_up_status(request: Request, user=Depends(get_admin_user)):
-    return request.app.state.config.ENABLE_SIGNUP
+@router.get("/admin/details")
+async def get_admin_details(request: Request, user=Depends(get_current_user)):
+    if request.app.state.config.SHOW_ADMIN_DETAILS:
+        admin_email = request.app.state.config.ADMIN_EMAIL
+        admin_name = None
+
+        print(admin_email, admin_name)
 
+        if admin_email:
+            admin = Users.get_user_by_email(admin_email)
+            if admin:
+                admin_name = admin.name
+        else:
+            admin = Users.get_first_user()
+            if admin:
+                admin_email = admin.email
+                admin_name = admin.name
 
-@router.get("/signup/enabled/toggle", response_model=bool)
-async def toggle_sign_up(request: Request, user=Depends(get_admin_user)):
-    request.app.state.config.ENABLE_SIGNUP = not request.app.state.config.ENABLE_SIGNUP
-    return request.app.state.config.ENABLE_SIGNUP
+        return {
+            "name": admin_name,
+            "email": admin_email,
+        }
+    else:
+        raise HTTPException(400, detail=ERROR_MESSAGES.ACTION_PROHIBITED)
 
 
 ############################
-# Default User Role
+# ToggleSignUp
 ############################
 
 
-@router.get("/signup/user/role")
-async def get_default_user_role(request: Request, user=Depends(get_admin_user)):
-    return request.app.state.config.DEFAULT_USER_ROLE
+@router.get("/admin/config")
+async def get_admin_config(request: Request, user=Depends(get_admin_user)):
+    return {
+        "SHOW_ADMIN_DETAILS": request.app.state.config.SHOW_ADMIN_DETAILS,
+        "ENABLE_SIGNUP": request.app.state.config.ENABLE_SIGNUP,
+        "DEFAULT_USER_ROLE": request.app.state.config.DEFAULT_USER_ROLE,
+        "JWT_EXPIRES_IN": request.app.state.config.JWT_EXPIRES_IN,
+        "ENABLE_COMMUNITY_SHARING": request.app.state.config.ENABLE_COMMUNITY_SHARING,
+    }
 
 
-class UpdateRoleForm(BaseModel):
-    role: str
+class AdminConfig(BaseModel):
+    SHOW_ADMIN_DETAILS: bool
+    ENABLE_SIGNUP: bool
+    DEFAULT_USER_ROLE: str
+    JWT_EXPIRES_IN: str
+    ENABLE_COMMUNITY_SHARING: bool
 
 
-@router.post("/signup/user/role")
-async def update_default_user_role(
-    request: Request, form_data: UpdateRoleForm, user=Depends(get_admin_user)
+@router.post("/admin/config")
+async def update_admin_config(
+    request: Request, form_data: AdminConfig, user=Depends(get_admin_user)
 ):
-    if form_data.role in ["pending", "user", "admin"]:
-        request.app.state.config.DEFAULT_USER_ROLE = form_data.role
-    return request.app.state.config.DEFAULT_USER_ROLE
-
-
-############################
-# JWT Expiration
-############################
-
-
-@router.get("/token/expires")
-async def get_token_expires_duration(request: Request, user=Depends(get_admin_user)):
-    return request.app.state.config.JWT_EXPIRES_IN
-
-
-class UpdateJWTExpiresDurationForm(BaseModel):
-    duration: str
+    request.app.state.config.SHOW_ADMIN_DETAILS = form_data.SHOW_ADMIN_DETAILS
+    request.app.state.config.ENABLE_SIGNUP = form_data.ENABLE_SIGNUP
 
+    if form_data.DEFAULT_USER_ROLE in ["pending", "user", "admin"]:
+        request.app.state.config.DEFAULT_USER_ROLE = form_data.DEFAULT_USER_ROLE
 
-@router.post("/token/expires/update")
-async def update_token_expires_duration(
-    request: Request,
-    form_data: UpdateJWTExpiresDurationForm,
-    user=Depends(get_admin_user),
-):
     pattern = r"^(-1|0|(-?\d+(\.\d+)?)(ms|s|m|h|d|w))$"
 
     # Check if the input string matches the pattern
-    if re.match(pattern, form_data.duration):
-        request.app.state.config.JWT_EXPIRES_IN = form_data.duration
-        return request.app.state.config.JWT_EXPIRES_IN
-    else:
-        return request.app.state.config.JWT_EXPIRES_IN
+    if re.match(pattern, form_data.JWT_EXPIRES_IN):
+        request.app.state.config.JWT_EXPIRES_IN = form_data.JWT_EXPIRES_IN
+
+    request.app.state.config.ENABLE_COMMUNITY_SHARING = (
+        form_data.ENABLE_COMMUNITY_SHARING
+    )
+
+    return {
+        "SHOW_ADMIN_DETAILS": request.app.state.config.SHOW_ADMIN_DETAILS,
+        "ENABLE_SIGNUP": request.app.state.config.ENABLE_SIGNUP,
+        "DEFAULT_USER_ROLE": request.app.state.config.DEFAULT_USER_ROLE,
+        "JWT_EXPIRES_IN": request.app.state.config.JWT_EXPIRES_IN,
+        "ENABLE_COMMUNITY_SHARING": request.app.state.config.ENABLE_COMMUNITY_SHARING,
+    }
 
 
 ############################

+ 6 - 1
backend/apps/webui/routers/users.py

@@ -19,7 +19,12 @@ from apps.webui.models.users import (
 from apps.webui.models.auths import Auths
 from apps.webui.models.chats import Chats
 
-from utils.utils import get_verified_user, get_password_hash, get_admin_user
+from utils.utils import (
+    get_verified_user,
+    get_password_hash,
+    get_current_user,
+    get_admin_user,
+)
 from constants import ERROR_MESSAGES
 
 from config import SRC_LOG_LEVELS

+ 14 - 0
backend/config.py

@@ -601,6 +601,20 @@ WEBUI_BANNERS = PersistentConfig(
     [BannerModel(**banner) for banner in json.loads("[]")],
 )
 
+
+SHOW_ADMIN_DETAILS = PersistentConfig(
+    "SHOW_ADMIN_DETAILS",
+    "auth.admin.show",
+    os.environ.get("SHOW_ADMIN_DETAILS", "true").lower() == "true",
+)
+
+ADMIN_EMAIL = PersistentConfig(
+    "ADMIN_EMAIL",
+    "auth.admin.email",
+    os.environ.get("ADMIN_EMAIL", None),
+)
+
+
 ####################################
 # WEBUI_SECRET_KEY
 ####################################

+ 1 - 17
backend/main.py

@@ -879,23 +879,7 @@ class UrlForm(BaseModel):
 async def update_webhook_url(form_data: UrlForm, user=Depends(get_admin_user)):
     app.state.config.WEBHOOK_URL = form_data.url
     webui_app.state.WEBHOOK_URL = app.state.config.WEBHOOK_URL
-
-    return {
-        "url": app.state.config.WEBHOOK_URL,
-    }
-
-
-@app.get("/api/community_sharing", response_model=bool)
-async def get_community_sharing_status(request: Request, user=Depends(get_admin_user)):
-    return webui_app.state.config.ENABLE_COMMUNITY_SHARING
-
-
-@app.get("/api/community_sharing/toggle", response_model=bool)
-async def toggle_community_sharing(request: Request, user=Depends(get_admin_user)):
-    webui_app.state.config.ENABLE_COMMUNITY_SHARING = (
-        not webui_app.state.config.ENABLE_COMMUNITY_SHARING
-    )
-    return webui_app.state.config.ENABLE_COMMUNITY_SHARING
+    return {"url": app.state.config.WEBHOOK_URL}
 
 
 @app.get("/api/version")

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

@@ -1,5 +1,87 @@
 import { WEBUI_API_BASE_URL } from '$lib/constants';
 
+export const getAdminDetails = async (token: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/auths/admin/details`, {
+		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 getAdminConfig = async (token: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/auths/admin/config`, {
+		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 updateAdminConfig = async (token: string, body: object) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/auths/admin/config`, {
+		method: 'POST',
+		headers: {
+			'Content-Type': 'application/json',
+			Authorization: `Bearer ${token}`
+		},
+		body: JSON.stringify(body)
+	})
+		.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 getSessionUser = async (token: string) => {
 	let error = null;
 

+ 82 - 161
src/lib/components/admin/Settings/General.svelte

@@ -6,61 +6,44 @@
 		updateWebhookUrl
 	} from '$lib/apis';
 	import {
+		getAdminConfig,
 		getDefaultUserRole,
 		getJWTExpiresDuration,
 		getSignUpEnabledStatus,
 		toggleSignUpEnabledStatus,
+		updateAdminConfig,
 		updateDefaultUserRole,
 		updateJWTExpiresDuration
 	} from '$lib/apis/auths';
+	import Switch from '$lib/components/common/Switch.svelte';
 	import { onMount, getContext } from 'svelte';
 
 	const i18n = getContext('i18n');
 
 	export let saveHandler: Function;
-	let signUpEnabled = true;
-	let defaultUserRole = 'pending';
-	let JWTExpiresIn = '';
 
+	let adminConfig = null;
 	let webhookUrl = '';
-	let communitySharingEnabled = true;
 
-	const toggleSignUpEnabled = async () => {
-		signUpEnabled = await toggleSignUpEnabledStatus(localStorage.token);
-	};
-
-	const updateDefaultUserRoleHandler = async (role) => {
-		defaultUserRole = await updateDefaultUserRole(localStorage.token, role);
-	};
-
-	const updateJWTExpiresDurationHandler = async (duration) => {
-		JWTExpiresIn = await updateJWTExpiresDuration(localStorage.token, duration);
-	};
-
-	const updateWebhookUrlHandler = async () => {
+	const updateHandler = async () => {
 		webhookUrl = await updateWebhookUrl(localStorage.token, webhookUrl);
-	};
+		const res = await updateAdminConfig(localStorage.token, adminConfig);
 
-	const toggleCommunitySharingEnabled = async () => {
-		communitySharingEnabled = await toggleCommunitySharingEnabledStatus(localStorage.token);
+		if (res) {
+			toast.success(i18n.t('Settings updated successfully'));
+		} else {
+			toast.error(i18n.t('Failed to update settings'));
+		}
 	};
 
 	onMount(async () => {
 		await Promise.all([
 			(async () => {
-				signUpEnabled = await getSignUpEnabledStatus(localStorage.token);
-			})(),
-			(async () => {
-				defaultUserRole = await getDefaultUserRole(localStorage.token);
-			})(),
-			(async () => {
-				JWTExpiresIn = await getJWTExpiresDuration(localStorage.token);
+				adminConfig = await getAdminConfig(localStorage.token);
 			})(),
+
 			(async () => {
 				webhookUrl = await getWebhookUrl(localStorage.token);
-			})(),
-			(async () => {
-				communitySharingEnabled = await getCommunitySharingEnabledStatus(localStorage.token);
 			})()
 		]);
 	});
@@ -69,156 +52,94 @@
 <form
 	class="flex flex-col h-full justify-between space-y-3 text-sm"
 	on:submit|preventDefault={() => {
-		updateJWTExpiresDurationHandler(JWTExpiresIn);
-		updateWebhookUrlHandler();
+		updateHandler();
 		saveHandler();
 	}}
 >
-	<div class=" space-y-3 pr-1.5 overflow-y-scroll max-h-80">
-		<div>
-			<div class=" mb-2 text-sm font-medium">{$i18n.t('General Settings')}</div>
-
-			<div class="  flex w-full justify-between">
-				<div class=" self-center text-xs font-medium">{$i18n.t('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">{$i18n.t('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">{$i18n.t('Disabled')}</span>
-					{/if}
-				</button>
-			</div>
+	<div class=" space-y-3 pr-1.5 overflow-y-scroll max-h-[22rem]">
+		{#if adminConfig !== null}
+			<div>
+				<div class=" mb-3 text-sm font-medium">{$i18n.t('General Settings')}</div>
+
+				<div class="  flex w-full justify-between pr-2">
+					<div class=" self-center text-xs font-medium">{$i18n.t('Enable New Sign Ups')}</div>
 
-			<div class=" flex w-full justify-between">
-				<div class=" self-center text-xs font-medium">{$i18n.t('Default User Role')}</div>
-				<div class="flex items-center relative">
-					<select
-						class="dark:bg-gray-900 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">{$i18n.t('pending')}</option>
-						<option value="user">{$i18n.t('user')}</option>
-						<option value="admin">{$i18n.t('admin')}</option>
-					</select>
+					<Switch bind:state={adminConfig.ENABLE_SIGNUP} />
 				</div>
-			</div>
 
-			<div class="  flex w-full justify-between">
-				<div class=" self-center text-xs font-medium">{$i18n.t('Enable Community Sharing')}</div>
-
-				<button
-					class="p-1 px-3 text-xs flex rounded transition"
-					on:click={() => {
-						toggleCommunitySharingEnabled();
-					}}
-					type="button"
-				>
-					{#if communitySharingEnabled}
-						<svg
-							xmlns="http://www.w3.org/2000/svg"
-							viewBox="0 0 16 16"
-							fill="currentColor"
-							class="w-4 h-4"
+				<div class="  my-3 flex w-full justify-between">
+					<div class=" self-center text-xs font-medium">{$i18n.t('Default User Role')}</div>
+					<div class="flex items-center relative">
+						<select
+							class="dark:bg-gray-900 w-fit pr-8 rounded px-2 text-xs bg-transparent outline-none text-right"
+							bind:value={adminConfig.DEFAULT_USER_ROLE}
+							placeholder="Select a role"
 						>
-							<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">{$i18n.t('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">{$i18n.t('Disabled')}</span>
-					{/if}
-				</button>
-			</div>
+							<option value="pending">{$i18n.t('pending')}</option>
+							<option value="user">{$i18n.t('user')}</option>
+							<option value="admin">{$i18n.t('admin')}</option>
+						</select>
+					</div>
+				</div>
 
-			<hr class=" dark:border-gray-700 my-3" />
+				<hr class=" dark:border-gray-850 my-2" />
 
-			<div class=" w-full justify-between">
-				<div class="flex w-full justify-between">
-					<div class=" self-center text-xs font-medium">{$i18n.t('Webhook URL')}</div>
-				</div>
+				<div class="my-3 flex w-full items-center justify-between pr-2">
+					<div class=" self-center text-xs font-medium">
+						{$i18n.t('Show Admin Details in Account Pending Overlay')}
+					</div>
 
-				<div class="flex mt-2 space-x-2">
-					<input
-						class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
-						type="text"
-						placeholder={`https://example.com/webhook`}
-						bind:value={webhookUrl}
-					/>
+					<Switch bind:state={adminConfig.SHOW_ADMIN_DETAILS} />
 				</div>
-			</div>
 
-			<hr class=" dark:border-gray-700 my-3" />
+				<div class="my-3 flex w-full items-center justify-between pr-2">
+					<div class=" self-center text-xs font-medium">{$i18n.t('Enable Community Sharing')}</div>
 
-			<div class=" w-full justify-between">
-				<div class="flex w-full justify-between">
-					<div class=" self-center text-xs font-medium">{$i18n.t('JWT Expiration')}</div>
+					<Switch bind:state={adminConfig.ENABLE_COMMUNITY_SHARING} />
 				</div>
 
-				<div class="flex mt-2 space-x-2">
-					<input
-						class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
-						type="text"
-						placeholder={`e.g.) "30m","1h", "10d". `}
-						bind:value={JWTExpiresIn}
-					/>
+				<hr class=" dark:border-gray-850 my-2" />
+
+				<div class=" w-full justify-between">
+					<div class="flex w-full justify-between">
+						<div class=" self-center text-xs font-medium">{$i18n.t('JWT Expiration')}</div>
+					</div>
+
+					<div class="flex mt-2 space-x-2">
+						<input
+							class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
+							type="text"
+							placeholder={`e.g.) "30m","1h", "10d". `}
+							bind:value={adminConfig.JWT_EXPIRES_IN}
+						/>
+					</div>
+
+					<div class="mt-2 text-xs text-gray-400 dark:text-gray-500">
+						{$i18n.t('Valid time units:')}
+						<span class=" text-gray-300 font-medium"
+							>{$i18n.t("'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.")}</span
+						>
+					</div>
 				</div>
 
-				<div class="mt-2 text-xs text-gray-400 dark:text-gray-500">
-					{$i18n.t('Valid time units:')}
-					<span class=" text-gray-300 font-medium"
-						>{$i18n.t("'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.")}</span
-					>
+				<hr class=" dark:border-gray-850 my-2" />
+
+				<div class=" w-full justify-between">
+					<div class="flex w-full justify-between">
+						<div class=" self-center text-xs font-medium">{$i18n.t('Webhook URL')}</div>
+					</div>
+
+					<div class="flex mt-2 space-x-2">
+						<input
+							class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
+							type="text"
+							placeholder={`https://example.com/webhook`}
+							bind:value={webhookUrl}
+						/>
+					</div>
 				</div>
 			</div>
-		</div>
+		{/if}
 	</div>
 
 	<div class="flex justify-end pt-3 text-sm font-medium">

+ 59 - 0
src/lib/components/layout/Overlay/AccountPending.svelte

@@ -0,0 +1,59 @@
+<script lang="ts">
+	import { getAdminDetails } from '$lib/apis/auths';
+	import { onMount, tick, getContext } from 'svelte';
+
+	const i18n = getContext('i18n');
+
+	let adminDetails = null;
+
+	onMount(async () => {
+		adminDetails = await getAdminDetails(localStorage.token).catch((err) => {
+			console.error(err);
+			return null;
+		});
+	});
+</script>
+
+<div class="fixed w-full h-full flex z-[999]">
+	<div
+		class="absolute w-full h-full backdrop-blur-lg bg-white/10 dark:bg-gray-900/50 flex justify-center"
+	>
+		<div class="m-auto pb-10 flex flex-col justify-center">
+			<div class="max-w-md">
+				<div class="text-center dark:text-white text-2xl font-medium z-50">
+					Account Activation Pending<br /> Contact Admin for WebUI Access
+				</div>
+
+				<div class=" mt-4 text-center text-sm dark:text-gray-200 w-full">
+					Your account status is currently pending activation.<br /> To access the WebUI, please reach
+					out to the administrator. Admins can manage user statuses from the Admin Panel.
+				</div>
+
+				{#if adminDetails}
+					<div class="mt-4 text-sm font-medium text-center">
+						<div>Admin: {adminDetails.name} ({adminDetails.email})</div>
+					</div>
+				{/if}
+
+				<div class=" mt-6 mx-auto relative group w-fit">
+					<button
+						class="relative z-20 flex px-5 py-2 rounded-full bg-white border border-gray-100 dark:border-none hover:bg-gray-100 text-gray-700 transition font-medium text-sm"
+						on:click={async () => {
+							location.href = '/';
+						}}
+					>
+						{$i18n.t('Check Again')}
+					</button>
+
+					<button
+						class="text-xs text-center w-full mt-2 text-gray-400 underline"
+						on:click={async () => {
+							localStorage.removeItem('token');
+							location.href = '/auth';
+						}}>{$i18n.t('Sign Out')}</button
+					>
+				</div>
+			</div>
+		</div>
+	</div>
+</div>

+ 7 - 39
src/routes/(app)/+layout.svelte

@@ -37,6 +37,8 @@
 	import { getBanners } from '$lib/apis/configs';
 	import { getUserSettings } from '$lib/apis/users';
 	import Help from '$lib/components/layout/Help.svelte';
+	import AccountPending from '$lib/components/layout/Overlay/AccountPending.svelte';
+	import { error } from '@sveltejs/kit';
 
 	const i18n = getContext('i18n');
 
@@ -74,7 +76,10 @@
 				// IndexedDB Not Found
 			}
 
-			const userSettings = await getUserSettings(localStorage.token);
+			const userSettings = await getUserSettings(localStorage.token).catch((error) => {
+				console.error(error);
+				return null;
+			});
 
 			if (userSettings) {
 				await settings.set(userSettings.ui);
@@ -186,44 +191,7 @@
 	>
 		{#if loaded}
 			{#if !['user', 'admin'].includes($user.role)}
-				<div class="fixed w-full h-full flex z-[999]">
-					<div
-						class="absolute w-full h-full backdrop-blur-lg bg-white/10 dark:bg-gray-900/50 flex justify-center"
-					>
-						<div class="m-auto pb-10 flex flex-col justify-center">
-							<div class="max-w-md">
-								<div class="text-center dark:text-white text-2xl font-medium z-50">
-									Account Activation Pending<br /> Contact Admin for WebUI Access
-								</div>
-
-								<div class=" mt-4 text-center text-sm dark:text-gray-200 w-full">
-									Your account status is currently pending activation. To access the WebUI, please
-									reach out to the administrator. Admins can manage user statuses from the Admin
-									Panel.
-								</div>
-
-								<div class=" mt-6 mx-auto relative group w-fit">
-									<button
-										class="relative z-20 flex px-5 py-2 rounded-full bg-white border border-gray-100 dark:border-none hover:bg-gray-100 text-gray-700 transition font-medium text-sm"
-										on:click={async () => {
-											location.href = '/';
-										}}
-									>
-										{$i18n.t('Check Again')}
-									</button>
-
-									<button
-										class="text-xs text-center w-full mt-2 text-gray-400 underline"
-										on:click={async () => {
-											localStorage.removeItem('token');
-											location.href = '/auth';
-										}}>{$i18n.t('Sign Out')}</button
-									>
-								</div>
-							</div>
-						</div>
-					</div>
-				</div>
+				<AccountPending />
 			{:else if localDBChats.length > 0}
 				<div class="fixed w-full h-full flex z-50">
 					<div