mirror of
https://github.com/open-webui/open-webui.git
synced 2026-08-27 06:46:30 -05:00
refac
This commit is contained in:
@@ -14,6 +14,7 @@
|
||||
getBaseModels,
|
||||
toggleModelById,
|
||||
updateModelById,
|
||||
updateModelAccessGrants,
|
||||
importModels
|
||||
} from '$lib/apis/models';
|
||||
import { copyToClipboard } from '$lib/utils';
|
||||
@@ -82,6 +83,18 @@
|
||||
);
|
||||
};
|
||||
|
||||
const isSharedModel = (model) => (model?.access_grants ?? []).length > 0 && !isPublicModel(model);
|
||||
|
||||
const modelAccessBadge = (model) => {
|
||||
if (isPublicModel(model)) {
|
||||
return { type: 'success', content: $i18n.t('Public') };
|
||||
}
|
||||
if (isSharedModel(model)) {
|
||||
return { type: 'info', content: $i18n.t('Shared') };
|
||||
}
|
||||
return { type: 'muted', content: $i18n.t('Private') };
|
||||
};
|
||||
|
||||
$: if (models) {
|
||||
filteredModels = models
|
||||
.filter((m) => searchValue === '' || m.name.toLowerCase().includes(searchValue.toLowerCase()))
|
||||
@@ -297,6 +310,43 @@
|
||||
);
|
||||
};
|
||||
|
||||
const toggleModelPrivacyHandler = async (model) => {
|
||||
const nextAccessGrants = isPublicModel(model)
|
||||
? []
|
||||
: [
|
||||
...(model?.access_grants ?? []),
|
||||
{
|
||||
principal_type: 'user',
|
||||
principal_id: '*',
|
||||
permission: 'read'
|
||||
}
|
||||
];
|
||||
|
||||
const res = await updateModelAccessGrants(
|
||||
localStorage.token,
|
||||
model.id,
|
||||
model.name,
|
||||
nextAccessGrants
|
||||
).catch(() => null);
|
||||
|
||||
if (res) {
|
||||
models = models.map((m) =>
|
||||
m.id === model.id ? { ...m, access_grants: res.access_grants ?? nextAccessGrants } : m
|
||||
);
|
||||
_models.set(
|
||||
await getModels(
|
||||
localStorage.token,
|
||||
$config?.features?.enable_direct_connections && ($settings?.directConnections ?? null)
|
||||
)
|
||||
);
|
||||
toast.success(
|
||||
isPublicModel({ access_grants: nextAccessGrants })
|
||||
? $i18n.t('Model is now public')
|
||||
: $i18n.t('Model is now private')
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const copyLinkHandler = async (model) => {
|
||||
const baseUrl = window.location.origin;
|
||||
const res = await copyToClipboard(`${baseUrl}/?model=${encodeURIComponent(model.id)}`);
|
||||
@@ -662,22 +712,8 @@
|
||||
</span>
|
||||
|
||||
<Badge
|
||||
type={(model?.access_grants ?? []).some(
|
||||
(g) =>
|
||||
g.principal_type === 'user' &&
|
||||
g.principal_id === '*' &&
|
||||
g.permission === 'read'
|
||||
)
|
||||
? 'success'
|
||||
: 'muted'}
|
||||
content={(model?.access_grants ?? []).some(
|
||||
(g) =>
|
||||
g.principal_type === 'user' &&
|
||||
g.principal_id === '*' &&
|
||||
g.permission === 'read'
|
||||
)
|
||||
? $i18n.t('Public')
|
||||
: $i18n.t('Private')}
|
||||
type={modelAccessBadge(model).type}
|
||||
content={modelAccessBadge(model).content}
|
||||
/>
|
||||
</div>
|
||||
</Tooltip>
|
||||
@@ -734,6 +770,9 @@
|
||||
hideHandler={() => {
|
||||
hideModelHandler(model);
|
||||
}}
|
||||
privacyHandler={() => {
|
||||
toggleModelPrivacyHandler(model);
|
||||
}}
|
||||
pinModelHandler={() => {
|
||||
pinModelHandler(model.id);
|
||||
}}
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
import ArrowUpCircle from '$lib/components/icons/ArrowUpCircle.svelte';
|
||||
import Pin from '$lib/components/icons/Pin.svelte';
|
||||
import PinSlash from '$lib/components/icons/PinSlash.svelte';
|
||||
import GlobeAlt from '$lib/components/icons/GlobeAlt.svelte';
|
||||
import LockClosed from '$lib/components/icons/LockClosed.svelte';
|
||||
|
||||
import { config, settings } from '$lib/stores';
|
||||
import Link from '$lib/components/icons/Link.svelte';
|
||||
@@ -25,6 +27,7 @@
|
||||
|
||||
export let exportHandler: Function;
|
||||
export let hideHandler: Function;
|
||||
export let privacyHandler: Function;
|
||||
export let pinModelHandler: Function;
|
||||
export let copyLinkHandler: Function;
|
||||
export let cloneHandler: Function;
|
||||
@@ -32,6 +35,11 @@
|
||||
export let onClose: Function;
|
||||
|
||||
let show = false;
|
||||
|
||||
const isPublicModel = (model) =>
|
||||
(model?.access_grants ?? []).some(
|
||||
(g) => g.principal_type === 'user' && g.principal_id === '*' && g.permission === 'read'
|
||||
);
|
||||
</script>
|
||||
|
||||
<Dropdown
|
||||
@@ -100,6 +108,27 @@
|
||||
</div>
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="select-none flex w-full gap-2 items-center h-[1.6875rem] px-2 text-[13px] font-normal cursor-pointer hover:bg-gray-50/40 dark:hover:bg-gray-800/40 rounded-xl"
|
||||
on:click={() => {
|
||||
privacyHandler();
|
||||
}}
|
||||
>
|
||||
{#if isPublicModel(model)}
|
||||
<LockClosed className="size-3.5" />
|
||||
{:else}
|
||||
<GlobeAlt className="size-3.5" />
|
||||
{/if}
|
||||
|
||||
<div class="flex items-center">
|
||||
{#if isPublicModel(model)}
|
||||
{$i18n.t('Make Private')}
|
||||
{:else}
|
||||
{$i18n.t('Make Public')}
|
||||
{/if}
|
||||
</div>
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="select-none flex w-full gap-2 items-center h-[1.6875rem] px-2 text-[13px] font-normal cursor-pointer hover:bg-gray-50/40 dark:hover:bg-gray-800/40 rounded-xl"
|
||||
on:click={() => {
|
||||
|
||||
@@ -426,28 +426,15 @@
|
||||
}
|
||||
|
||||
onMount(async () => {
|
||||
console.log('AccessControl mounted', { accessGrants, accessControl });
|
||||
const res = await getGroups(localStorage.token, true).catch((error) => {
|
||||
console.error(error);
|
||||
return [];
|
||||
});
|
||||
|
||||
console.log('getGroups res', res);
|
||||
|
||||
groups = [...groups, ...res].filter(
|
||||
(g, index, self) => index === self.findIndex((t) => t.id === g.id)
|
||||
);
|
||||
});
|
||||
|
||||
$: console.log('AccessControl state', {
|
||||
accessGrants,
|
||||
readGroupIds,
|
||||
writeGroupIds,
|
||||
selectedUserIds,
|
||||
groups,
|
||||
accessGroups,
|
||||
selectedUsers
|
||||
});
|
||||
</script>
|
||||
|
||||
<AddAccessModal
|
||||
@@ -458,11 +445,11 @@
|
||||
onAdd={handleAddAccess}
|
||||
/>
|
||||
|
||||
<div class=" rounded-lg flex flex-col gap-1">
|
||||
<div class="py-2">
|
||||
<div class="flex gap-2.5 items-center">
|
||||
<div class="rounded-lg flex flex-col gap-1">
|
||||
<div class="py-1.5">
|
||||
<div class="flex gap-2 items-center">
|
||||
<div>
|
||||
<div class=" p-2 bg-black/5 dark:bg-white/5 rounded-full">
|
||||
<div class="p-2 bg-black/5 dark:bg-white/5 rounded-full">
|
||||
{#if !hasPublicReadGrant(accessGrants ?? [])}
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
@@ -470,7 +457,7 @@
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
class="w-5 h-5"
|
||||
class="size-4.5"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
@@ -485,7 +472,7 @@
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
class="w-5 h-5"
|
||||
class="size-4.5"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
@@ -505,7 +492,7 @@
|
||||
>
|
||||
<select
|
||||
id="models"
|
||||
class="outline-none bg-transparent text-sm font-normal block w-fit pr-10 max-w-full placeholder-gray-400"
|
||||
class="outline-none bg-transparent text-sm font-normal block w-fit pr-8 max-w-full placeholder-gray-400"
|
||||
value={!hasPublicReadGrant(accessGrants ?? []) ? 'private' : 'public'}
|
||||
on:change={(e) => {
|
||||
setPublic((e.target as HTMLSelectElement).value === 'public');
|
||||
@@ -529,7 +516,7 @@
|
||||
</div>
|
||||
|
||||
{#if hasPublicReadGrant(accessGrants ?? []) && accessRoles.includes('write')}
|
||||
<div class="flex w-full justify-between mt-2 ml-0.5">
|
||||
<div class="flex w-full justify-between mt-1.5 ml-0.5">
|
||||
<div class="self-center text-xs">
|
||||
{$i18n.t('Allow public write access')}
|
||||
</div>
|
||||
@@ -544,7 +531,7 @@
|
||||
</div>
|
||||
|
||||
{#if share}
|
||||
<div class="flex items-center justify-between text-xs font-normal text-gray-500 my-1">
|
||||
<div class="flex items-center justify-between text-xs font-normal text-gray-500 my-0.5">
|
||||
<div>
|
||||
{$i18n.t('Access List')}
|
||||
</div>
|
||||
@@ -563,11 +550,11 @@
|
||||
</div>
|
||||
|
||||
<!-- List -->
|
||||
<div class="flex flex-col gap-2">
|
||||
<div class="flex flex-col gap-1">
|
||||
<!-- Groups -->
|
||||
{#each accessGroups as group}
|
||||
<div class="flex items-center gap-3 justify-between text-sm w-full transition pb-1">
|
||||
<div class="flex items-center gap-2 w-full flex-1">
|
||||
<div class="flex items-center gap-2 justify-between text-sm w-full transition pb-1">
|
||||
<div class="flex items-center gap-2 min-w-0 flex-1">
|
||||
<!-- Placeholder for group icon vs user icon -->
|
||||
<div
|
||||
class="size-5 rounded-full bg-gray-100 dark:bg-gray-850 flex items-center justify-center text-xs"
|
||||
@@ -583,11 +570,11 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="w-full flex justify-end items-center gap-2">
|
||||
<div class="flex justify-end items-center gap-1.5 shrink-0">
|
||||
{#if accessRoles.includes('write')}
|
||||
<select
|
||||
aria-label={$i18n.t('Access level')}
|
||||
class="bg-transparent text-sm"
|
||||
class="bg-transparent text-sm outline-none"
|
||||
value={writeGroupIds.includes(group.id) ? 'write' : 'read'}
|
||||
on:change={(e) => {
|
||||
if (
|
||||
@@ -606,7 +593,7 @@
|
||||
{/if}
|
||||
|
||||
<button
|
||||
class=" rounded-full p-1 hover:bg-gray-100 dark:hover:bg-gray-850 transition"
|
||||
class="rounded-full p-1 hover:bg-gray-100 dark:hover:bg-gray-850 transition"
|
||||
type="button"
|
||||
on:click={() => {
|
||||
removePrincipal('group', group.id);
|
||||
@@ -622,26 +609,26 @@
|
||||
{#if shareUsers}
|
||||
{#each selectedUsers as user}
|
||||
<div
|
||||
class="flex items-center gap-3 justify-between text-sm w-full transition border-b border-gray-50 dark:border-gray-850 pb-2 last:border-0"
|
||||
class="flex items-center gap-2 justify-between text-sm w-full transition border-b border-gray-50 dark:border-gray-850 pb-1.5 last:border-0"
|
||||
>
|
||||
<div class="flex items-center gap-2 w-full flex-1">
|
||||
<div class="flex items-center gap-2 min-w-0 flex-1">
|
||||
<img
|
||||
class="rounded-full size-5 object-cover"
|
||||
src={`${WEBUI_API_BASE_URL}/users/${user.id}/profile/image`}
|
||||
alt={user.name ?? user.id}
|
||||
/>
|
||||
<div class="w-full">
|
||||
<div class="min-w-0 flex-1">
|
||||
<Tooltip content={user.email} placement="top-start">
|
||||
<div class="truncate text-sm">{user.name ?? user.id}</div>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="w-full flex justify-end items-center gap-2">
|
||||
<div class="flex justify-end items-center gap-1.5 shrink-0">
|
||||
{#if accessRoles.includes('write')}
|
||||
<select
|
||||
aria-label={$i18n.t('Access level')}
|
||||
class="bg-transparent text-sm"
|
||||
class="bg-transparent text-sm outline-none"
|
||||
value={writeUserIds.includes(user.id) ? 'write' : 'read'}
|
||||
on:change={(e) => {
|
||||
if (
|
||||
@@ -660,7 +647,7 @@
|
||||
{/if}
|
||||
|
||||
<button
|
||||
class=" rounded-full p-1 hover:bg-gray-100 dark:hover:bg-gray-850 transition"
|
||||
class="rounded-full p-1 hover:bg-gray-100 dark:hover:bg-gray-850 transition"
|
||||
type="button"
|
||||
on:click={() => {
|
||||
removePrincipal('user', user.id);
|
||||
@@ -674,7 +661,7 @@
|
||||
{/if}
|
||||
|
||||
{#if !hasPublicReadGrant(accessGrants ?? []) && accessGroups.length === 0 && selectedUsers.length === 0}
|
||||
<div class="text-xs text-gray-500 text-center py-4">
|
||||
<div class="text-xs text-gray-500 text-center py-3">
|
||||
{$i18n.t('No access grants. Private to you.')}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
@@ -26,23 +26,23 @@
|
||||
</script>
|
||||
|
||||
<Modal size="sm" bind:show>
|
||||
<div>
|
||||
<div class=" flex justify-between dark:text-gray-100 px-5 pt-3 pb-1">
|
||||
<div class=" text-lg font-normal self-center ">
|
||||
{$i18n.t('Access Control')}
|
||||
<div>
|
||||
<div class="flex justify-between dark:text-gray-100 px-4 pt-3 pb-1">
|
||||
<div class="text-base font-normal self-center">
|
||||
{$i18n.t('Access Control')}
|
||||
</div>
|
||||
<button
|
||||
class="self-center rounded-lg p-1 text-gray-500 transition hover:bg-gray-50 hover:text-gray-700 dark:text-gray-400 dark:hover:bg-gray-800 dark:hover:text-gray-200"
|
||||
on:click={() => {
|
||||
show = false;
|
||||
}}
|
||||
>
|
||||
<XMark className={'size-4'} />
|
||||
</button>
|
||||
</div>
|
||||
<button
|
||||
class="self-center"
|
||||
on:click={() => {
|
||||
show = false;
|
||||
}}
|
||||
>
|
||||
<XMark className={'size-5'} />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="w-full px-5 pb-4 dark:text-white">
|
||||
<AccessControl
|
||||
<div class="w-full px-4 pb-3 dark:text-white">
|
||||
<AccessControl
|
||||
bind:accessGrants
|
||||
bind:accessControl
|
||||
{onChange}
|
||||
|
||||
@@ -28,24 +28,24 @@
|
||||
</script>
|
||||
|
||||
<Modal size="sm" bind:show>
|
||||
<div>
|
||||
<div class=" flex justify-between dark:text-gray-100 px-5 pt-4 mb-1.5">
|
||||
<div class="self-center text-base">
|
||||
<div class="flex items-center gap-0.5 shrink-0">
|
||||
{$i18n.t('Add Access')}
|
||||
<div>
|
||||
<div class="flex justify-between dark:text-gray-100 px-4 pt-3 mb-1">
|
||||
<div class="self-center text-sm font-medium">
|
||||
<div class="flex items-center gap-0.5 shrink-0">
|
||||
{$i18n.t('Add Access')}
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
class="self-center rounded-lg p-1 text-gray-500 transition hover:bg-gray-50 hover:text-gray-700 dark:text-gray-400 dark:hover:bg-gray-800 dark:hover:text-gray-200"
|
||||
on:click={() => {
|
||||
show = false;
|
||||
}}
|
||||
>
|
||||
<XMark className={'size-4'} />
|
||||
</button>
|
||||
</div>
|
||||
<button
|
||||
class="self-center"
|
||||
on:click={() => {
|
||||
show = false;
|
||||
}}
|
||||
>
|
||||
<XMark className={'size-5'} />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col md:flex-row w-full px-3 pb-4 md:space-x-4 dark:text-gray-200">
|
||||
<div class="flex flex-col md:flex-row w-full px-3 pb-3 md:space-x-4 dark:text-gray-200">
|
||||
<div class=" flex flex-col w-full sm:flex-row sm:justify-center sm:space-x-6">
|
||||
<form
|
||||
class="flex flex-col w-full"
|
||||
@@ -64,9 +64,9 @@
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-end pt-3 text-sm font-normal gap-1.5">
|
||||
<div class="flex justify-end pt-2 text-sm font-normal gap-1.5">
|
||||
<button
|
||||
class="px-3.5 py-1.5 text-sm font-normal bg-black hover:bg-gray-950 text-white dark:bg-white dark:text-black dark:hover:bg-gray-100 transition rounded-full flex flex-row space-x-1 items-center"
|
||||
class="px-3 py-1.5 text-sm font-normal bg-black hover:bg-gray-950 text-white dark:bg-white dark:text-black dark:hover:bg-gray-100 transition rounded-full flex flex-row space-x-1 items-center"
|
||||
type="submit"
|
||||
>
|
||||
{$i18n.t('Add')}
|
||||
|
||||
Reference in New Issue
Block a user