This commit is contained in:
Timothy Jaeryang Baek 2024-11-14 20:51:49 -08:00
parent dff85c733d
commit b80ec76435
7 changed files with 145 additions and 178 deletions

View File

@ -739,12 +739,12 @@ DEFAULT_USER_ROLE = PersistentConfig(
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 = (
@ -753,11 +753,11 @@ USER_PERMISSIONS_CHAT_TEMPORARY = (
USER_PERMISSIONS = PersistentConfig(
"USER_PERMISSIONS",
"ui.user_permissions",
"user.permissions",
{
"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,
}
},
@ -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", "webhook_url", os.environ.get("WEBHOOK_URL", "")
)

View File

@ -70,13 +70,11 @@ from open_webui.config import (
DEFAULT_LOCALE,
ENABLE_ADMIN_CHAT_ACCESS,
ENABLE_ADMIN_EXPORT,
ENABLE_MODEL_FILTER,
ENABLE_OLLAMA_API,
ENABLE_OPENAI_API,
ENABLE_TAGS_GENERATION,
ENV,
FRONTEND_BUILD_DIR,
MODEL_FILTER_LIST,
OAUTH_PROVIDERS,
ENABLE_SEARCH_QUERY,
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_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.TASK_MODEL = TASK_MODEL

View File

@ -48,69 +48,38 @@
let showCreateGroupModal = 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 () => {
if ($user?.role !== 'admin') {
await goto('/');
} 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;
});
</script>
{#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="flex md:self-center text-lg font-medium px-0.5">
{$i18n.t('Groups')}
@ -196,7 +165,7 @@
{#each filteredGroups as group}
<div class="my-2">
<GroupItem {group} {users} />
<GroupItem {group} {users} {setGroups} />
</div>
{/each}
</div>
@ -204,7 +173,12 @@
<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
class="flex items-center justify-between rounded-lg w-full transition pt-1"

View File

@ -4,16 +4,9 @@
const i18n = getContext('i18n');
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 Permissions from './Permissions.svelte';
import Users from './Users.svelte';
import UsersSolid from '$lib/components/icons/UsersSolid.svelte';
import UserPlusSolid from '$lib/components/icons/UserPlusSolid.svelte';
import WrenchSolid from '$lib/components/icons/WrenchSolid.svelte';
@ -31,15 +24,25 @@
export let tabs = ['general', 'permissions', 'users'];
let selectedTab = 'general';
let loading = false;
let name = '';
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 adminIds = [];
let loading = false;
const submitHandler = async () => {
loading = true;
@ -65,7 +68,19 @@
if (group) {
name = group.name;
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 ?? [];
}
};
@ -200,9 +215,9 @@
{#if selectedTab == 'general'}
<Display bind:name bind:description />
{:else if selectedTab == 'permissions'}
<Permissions bind:permissions {custom} />
<Permissions bind:permissions />
{:else if selectedTab == 'users'}
<Users bind:userIds bind:adminIds {users} />
<Users bind:userIds {users} />
{/if}
</div>
</div>

View File

@ -1,7 +1,13 @@
<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 User from '$lib/components/icons/User.svelte';
import UserCircleSolid from '$lib/components/icons/UserCircleSolid.svelte';
import GroupModal from './EditGroupModal.svelte';
@ -11,10 +17,43 @@
user_ids: [1, 2, 3]
};
export let setGroups = () => {};
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>
<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-1.5 w-full font-medium">

View File

@ -3,34 +3,25 @@
const i18n = getContext('i18n');
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';
import Tooltip from '$lib/components/common/Tooltip.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;
export let permissions = {
workspace: {
models: false,
knowledge: false,
prompts: false,
tools: false
},
chat: {
delete: true,
edit: true,
temporary: true
}
};
</script>
<div>
<div>
<!-- <div>
<div class=" mb-2 text-sm font-medium">{$i18n.t('Model Permissions')}</div>
<div class="mb-2">
@ -130,52 +121,46 @@
</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 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=" self-center text-xs font-medium">
{#if custom}
{$i18n.t('Admins')}:
{/if}
{$i18n.t('Models Access')}
</div>
<Switch bind:state={workspaceModelsAccess} />
<Switch bind:state={permissions.workspace.models} />
</div>
<div class=" flex w-full justify-between my-2 pr-2">
<div class=" self-center text-xs font-medium">
{#if custom}
{$i18n.t('Admins')}:
{/if}
{$i18n.t('Knowledge Access')}
</div>
<Switch bind:state={workspaceKnowledgeAccess} />
<Switch bind:state={permissions.workspace.knowledge} />
</div>
<div class=" flex w-full justify-between my-2 pr-2">
<div class=" self-center text-xs font-medium">
{#if custom}
{$i18n.t('Admins')}:
{/if}
{$i18n.t('Prompts Access')}
</div>
<Switch bind:state={workspacePromptsAccess} />
<Switch bind:state={permissions.workspace.prompts} />
</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 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>
<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=" self-center text-xs font-medium">
{#if custom}
{$i18n.t('Members')}:
{/if}
{$i18n.t('Allow Chat Deletion')}
{$i18n.t('Allow Chat Delete')}
</div>
<Switch bind:state={chatDeletion} />
<Switch bind:state={permissions.chat.delete} />
</div>
<div class=" flex w-full justify-between my-2 pr-2">
<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>
<Switch bind:state={chatEdit} />
<Switch bind:state={permissions.chat.edit} />
</div>
<div class=" flex w-full justify-between my-2 pr-2">
<div class=" self-center text-xs font-medium">
{#if custom}
{$i18n.t('Members')}:
{/if}
{$i18n.t('Allow Temporary Chat')}
</div>
<Switch bind:state={chatTemporary} />
<Switch bind:state={permissions.chat.temporary} />
</div>
</div>
</div>

View File

@ -10,7 +10,6 @@
export let users = [];
export let userIds = [];
export let adminIds = [];
let filteredUsers = [];
@ -30,16 +29,10 @@
);
})
.sort((a, b) => {
const aIsAdmin = adminIds.includes(a.id);
const bIsAdmin = adminIds.includes(b.id);
const aUserIndex = userIds.indexOf(a.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 (bUserIndex !== -1 && aUserIndex === -1) return 1; // 'b' has valid userId -> prioritize
@ -114,20 +107,7 @@
</Tooltip>
{#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}
</div>
</div>