Timothy Jaeryang Baek 5 月之前
父节点
当前提交
b80ec76435

+ 7 - 19
backend/open_webui/config.py

@@ -739,12 +739,12 @@ DEFAULT_USER_ROLE = PersistentConfig(
     os.getenv("DEFAULT_USER_ROLE", "pending"),
     os.getenv("DEFAULT_USER_ROLE", "pending"),
 )
 )
 
 
-USER_PERMISSIONS_CHAT_DELETION = (
-    os.environ.get("USER_PERMISSIONS_CHAT_DELETION", "True").lower() == "true"
+USER_PERMISSIONS_CHAT_DELETE = (
+    os.environ.get("USER_PERMISSIONS_CHAT_DELETE", "True").lower() == "true"
 )
 )
 
 
-USER_PERMISSIONS_CHAT_EDITING = (
-    os.environ.get("USER_PERMISSIONS_CHAT_EDITING", "True").lower() == "true"
+USER_PERMISSIONS_CHAT_EDIT = (
+    os.environ.get("USER_PERMISSIONS_CHAT_EDIT", "True").lower() == "true"
 )
 )
 
 
 USER_PERMISSIONS_CHAT_TEMPORARY = (
 USER_PERMISSIONS_CHAT_TEMPORARY = (
@@ -753,11 +753,11 @@ USER_PERMISSIONS_CHAT_TEMPORARY = (
 
 
 USER_PERMISSIONS = PersistentConfig(
 USER_PERMISSIONS = PersistentConfig(
     "USER_PERMISSIONS",
     "USER_PERMISSIONS",
-    "ui.user_permissions",
+    "user.permissions",
     {
     {
         "chat": {
         "chat": {
-            "deletion": USER_PERMISSIONS_CHAT_DELETION,
-            "editing": USER_PERMISSIONS_CHAT_EDITING,
+            "deletion": USER_PERMISSIONS_CHAT_DELETE,
+            "editing": USER_PERMISSIONS_CHAT_EDIT,
             "temporary": USER_PERMISSIONS_CHAT_TEMPORARY,
             "temporary": USER_PERMISSIONS_CHAT_TEMPORARY,
         }
         }
     },
     },
@@ -785,18 +785,6 @@ DEFAULT_ARENA_MODEL = {
     },
     },
 }
 }
 
 
-ENABLE_MODEL_FILTER = PersistentConfig(
-    "ENABLE_MODEL_FILTER",
-    "model_filter.enable",
-    os.environ.get("ENABLE_MODEL_FILTER", "False").lower() == "true",
-)
-MODEL_FILTER_LIST = os.environ.get("MODEL_FILTER_LIST", "")
-MODEL_FILTER_LIST = PersistentConfig(
-    "MODEL_FILTER_LIST",
-    "model_filter.list",
-    [model.strip() for model in MODEL_FILTER_LIST.split(";")],
-)
-
 WEBHOOK_URL = PersistentConfig(
 WEBHOOK_URL = PersistentConfig(
     "WEBHOOK_URL", "webhook_url", os.environ.get("WEBHOOK_URL", "")
     "WEBHOOK_URL", "webhook_url", os.environ.get("WEBHOOK_URL", "")
 )
 )

+ 0 - 5
backend/open_webui/main.py

@@ -70,13 +70,11 @@ from open_webui.config import (
     DEFAULT_LOCALE,
     DEFAULT_LOCALE,
     ENABLE_ADMIN_CHAT_ACCESS,
     ENABLE_ADMIN_CHAT_ACCESS,
     ENABLE_ADMIN_EXPORT,
     ENABLE_ADMIN_EXPORT,
-    ENABLE_MODEL_FILTER,
     ENABLE_OLLAMA_API,
     ENABLE_OLLAMA_API,
     ENABLE_OPENAI_API,
     ENABLE_OPENAI_API,
     ENABLE_TAGS_GENERATION,
     ENABLE_TAGS_GENERATION,
     ENV,
     ENV,
     FRONTEND_BUILD_DIR,
     FRONTEND_BUILD_DIR,
-    MODEL_FILTER_LIST,
     OAUTH_PROVIDERS,
     OAUTH_PROVIDERS,
     ENABLE_SEARCH_QUERY,
     ENABLE_SEARCH_QUERY,
     SEARCH_QUERY_GENERATION_PROMPT_TEMPLATE,
     SEARCH_QUERY_GENERATION_PROMPT_TEMPLATE,
@@ -194,9 +192,6 @@ app.state.config = AppConfig()
 app.state.config.ENABLE_OPENAI_API = ENABLE_OPENAI_API
 app.state.config.ENABLE_OPENAI_API = ENABLE_OPENAI_API
 app.state.config.ENABLE_OLLAMA_API = ENABLE_OLLAMA_API
 app.state.config.ENABLE_OLLAMA_API = ENABLE_OLLAMA_API
 
 
-app.state.config.ENABLE_MODEL_FILTER = ENABLE_MODEL_FILTER
-app.state.config.MODEL_FILTER_LIST = MODEL_FILTER_LIST
-
 app.state.config.WEBHOOK_URL = WEBHOOK_URL
 app.state.config.WEBHOOK_URL = WEBHOOK_URL
 
 
 app.state.config.TASK_MODEL = TASK_MODEL
 app.state.config.TASK_MODEL = TASK_MODEL

+ 29 - 55
src/lib/components/admin/Users/Groups.svelte

@@ -48,69 +48,38 @@
 	let showCreateGroupModal = false;
 	let showCreateGroupModal = false;
 	let showDefaultPermissionsModal = false;
 	let showDefaultPermissionsModal = false;
 
 
+	const setGroups = async () => {
+		groups = await getGroups(localStorage.token);
+	};
+
+	const addGroupHandler = async (group) => {
+		const res = await createNewGroup(localStorage.token, group).catch((error) => {
+			toast.error(error);
+			return null;
+		});
+
+		if (res) {
+			toast.success($i18n.t('Group created successfully'));
+			groups = await getGroups(localStorage.token);
+		}
+	};
+
+	const updateDefaultPermissionsHandler = async (permissions) => {
+		console.log(permissions);
+	};
+
 	onMount(async () => {
 	onMount(async () => {
 		if ($user?.role !== 'admin') {
 		if ($user?.role !== 'admin') {
 			await goto('/');
 			await goto('/');
 		} else {
 		} else {
-			groups = await getGroups(localStorage.token);
-
-			// [
-			// {
-			// 	id: '1',
-			// 	name: 'Group A',
-			// 	description: 'Group A description',
-			// 	permissions: {
-			// 		model: {
-			// 			enable_filter: false, // boolean
-			// 			ids: [], // array of strings
-			// 			default_id: null // null or string
-			// 		},
-			// 		workspace: {
-			// 			models: false, // boolean
-			// 			knowledge: false, // boolean
-			// 			prompts: false // boolean
-			// 		},
-			// 		chat: {
-			// 			delete: true, // boolean
-			// 			edit: true, // boolean
-			// 			temporary: true // boolean
-			// 		}
-			// 	},
-			// 	user_ids: ['1', '2', '3'], // array of strings
-			// 	admin_ids: ['1'] // array of strings
-			// },
-			// {
-			// 	id: '2',
-			// 	name: 'Moderators',
-			// 	description: 'Moderators description',
-			// 	permissions: {
-			// 		model: {
-			// 			enable_filter: false, // boolean
-			// 			ids: [], // array of strings
-			// 			default_id: null // null or string
-			// 		},
-			// 		workspace: {
-			// 			models: false, // boolean
-			// 			knowledge: false, // boolean
-			// 			prompts: false // boolean
-			// 		},
-			// 		chat: {
-			// 			delete: true, // boolean
-			// 			edit: true, // boolean
-			// 			temporary: true // boolean
-			// 		}
-			// 	},
-			// 	user_ids: ['1', '5', '6'], // array of strings
-			// 	admin_ids: ['1'] // array of strings
-			// }
-			// ];
+			await setGroups();
 		}
 		}
 		loaded = true;
 		loaded = true;
 	});
 	});
 </script>
 </script>
 
 
 {#if loaded}
 {#if loaded}
-	<AddGroupModal bind:show={showCreateGroupModal} />
+	<AddGroupModal bind:show={showCreateGroupModal} onSubmit={addGroupHandler} />
 	<div class="mt-0.5 mb-2 gap-1 flex flex-col md:flex-row justify-between">
 	<div class="mt-0.5 mb-2 gap-1 flex flex-col md:flex-row justify-between">
 		<div class="flex md:self-center text-lg font-medium px-0.5">
 		<div class="flex md:self-center text-lg font-medium px-0.5">
 			{$i18n.t('Groups')}
 			{$i18n.t('Groups')}
@@ -196,7 +165,7 @@
 
 
 				{#each filteredGroups as group}
 				{#each filteredGroups as group}
 					<div class="my-2">
 					<div class="my-2">
-						<GroupItem {group} {users} />
+						<GroupItem {group} {users} {setGroups} />
 					</div>
 					</div>
 				{/each}
 				{/each}
 			</div>
 			</div>
@@ -204,7 +173,12 @@
 
 
 		<hr class="mb-2 border-gray-50 dark:border-gray-850" />
 		<hr class="mb-2 border-gray-50 dark:border-gray-850" />
 
 
-		<GroupModal bind:show={showDefaultPermissionsModal} tabs={['permissions']} custom={false} />
+		<GroupModal
+			bind:show={showDefaultPermissionsModal}
+			tabs={['permissions']}
+			custom={false}
+			onSubmit={updateDefaultPermissionsHandler}
+		/>
 
 
 		<button
 		<button
 			class="flex items-center justify-between rounded-lg w-full transition pt-1"
 			class="flex items-center justify-between rounded-lg w-full transition pt-1"

+ 29 - 14
src/lib/components/admin/Users/Groups/EditGroupModal.svelte

@@ -4,16 +4,9 @@
 	const i18n = getContext('i18n');
 	const i18n = getContext('i18n');
 
 
 	import Modal from '$lib/components/common/Modal.svelte';
 	import Modal from '$lib/components/common/Modal.svelte';
-	import Plus from '$lib/components/icons/Plus.svelte';
-	import Minus from '$lib/components/icons/Minus.svelte';
-	import PencilSolid from '$lib/components/icons/PencilSolid.svelte';
-	import SensitiveInput from '$lib/components/common/SensitiveInput.svelte';
-	import Tooltip from '$lib/components/common/Tooltip.svelte';
-	import Switch from '$lib/components/common/Switch.svelte';
 	import Display from './Display.svelte';
 	import Display from './Display.svelte';
 	import Permissions from './Permissions.svelte';
 	import Permissions from './Permissions.svelte';
 	import Users from './Users.svelte';
 	import Users from './Users.svelte';
-	import UsersSolid from '$lib/components/icons/UsersSolid.svelte';
 	import UserPlusSolid from '$lib/components/icons/UserPlusSolid.svelte';
 	import UserPlusSolid from '$lib/components/icons/UserPlusSolid.svelte';
 	import WrenchSolid from '$lib/components/icons/WrenchSolid.svelte';
 	import WrenchSolid from '$lib/components/icons/WrenchSolid.svelte';
 
 
@@ -31,15 +24,25 @@
 	export let tabs = ['general', 'permissions', 'users'];
 	export let tabs = ['general', 'permissions', 'users'];
 
 
 	let selectedTab = 'general';
 	let selectedTab = 'general';
+	let loading = false;
 
 
 	let name = '';
 	let name = '';
 	let description = '';
 	let description = '';
 
 
-	let permissions = {};
+	let permissions = {
+		workspace: {
+			models: false,
+			knowledge: false,
+			prompts: false,
+			tools: false
+		},
+		chat: {
+			delete: true,
+			edit: true,
+			temporary: true
+		}
+	};
 	let userIds = [];
 	let userIds = [];
-	let adminIds = [];
-
-	let loading = false;
 
 
 	const submitHandler = async () => {
 	const submitHandler = async () => {
 		loading = true;
 		loading = true;
@@ -65,7 +68,19 @@
 		if (group) {
 		if (group) {
 			name = group.name;
 			name = group.name;
 			description = group.description;
 			description = group.description;
-			permissions = group?.permissions ?? {};
+			permissions = group?.permissions ?? {
+				workspace: {
+					models: false,
+					knowledge: false,
+					prompts: false,
+					tools: false
+				},
+				chat: {
+					delete: true,
+					edit: true,
+					temporary: true
+				}
+			};
 			userIds = group?.user_ids ?? [];
 			userIds = group?.user_ids ?? [];
 		}
 		}
 	};
 	};
@@ -200,9 +215,9 @@
 							{#if selectedTab == 'general'}
 							{#if selectedTab == 'general'}
 								<Display bind:name bind:description />
 								<Display bind:name bind:description />
 							{:else if selectedTab == 'permissions'}
 							{:else if selectedTab == 'permissions'}
-								<Permissions bind:permissions {custom} />
+								<Permissions bind:permissions />
 							{:else if selectedTab == 'users'}
 							{:else if selectedTab == 'users'}
-								<Users bind:userIds bind:adminIds {users} />
+								<Users bind:userIds {users} />
 							{/if}
 							{/if}
 						</div>
 						</div>
 					</div>
 					</div>

+ 41 - 2
src/lib/components/admin/Users/Groups/GroupItem.svelte

@@ -1,7 +1,13 @@
 <script>
 <script>
+	import { toast } from 'svelte-sonner';
+	import { getContext } from 'svelte';
+
+	const i18n = getContext('i18n');
+
+	import { deleteGroupById, updateGroupById } from '$lib/apis/groups';
+
 	import Pencil from '$lib/components/icons/Pencil.svelte';
 	import Pencil from '$lib/components/icons/Pencil.svelte';
 	import User from '$lib/components/icons/User.svelte';
 	import User from '$lib/components/icons/User.svelte';
-
 	import UserCircleSolid from '$lib/components/icons/UserCircleSolid.svelte';
 	import UserCircleSolid from '$lib/components/icons/UserCircleSolid.svelte';
 	import GroupModal from './EditGroupModal.svelte';
 	import GroupModal from './EditGroupModal.svelte';
 
 
@@ -11,10 +17,43 @@
 		user_ids: [1, 2, 3]
 		user_ids: [1, 2, 3]
 	};
 	};
 
 
+	export let setGroups = () => {};
+
 	let showEdit = false;
 	let showEdit = false;
+
+	const updateHandler = async (_group) => {
+		const res = await updateGroupById(localStorage.token, group.id, _group).catch((error) => {
+			toast.error(error);
+			return null;
+		});
+
+		if (res) {
+			toast.success($i18n.t('Group updated successfully'));
+			setGroups();
+		}
+	};
+
+	const deleteHandler = async () => {
+		const res = await deleteGroupById(localStorage.token, group.id).catch((error) => {
+			toast.error(error);
+			return null;
+		});
+
+		if (res) {
+			toast.success($i18n.t('Group deleted successfully'));
+			setGroups();
+		}
+	};
 </script>
 </script>
 
 
-<GroupModal bind:show={showEdit} edit {group} {users} />
+<GroupModal
+	bind:show={showEdit}
+	edit
+	{users}
+	{group}
+	onSubmit={updateHandler}
+	onDelete={deleteHandler}
+/>
 
 
 <div class="flex items-center gap-3 justify-between px-1 text-xs w-full transition">
 <div class="flex items-center gap-3 justify-between px-1 text-xs w-full transition">
 	<div class="flex items-center gap-1.5 w-full font-medium">
 	<div class="flex items-center gap-1.5 w-full font-medium">

+ 38 - 62
src/lib/components/admin/Users/Groups/Permissions.svelte

@@ -3,34 +3,25 @@
 	const i18n = getContext('i18n');
 	const i18n = getContext('i18n');
 
 
 	import Switch from '$lib/components/common/Switch.svelte';
 	import Switch from '$lib/components/common/Switch.svelte';
-	import { models } from '$lib/stores';
-	import Minus from '$lib/components/icons/Minus.svelte';
-	import Plus from '$lib/components/icons/Plus.svelte';
-
-	export let permissions = {};
-	export let custom = true;
-
-	let defaultModelId = '';
-
-	let selectedModelId = '';
-
-	let filterEnabled = false;
-	let filterMode = 'include';
-	let filterModelIds = [];
-
-	let workspaceModelsAccess = false;
-	let workspaceKnowledgeAccess = false;
-	let workspacePromptsAccess = false;
-	let workspaceToolsAccess = false;
-	let workspaceFunctionsAccess = false;
-
-	let chatDeletion = true;
-	let chatEdit = true;
-	let chatTemporary = true;
+	import Tooltip from '$lib/components/common/Tooltip.svelte';
+
+	export let permissions = {
+		workspace: {
+			models: false,
+			knowledge: false,
+			prompts: false,
+			tools: false
+		},
+		chat: {
+			delete: true,
+			edit: true,
+			temporary: true
+		}
+	};
 </script>
 </script>
 
 
 <div>
 <div>
-	<div>
+	<!-- <div>
 		<div class=" mb-2 text-sm font-medium">{$i18n.t('Model Permissions')}</div>
 		<div class=" mb-2 text-sm font-medium">{$i18n.t('Model Permissions')}</div>
 
 
 		<div class="mb-2">
 		<div class="mb-2">
@@ -130,52 +121,46 @@
 		</div>
 		</div>
 	</div>
 	</div>
 
 
-	<hr class=" border-gray-50 dark:border-gray-850 my-2" />
+	<hr class=" border-gray-50 dark:border-gray-850 my-2" /> -->
 
 
 	<div>
 	<div>
 		<div class=" mb-2 text-sm font-medium">{$i18n.t('Workspace Permissions')}</div>
 		<div class=" mb-2 text-sm font-medium">{$i18n.t('Workspace Permissions')}</div>
 
 
 		<div class="  flex w-full justify-between my-2 pr-2">
 		<div class="  flex w-full justify-between my-2 pr-2">
 			<div class=" self-center text-xs font-medium">
 			<div class=" self-center text-xs font-medium">
-				{#if custom}
-					{$i18n.t('Admins')}:
-				{/if}
 				{$i18n.t('Models Access')}
 				{$i18n.t('Models Access')}
 			</div>
 			</div>
-			<Switch bind:state={workspaceModelsAccess} />
+			<Switch bind:state={permissions.workspace.models} />
 		</div>
 		</div>
 
 
 		<div class="  flex w-full justify-between my-2 pr-2">
 		<div class="  flex w-full justify-between my-2 pr-2">
 			<div class=" self-center text-xs font-medium">
 			<div class=" self-center text-xs font-medium">
-				{#if custom}
-					{$i18n.t('Admins')}:
-				{/if}
-
 				{$i18n.t('Knowledge Access')}
 				{$i18n.t('Knowledge Access')}
 			</div>
 			</div>
-			<Switch bind:state={workspaceKnowledgeAccess} />
+			<Switch bind:state={permissions.workspace.knowledge} />
 		</div>
 		</div>
 
 
 		<div class="  flex w-full justify-between my-2 pr-2">
 		<div class="  flex w-full justify-between my-2 pr-2">
 			<div class=" self-center text-xs font-medium">
 			<div class=" self-center text-xs font-medium">
-				{#if custom}
-					{$i18n.t('Admins')}:
-				{/if}
-
 				{$i18n.t('Prompts Access')}
 				{$i18n.t('Prompts Access')}
 			</div>
 			</div>
-			<Switch bind:state={workspacePromptsAccess} />
+			<Switch bind:state={permissions.workspace.prompts} />
 		</div>
 		</div>
 
 
-		<!-- <div class="  flex w-full justify-between my-2 pr-2">
-			<div class=" self-center text-xs font-medium">{$i18n.t('Allow Tools Access')}</div>
-			<Switch bind:state={workspaceToolsAccess} />
+		<div class=" ">
+			<Tooltip
+				className=" flex w-full justify-between my-2 pr-2"
+				content={$i18n.t(
+					'Warning: Enabling this will allow users to upload arbitrary code on the server.'
+				)}
+				placement="top-start"
+			>
+				<div class=" self-center text-xs font-medium">
+					{$i18n.t('Tools Access')}
+				</div>
+				<Switch bind:state={permissions.workspace.tools} />
+			</Tooltip>
 		</div>
 		</div>
-
-		<div class="  flex w-full justify-between my-2 pr-2">
-			<div class=" self-center text-xs font-medium">{$i18n.t('Allow Functions Access')}</div>
-			<Switch bind:state={workspaceFunctionsAccess} />
-		</div> -->
 	</div>
 	</div>
 
 
 	<hr class=" border-gray-50 dark:border-gray-850 my-2" />
 	<hr class=" border-gray-50 dark:border-gray-850 my-2" />
@@ -185,35 +170,26 @@
 
 
 		<div class="  flex w-full justify-between my-2 pr-2">
 		<div class="  flex w-full justify-between my-2 pr-2">
 			<div class=" self-center text-xs font-medium">
 			<div class=" self-center text-xs font-medium">
-				{#if custom}
-					{$i18n.t('Members')}:
-				{/if}
-				{$i18n.t('Allow Chat Deletion')}
+				{$i18n.t('Allow Chat Delete')}
 			</div>
 			</div>
 
 
-			<Switch bind:state={chatDeletion} />
+			<Switch bind:state={permissions.chat.delete} />
 		</div>
 		</div>
 
 
 		<div class="  flex w-full justify-between my-2 pr-2">
 		<div class="  flex w-full justify-between my-2 pr-2">
 			<div class=" self-center text-xs font-medium">
 			<div class=" self-center text-xs font-medium">
-				{#if custom}
-					{$i18n.t('Members')}:
-				{/if}
-				{$i18n.t('Allow Chat Editing')}
+				{$i18n.t('Allow Chat Edit')}
 			</div>
 			</div>
 
 
-			<Switch bind:state={chatEdit} />
+			<Switch bind:state={permissions.chat.edit} />
 		</div>
 		</div>
 
 
 		<div class="  flex w-full justify-between my-2 pr-2">
 		<div class="  flex w-full justify-between my-2 pr-2">
 			<div class=" self-center text-xs font-medium">
 			<div class=" self-center text-xs font-medium">
-				{#if custom}
-					{$i18n.t('Members')}:
-				{/if}
 				{$i18n.t('Allow Temporary Chat')}
 				{$i18n.t('Allow Temporary Chat')}
 			</div>
 			</div>
 
 
-			<Switch bind:state={chatTemporary} />
+			<Switch bind:state={permissions.chat.temporary} />
 		</div>
 		</div>
 	</div>
 	</div>
 </div>
 </div>

+ 2 - 22
src/lib/components/admin/Users/Groups/Users.svelte

@@ -10,7 +10,6 @@
 
 
 	export let users = [];
 	export let users = [];
 	export let userIds = [];
 	export let userIds = [];
-	export let adminIds = [];
 
 
 	let filteredUsers = [];
 	let filteredUsers = [];
 
 
@@ -30,16 +29,10 @@
 			);
 			);
 		})
 		})
 		.sort((a, b) => {
 		.sort((a, b) => {
-			const aIsAdmin = adminIds.includes(a.id);
-			const bIsAdmin = adminIds.includes(b.id);
 			const aUserIndex = userIds.indexOf(a.id);
 			const aUserIndex = userIds.indexOf(a.id);
 			const bUserIndex = userIds.indexOf(b.id);
 			const bUserIndex = userIds.indexOf(b.id);
 
 
-			// Admin users should come first
-			if (aIsAdmin && !bIsAdmin) return -1; // Place 'a' first if it's admin
-			if (!aIsAdmin && bIsAdmin) return 1; // Place 'b' first if it's admin
-
-			// Neither are admin, compare based on userIds or fall back to alphabetical order
+			// Compare based on userIds or fall back to alphabetical order
 			if (aUserIndex !== -1 && bUserIndex === -1) return -1; // 'a' has valid userId -> prioritize
 			if (aUserIndex !== -1 && bUserIndex === -1) return -1; // 'a' has valid userId -> prioritize
 			if (bUserIndex !== -1 && aUserIndex === -1) return 1; // 'b' has valid userId -> prioritize
 			if (bUserIndex !== -1 && aUserIndex === -1) return 1; // 'b' has valid userId -> prioritize
 
 
@@ -114,20 +107,7 @@
 							</Tooltip>
 							</Tooltip>
 
 
 							{#if userIds.includes(user.id)}
 							{#if userIds.includes(user.id)}
-								<button
-									on:click={() => {
-										adminIds = adminIds.includes(user.id)
-											? adminIds.filter((id) => id !== user.id)
-											: [...adminIds, user.id];
-									}}
-									type="button"
-								>
-									{#if adminIds.includes(user.id)}
-										<Badge type="info" content="admin" />
-									{:else}
-										<Badge type="success" content="member" />
-									{/if}
-								</button>
+								<Badge type="success" content="member" />
 							{/if}
 							{/if}
 						</div>
 						</div>
 					</div>
 					</div>