Przeglądaj źródła

Merge pull request #2579 from cheahjs/feat/disable-community-sharing

feat: toggleable community sharing
Timothy Jaeryang Baek 11 miesięcy temu
rodzic
commit
c62cb43f7e

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

@@ -25,6 +25,7 @@ from config import (
     JWT_EXPIRES_IN,
     WEBUI_BANNERS,
     AppConfig,
+    ENABLE_COMMUNITY_SHARING,
 )
 
 app = FastAPI()
@@ -43,6 +44,7 @@ app.state.config.USER_PERMISSIONS = USER_PERMISSIONS
 app.state.config.WEBHOOK_URL = WEBHOOK_URL
 app.state.config.BANNERS = WEBUI_BANNERS
 
+app.state.config.ENABLE_COMMUNITY_SHARING = ENABLE_COMMUNITY_SHARING
 
 app.state.MODELS = {}
 app.state.AUTH_TRUSTED_EMAIL_HEADER = WEBUI_AUTH_TRUSTED_EMAIL_HEADER

+ 5 - 0
backend/config.py

@@ -568,6 +568,11 @@ WEBHOOK_URL = PersistentConfig(
 
 ENABLE_ADMIN_EXPORT = os.environ.get("ENABLE_ADMIN_EXPORT", "True").lower() == "true"
 
+ENABLE_COMMUNITY_SHARING = PersistentConfig(
+    "ENABLE_COMMUNITY_SHARING",
+    "ui.enable_community_sharing",
+    os.environ.get("ENABLE_COMMUNITY_SHARING", "True").lower() == "true",
+)
 
 class BannerModel(BaseModel):
     id: str

+ 21 - 5
backend/main.py

@@ -358,14 +358,17 @@ async def get_app_config():
         "status": True,
         "name": WEBUI_NAME,
         "version": VERSION,
-        "auth": WEBUI_AUTH,
-        "auth_trusted_header": bool(webui_app.state.AUTH_TRUSTED_EMAIL_HEADER),
-        "enable_signup": webui_app.state.config.ENABLE_SIGNUP,
-        "enable_image_generation": images_app.state.config.ENABLED,
-        "enable_admin_export": ENABLE_ADMIN_EXPORT,
         "default_locale": default_locale,
         "default_models": webui_app.state.config.DEFAULT_MODELS,
         "default_prompt_suggestions": webui_app.state.config.DEFAULT_PROMPT_SUGGESTIONS,
+        "features": {
+            "auth": WEBUI_AUTH,
+            "auth_trusted_header": bool(webui_app.state.AUTH_TRUSTED_EMAIL_HEADER),
+            "enable_signup": webui_app.state.config.ENABLE_SIGNUP,
+            "enable_image_generation": images_app.state.config.ENABLED,
+            "enable_admin_export": ENABLE_ADMIN_EXPORT,
+            "enable_community_sharing": webui_app.state.config.ENABLE_COMMUNITY_SHARING,
+        },
     }
 
 
@@ -416,6 +419,19 @@ async def update_webhook_url(form_data: UrlForm, user=Depends(get_admin_user)):
     }
 
 
+@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
+
+
 @app.get("/api/version")
 async def get_app_config():
     return {

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

@@ -1,4 +1,4 @@
-import { WEBUI_BASE_URL } from '$lib/constants';
+import { WEBUI_API_BASE_URL, WEBUI_BASE_URL } from '$lib/constants';
 
 export const getModels = async (token: string = '') => {
 	let error = null;
@@ -246,6 +246,60 @@ export const updateWebhookUrl = async (token: string, url: string) => {
 	return res.url;
 };
 
+export const getCommunitySharingEnabledStatus = async (token: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_BASE_URL}/api/community_sharing`, {
+		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;
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const toggleCommunitySharingEnabledStatus = async (token: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_BASE_URL}/api/community_sharing/toggle`, {
+		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 getModelConfig = async (token: string): Promise<GlobalModelConfig> => {
 	let error = null;
 

+ 1 - 1
src/lib/components/admin/Settings/Database.svelte

@@ -34,7 +34,7 @@
 		<div>
 			<div class=" mb-2 text-sm font-medium">{$i18n.t('Database')}</div>
 
-			{#if $config?.enable_admin_export ?? true}
+			{#if $config?.features.enable_admin_export ?? true}
 				<div class="  flex w-full justify-between">
 					<!-- <div class=" self-center text-xs font-medium">{$i18n.t('Allow Chat Deletion')}</div> -->
 

+ 69 - 5
src/lib/components/admin/Settings/General.svelte

@@ -1,5 +1,10 @@
 <script lang="ts">
-	import { getWebhookUrl, updateWebhookUrl } from '$lib/apis';
+	import {
+		getCommunitySharingEnabledStatus,
+		getWebhookUrl,
+		toggleCommunitySharingEnabledStatus,
+		updateWebhookUrl
+	} from '$lib/apis';
 	import {
 		getDefaultUserRole,
 		getJWTExpiresDuration,
@@ -18,6 +23,7 @@
 	let JWTExpiresIn = '';
 
 	let webhookUrl = '';
+	let communitySharingEnabled = true;
 
 	const toggleSignUpEnabled = async () => {
 		signUpEnabled = await toggleSignUpEnabledStatus(localStorage.token);
@@ -35,11 +41,28 @@
 		webhookUrl = await updateWebhookUrl(localStorage.token, webhookUrl);
 	};
 
+	const toggleCommunitySharingEnabled = async () => {
+		communitySharingEnabled = await toggleCommunitySharingEnabledStatus(localStorage.token);
+	};
+
 	onMount(async () => {
-		signUpEnabled = await getSignUpEnabledStatus(localStorage.token);
-		defaultUserRole = await getDefaultUserRole(localStorage.token);
-		JWTExpiresIn = await getJWTExpiresDuration(localStorage.token);
-		webhookUrl = await getWebhookUrl(localStorage.token);
+		await Promise.all([
+			(async () => {
+				signUpEnabled = await getSignUpEnabledStatus(localStorage.token);
+			})(),
+			(async () => {
+				defaultUserRole = await getDefaultUserRole(localStorage.token);
+			})(),
+			(async () => {
+				JWTExpiresIn = await getJWTExpiresDuration(localStorage.token);
+			})(),
+			(async () => {
+				webhookUrl = await getWebhookUrl(localStorage.token);
+			})(),
+			(async () => {
+				communitySharingEnabled = await getCommunitySharingEnabledStatus(localStorage.token);
+			})()
+		]);
 	});
 </script>
 
@@ -114,6 +137,47 @@
 				</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"
+						>
+							<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>
+
 			<hr class=" dark:border-gray-700 my-3" />
 
 			<div class=" w-full justify-between">

+ 1 - 1
src/lib/components/chat/Messages/ResponseMessage.svelte

@@ -693,7 +693,7 @@
 											</button>
 										</Tooltip>
 
-										{#if $config.enable_image_generation && !readOnly}
+										{#if $config?.features.enable_image_generation && !readOnly}
 											<Tooltip content="Generate Image" placement="bottom">
 												<button
 													class="{isLastMessage

+ 13 - 11
src/lib/components/chat/ShareChatModal.svelte

@@ -1,6 +1,6 @@
 <script lang="ts">
 	import { getContext, onMount } from 'svelte';
-	import { models } from '$lib/stores';
+	import { models, config } from '$lib/stores';
 
 	import { toast } from 'svelte-sonner';
 	import { deleteSharedChatById, getChatById, shareChatById } from '$lib/apis/chats';
@@ -134,16 +134,18 @@
 				<div class="flex justify-end">
 					<div class="flex flex-col items-end space-x-1 mt-1.5">
 						<div class="flex gap-1">
-							<button
-								class=" self-center px-3.5 py-2 rounded-xl text-sm font-medium bg-gray-100 hover:bg-gray-200 text-gray-800 dark:bg-gray-850 dark:hover:bg-gray-800 dark:text-white"
-								type="button"
-								on:click={() => {
-									shareChat();
-									show = false;
-								}}
-							>
-								{$i18n.t('Share to OpenWebUI Community')}
-							</button>
+							{#if $config?.features.enable_community_sharing}
+								<button
+									class=" self-center px-3.5 py-2 rounded-xl text-sm font-medium bg-gray-100 hover:bg-gray-200 text-gray-800 dark:bg-gray-850 dark:hover:bg-gray-800 dark:text-white"
+									type="button"
+									on:click={() => {
+										shareChat();
+										show = false;
+									}}
+								>
+									{$i18n.t('Share to OpenWebUI Community')}
+								</button>
+							{/if}
 
 							<button
 								class=" self-center flex items-center gap-1 px-3.5 py-2 rounded-xl text-sm font-medium bg-emerald-600 hover:bg-emerald-500 text-white"

+ 14 - 9
src/lib/stores/index.ts

@@ -129,15 +129,20 @@ type Prompt = {
 };
 
 type Config = {
-	status?: boolean;
-	name?: string;
-	version?: string;
-	default_locale?: string;
-	images?: boolean;
-	default_models?: string[];
-	default_prompt_suggestions?: PromptSuggestion[];
-	auth_trusted_header?: boolean;
-	model_config?: GlobalModelConfig;
+	status: boolean;
+	name: string;
+	version: string;
+	default_locale: string;
+	default_models: string[];
+	default_prompt_suggestions: PromptSuggestion[];
+	features: {
+		auth: boolean;
+		enable_signup: boolean;
+		auth_trusted_header: boolean;
+		enable_image_generation: boolean;
+		enable_admin_export: boolean;
+		enable_community_sharing: boolean;
+	};
 };
 
 type PromptSuggestion = {

+ 3 - 3
src/routes/auth/+page.svelte

@@ -60,7 +60,7 @@
 			await goto('/');
 		}
 		loaded = true;
-		if (($config?.auth_trusted_header ?? false) || $config?.auth === false) {
+		if (($config?.features.auth_trusted_header ?? false) || $config?.features.auth === false) {
 			await signInHandler();
 		}
 	});
@@ -102,7 +102,7 @@
 		</div> -->
 
 		<div class="w-full sm:max-w-md px-10 min-h-screen flex flex-col text-center">
-			{#if ($config?.auth_trusted_header ?? false) || $config?.auth === false}
+			{#if ($config?.features.auth_trusted_header ?? false) || $config?.features.auth === false}
 				<div class=" my-auto pb-10 w-full">
 					<div
 						class="flex items-center justify-center gap-3 text-xl sm:text-2xl text-center font-bold dark:text-gray-200"
@@ -194,7 +194,7 @@
 								{mode === 'signin' ? $i18n.t('Sign in') : $i18n.t('Create Account')}
 							</button>
 
-							{#if $config.enable_signup}
+							{#if $config?.features.enable_signup}
 								<div class=" mt-4 text-sm text-center">
 									{mode === 'signin'
 										? $i18n.t("Don't have an account?")