mirror of
https://github.com/open-webui/open-webui
synced 2025-06-26 18:26:48 +00:00
refac: access control
This commit is contained in:
parent
b41e456c4f
commit
41bad9abcb
@ -85,6 +85,8 @@ class KnowledgeResponse(BaseModel):
|
|||||||
description: str
|
description: str
|
||||||
data: Optional[dict] = None
|
data: Optional[dict] = None
|
||||||
meta: Optional[dict] = None
|
meta: Optional[dict] = None
|
||||||
|
|
||||||
|
access_control: Optional[dict] = None
|
||||||
created_at: int # timestamp in epoch
|
created_at: int # timestamp in epoch
|
||||||
updated_at: int # timestamp in epoch
|
updated_at: int # timestamp in epoch
|
||||||
|
|
||||||
@ -95,12 +97,7 @@ class KnowledgeForm(BaseModel):
|
|||||||
name: str
|
name: str
|
||||||
description: str
|
description: str
|
||||||
data: Optional[dict] = None
|
data: Optional[dict] = None
|
||||||
|
access_control: Optional[dict] = None
|
||||||
|
|
||||||
class KnowledgeUpdateForm(BaseModel):
|
|
||||||
name: Optional[str] = None
|
|
||||||
description: Optional[str] = None
|
|
||||||
data: Optional[dict] = None
|
|
||||||
|
|
||||||
|
|
||||||
class KnowledgeTable:
|
class KnowledgeTable:
|
||||||
@ -159,14 +156,32 @@ class KnowledgeTable:
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
def update_knowledge_by_id(
|
def update_knowledge_by_id(
|
||||||
self, id: str, form_data: KnowledgeUpdateForm, overwrite: bool = False
|
self, id: str, form_data: KnowledgeForm, overwrite: bool = False
|
||||||
) -> Optional[KnowledgeModel]:
|
) -> Optional[KnowledgeModel]:
|
||||||
try:
|
try:
|
||||||
with get_db() as db:
|
with get_db() as db:
|
||||||
knowledge = self.get_knowledge_by_id(id=id)
|
knowledge = self.get_knowledge_by_id(id=id)
|
||||||
db.query(Knowledge).filter_by(id=id).update(
|
db.query(Knowledge).filter_by(id=id).update(
|
||||||
{
|
{
|
||||||
**form_data.model_dump(exclude_none=True),
|
**form_data.model_dump(),
|
||||||
|
"updated_at": int(time.time()),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
db.commit()
|
||||||
|
return self.get_knowledge_by_id(id=id)
|
||||||
|
except Exception as e:
|
||||||
|
log.exception(e)
|
||||||
|
return None
|
||||||
|
|
||||||
|
def update_knowledge_data_by_id(
|
||||||
|
self, id: str, data: dict
|
||||||
|
) -> Optional[KnowledgeModel]:
|
||||||
|
try:
|
||||||
|
with get_db() as db:
|
||||||
|
knowledge = self.get_knowledge_by_id(id=id)
|
||||||
|
db.query(Knowledge).filter_by(id=id).update(
|
||||||
|
{
|
||||||
|
"data": data,
|
||||||
"updated_at": int(time.time()),
|
"updated_at": int(time.time()),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -81,6 +81,7 @@ class ToolResponse(BaseModel):
|
|||||||
user_id: str
|
user_id: str
|
||||||
name: str
|
name: str
|
||||||
meta: ToolMeta
|
meta: ToolMeta
|
||||||
|
access_control: Optional[dict] = None
|
||||||
updated_at: int # timestamp in epoch
|
updated_at: int # timestamp in epoch
|
||||||
created_at: int # timestamp in epoch
|
created_at: int # timestamp in epoch
|
||||||
|
|
||||||
@ -90,6 +91,7 @@ class ToolForm(BaseModel):
|
|||||||
name: str
|
name: str
|
||||||
content: str
|
content: str
|
||||||
meta: ToolMeta
|
meta: ToolMeta
|
||||||
|
access_control: Optional[dict] = None
|
||||||
|
|
||||||
|
|
||||||
class ToolValves(BaseModel):
|
class ToolValves(BaseModel):
|
||||||
|
@ -6,7 +6,6 @@ import logging
|
|||||||
|
|
||||||
from open_webui.apps.webui.models.knowledge import (
|
from open_webui.apps.webui.models.knowledge import (
|
||||||
Knowledges,
|
Knowledges,
|
||||||
KnowledgeUpdateForm,
|
|
||||||
KnowledgeForm,
|
KnowledgeForm,
|
||||||
KnowledgeResponse,
|
KnowledgeResponse,
|
||||||
)
|
)
|
||||||
@ -64,8 +63,8 @@ async def get_knowledge(user=Depends(get_verified_user)):
|
|||||||
file_ids.remove(missing_file)
|
file_ids.remove(missing_file)
|
||||||
|
|
||||||
data["file_ids"] = file_ids
|
data["file_ids"] = file_ids
|
||||||
Knowledges.update_knowledge_by_id(
|
Knowledges.update_knowledge_data_by_id(
|
||||||
id=knowledge_base.id, form_data=KnowledgeUpdateForm(data=data)
|
id=knowledge_base.id, data=data
|
||||||
)
|
)
|
||||||
|
|
||||||
files = Files.get_file_metadatas_by_ids(file_ids)
|
files = Files.get_file_metadatas_by_ids(file_ids)
|
||||||
@ -109,8 +108,8 @@ async def get_knowledge_list(user=Depends(get_verified_user)):
|
|||||||
file_ids.remove(missing_file)
|
file_ids.remove(missing_file)
|
||||||
|
|
||||||
data["file_ids"] = file_ids
|
data["file_ids"] = file_ids
|
||||||
Knowledges.update_knowledge_by_id(
|
Knowledges.update_knowledge_data_by_id(
|
||||||
id=knowledge_base.id, form_data=KnowledgeUpdateForm(data=data)
|
id=knowledge_base.id, data=data
|
||||||
)
|
)
|
||||||
|
|
||||||
files = Files.get_file_metadatas_by_ids(file_ids)
|
files = Files.get_file_metadatas_by_ids(file_ids)
|
||||||
@ -186,7 +185,7 @@ async def get_knowledge_by_id(id: str, user=Depends(get_verified_user)):
|
|||||||
@router.post("/{id}/update", response_model=Optional[KnowledgeFilesResponse])
|
@router.post("/{id}/update", response_model=Optional[KnowledgeFilesResponse])
|
||||||
async def update_knowledge_by_id(
|
async def update_knowledge_by_id(
|
||||||
id: str,
|
id: str,
|
||||||
form_data: KnowledgeUpdateForm,
|
form_data: KnowledgeForm,
|
||||||
user=Depends(get_verified_user),
|
user=Depends(get_verified_user),
|
||||||
):
|
):
|
||||||
knowledge = Knowledges.get_knowledge_by_id(id=id)
|
knowledge = Knowledges.get_knowledge_by_id(id=id)
|
||||||
@ -277,9 +276,7 @@ def add_file_to_knowledge_by_id(
|
|||||||
file_ids.append(form_data.file_id)
|
file_ids.append(form_data.file_id)
|
||||||
data["file_ids"] = file_ids
|
data["file_ids"] = file_ids
|
||||||
|
|
||||||
knowledge = Knowledges.update_knowledge_by_id(
|
knowledge = Knowledges.update_knowledge_data_by_id(id=id.id, data=data)
|
||||||
id=id, form_data=KnowledgeUpdateForm(data=data)
|
|
||||||
)
|
|
||||||
|
|
||||||
if knowledge:
|
if knowledge:
|
||||||
files = Files.get_files_by_ids(file_ids)
|
files = Files.get_files_by_ids(file_ids)
|
||||||
@ -413,9 +410,7 @@ def remove_file_from_knowledge_by_id(
|
|||||||
file_ids.remove(form_data.file_id)
|
file_ids.remove(form_data.file_id)
|
||||||
data["file_ids"] = file_ids
|
data["file_ids"] = file_ids
|
||||||
|
|
||||||
knowledge = Knowledges.update_knowledge_by_id(
|
knowledge = Knowledges.update_knowledge_data_by_id(id=id.id, data=data)
|
||||||
id=id, form_data=KnowledgeUpdateForm(data=data)
|
|
||||||
)
|
|
||||||
|
|
||||||
if knowledge:
|
if knowledge:
|
||||||
files = Files.get_files_by_ids(file_ids)
|
files = Files.get_files_by_ids(file_ids)
|
||||||
@ -496,7 +491,6 @@ async def reset_knowledge_by_id(id: str, user=Depends(get_verified_user)):
|
|||||||
log.debug(e)
|
log.debug(e)
|
||||||
pass
|
pass
|
||||||
|
|
||||||
knowledge = Knowledges.update_knowledge_by_id(
|
knowledge = Knowledges.update_knowledge_data_by_id(id=id.id, data={"file_ids": []})
|
||||||
id=id, form_data=KnowledgeUpdateForm(data={"file_ids": []})
|
|
||||||
)
|
|
||||||
return knowledge
|
return knowledge
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { WEBUI_API_BASE_URL } from '$lib/constants';
|
import { WEBUI_API_BASE_URL } from '$lib/constants';
|
||||||
|
|
||||||
export const createNewKnowledge = async (token: string, name: string, description: string) => {
|
export const createNewKnowledge = async (token: string, name: string, description: string, accessControl: null|object) => {
|
||||||
let error = null;
|
let error = null;
|
||||||
|
|
||||||
const res = await fetch(`${WEBUI_API_BASE_URL}/knowledge/create`, {
|
const res = await fetch(`${WEBUI_API_BASE_URL}/knowledge/create`, {
|
||||||
@ -12,7 +12,8 @@ export const createNewKnowledge = async (token: string, name: string, descriptio
|
|||||||
},
|
},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
name: name,
|
name: name,
|
||||||
description: description
|
description: description,
|
||||||
|
access_control: accessControl
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.then(async (res) => {
|
.then(async (res) => {
|
||||||
@ -130,6 +131,7 @@ type KnowledgeUpdateForm = {
|
|||||||
name?: string;
|
name?: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
data?: object;
|
data?: object;
|
||||||
|
access_control?: null|object;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const updateKnowledgeById = async (token: string, id: string, form: KnowledgeUpdateForm) => {
|
export const updateKnowledgeById = async (token: string, id: string, form: KnowledgeUpdateForm) => {
|
||||||
@ -145,7 +147,8 @@ export const updateKnowledgeById = async (token: string, id: string, form: Knowl
|
|||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
name: form?.name ? form.name : undefined,
|
name: form?.name ? form.name : undefined,
|
||||||
description: form?.description ? form.description : undefined,
|
description: form?.description ? form.description : undefined,
|
||||||
data: form?.data ? form.data : undefined
|
data: form?.data ? form.data : undefined,
|
||||||
|
access_control: form.access_control
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.then(async (res) => {
|
.then(async (res) => {
|
||||||
|
19
src/lib/components/icons/LockClosed.svelte
Normal file
19
src/lib/components/icons/LockClosed.svelte
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
export let className = 'size-4';
|
||||||
|
export let strokeWidth = '1.5';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
stroke-width={strokeWidth}
|
||||||
|
stroke="currentColor"
|
||||||
|
class={className}
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
d="M16.5 10.5V6.75a4.5 4.5 0 1 0-9 0v3.75m-.75 11.25h10.5a2.25 2.25 0 0 0 2.25-2.25v-6.75a2.25 2.25 0 0 0-2.25-2.25H6.75a2.25 2.25 0 0 0-2.25 2.25v6.75a2.25 2.25 0 0 0 2.25 2.25Z"
|
||||||
|
/>
|
||||||
|
</svg>
|
@ -12,6 +12,7 @@
|
|||||||
|
|
||||||
let name = '';
|
let name = '';
|
||||||
let description = '';
|
let description = '';
|
||||||
|
let accessControl = null;
|
||||||
|
|
||||||
const submitHandler = async () => {
|
const submitHandler = async () => {
|
||||||
loading = true;
|
loading = true;
|
||||||
@ -24,7 +25,12 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const res = await createNewKnowledge(localStorage.token, name, description).catch((e) => {
|
const res = await createNewKnowledge(
|
||||||
|
localStorage.token,
|
||||||
|
name,
|
||||||
|
description,
|
||||||
|
accessControl
|
||||||
|
).catch((e) => {
|
||||||
toast.error(e);
|
toast.error(e);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -105,7 +111,9 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mt-2">
|
<div class="mt-2">
|
||||||
<AccessControl />
|
<div class="px-3 py-2 bg-gray-50 dark:bg-gray-950 rounded-lg">
|
||||||
|
<AccessControl bind:accessControl />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex justify-end mt-2">
|
<div class="flex justify-end mt-2">
|
||||||
|
@ -38,8 +38,8 @@
|
|||||||
import EllipsisVertical from '$lib/components/icons/EllipsisVertical.svelte';
|
import EllipsisVertical from '$lib/components/icons/EllipsisVertical.svelte';
|
||||||
import Drawer from '$lib/components/common/Drawer.svelte';
|
import Drawer from '$lib/components/common/Drawer.svelte';
|
||||||
import ChevronLeft from '$lib/components/icons/ChevronLeft.svelte';
|
import ChevronLeft from '$lib/components/icons/ChevronLeft.svelte';
|
||||||
import MenuLines from '$lib/components/icons/MenuLines.svelte';
|
import LockClosed from '$lib/components/icons/LockClosed.svelte';
|
||||||
import AccessControl from '../common/AccessControl.svelte';
|
import AccessControlModal from '../common/AccessControlModal.svelte';
|
||||||
|
|
||||||
let largeScreen = true;
|
let largeScreen = true;
|
||||||
|
|
||||||
@ -63,6 +63,7 @@
|
|||||||
|
|
||||||
let showAddTextContentModal = false;
|
let showAddTextContentModal = false;
|
||||||
let showSyncConfirmModal = false;
|
let showSyncConfirmModal = false;
|
||||||
|
let showAccessControlModal = false;
|
||||||
|
|
||||||
let inputFiles = null;
|
let inputFiles = null;
|
||||||
|
|
||||||
@ -421,7 +422,8 @@
|
|||||||
|
|
||||||
const res = await updateKnowledgeById(localStorage.token, id, {
|
const res = await updateKnowledgeById(localStorage.token, id, {
|
||||||
name: knowledge.name,
|
name: knowledge.name,
|
||||||
description: knowledge.description
|
description: knowledge.description,
|
||||||
|
access_control: knowledge.access_control
|
||||||
}).catch((e) => {
|
}).catch((e) => {
|
||||||
toast.error(e);
|
toast.error(e);
|
||||||
});
|
});
|
||||||
@ -599,6 +601,61 @@
|
|||||||
|
|
||||||
<div class="flex flex-col w-full h-full max-h-[100dvh] translate-y-1" id="collection-container">
|
<div class="flex flex-col w-full h-full max-h-[100dvh] translate-y-1" id="collection-container">
|
||||||
{#if id && knowledge}
|
{#if id && knowledge}
|
||||||
|
<AccessControlModal
|
||||||
|
bind:show={showAccessControlModal}
|
||||||
|
bind:accessControl={knowledge.access_control}
|
||||||
|
onChange={() => {
|
||||||
|
changeDebounceHandler();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<div class="w-full mb-2.5">
|
||||||
|
<div class=" flex w-full">
|
||||||
|
<div class="flex-1">
|
||||||
|
<div class="flex items-center justify-between w-full px-0.5 mb-1">
|
||||||
|
<div class="w-full">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
class="text-left w-full font-semibold text-2xl font-primary bg-transparent outline-none"
|
||||||
|
bind:value={knowledge.name}
|
||||||
|
placeholder="Knowledge Name"
|
||||||
|
on:input={() => {
|
||||||
|
changeDebounceHandler();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="self-center">
|
||||||
|
<button
|
||||||
|
class="bg-gray-50 hover:bg-gray-100 text-black transition px-2 py-1 rounded-full flex gap-1 items-center"
|
||||||
|
type="button"
|
||||||
|
on:click={() => {
|
||||||
|
showAccessControlModal = true;
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<LockClosed strokeWidth="2.5" />
|
||||||
|
|
||||||
|
<div class="text-sm font-medium flex-shrink-0">
|
||||||
|
{$i18n.t('Share')}
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex w-full px-1">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
class="text-left text-xs w-full text-gray-500 bg-transparent outline-none"
|
||||||
|
bind:value={knowledge.description}
|
||||||
|
placeholder="Knowledge Description"
|
||||||
|
on:input={() => {
|
||||||
|
changeDebounceHandler();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="flex flex-row flex-1 h-full max-h-full pb-2.5">
|
<div class="flex flex-row flex-1 h-full max-h-full pb-2.5">
|
||||||
<PaneGroup direction="horizontal">
|
<PaneGroup direction="horizontal">
|
||||||
<Pane
|
<Pane
|
||||||
@ -764,41 +821,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
<div class="m-auto pb-20">
|
<div></div>
|
||||||
<div>
|
|
||||||
<div class=" flex w-full mt-1 mb-3.5">
|
|
||||||
<div class="flex-1">
|
|
||||||
<div class="flex items-center justify-between w-full px-0.5 mb-1">
|
|
||||||
<div class="w-full">
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
class="text-center w-full font-medium text-3xl font-primary bg-transparent outline-none"
|
|
||||||
bind:value={knowledge.name}
|
|
||||||
on:input={() => {
|
|
||||||
changeDebounceHandler();
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="flex w-full px-1">
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
class="text-center w-full text-gray-500 bg-transparent outline-none"
|
|
||||||
bind:value={knowledge.description}
|
|
||||||
on:input={() => {
|
|
||||||
changeDebounceHandler();
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="mt-2">
|
|
||||||
<AccessControl />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</Pane>
|
</Pane>
|
||||||
|
@ -9,12 +9,16 @@
|
|||||||
import Badge from '$lib/components/common/Badge.svelte';
|
import Badge from '$lib/components/common/Badge.svelte';
|
||||||
import ChevronLeft from '$lib/components/icons/ChevronLeft.svelte';
|
import ChevronLeft from '$lib/components/icons/ChevronLeft.svelte';
|
||||||
import Tooltip from '$lib/components/common/Tooltip.svelte';
|
import Tooltip from '$lib/components/common/Tooltip.svelte';
|
||||||
|
import LockClosed from '$lib/components/icons/LockClosed.svelte';
|
||||||
|
import AccessControlModal from '../common/AccessControlModal.svelte';
|
||||||
|
|
||||||
const dispatch = createEventDispatcher();
|
const dispatch = createEventDispatcher();
|
||||||
|
|
||||||
let formElement = null;
|
let formElement = null;
|
||||||
let loading = false;
|
let loading = false;
|
||||||
|
|
||||||
let showConfirm = false;
|
let showConfirm = false;
|
||||||
|
let showAccessControlModal = false;
|
||||||
|
|
||||||
export let edit = false;
|
export let edit = false;
|
||||||
export let clone = false;
|
export let clone = false;
|
||||||
@ -25,6 +29,8 @@
|
|||||||
description: ''
|
description: ''
|
||||||
};
|
};
|
||||||
export let content = '';
|
export let content = '';
|
||||||
|
export let accessControl = null;
|
||||||
|
|
||||||
let _content = '';
|
let _content = '';
|
||||||
|
|
||||||
$: if (content) {
|
$: if (content) {
|
||||||
@ -148,7 +154,8 @@ class Tools:
|
|||||||
id,
|
id,
|
||||||
name,
|
name,
|
||||||
meta,
|
meta,
|
||||||
content
|
content,
|
||||||
|
access_control: accessControl
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -172,6 +179,8 @@ class Tools:
|
|||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<AccessControlModal bind:show={showAccessControlModal} bind:accessControl />
|
||||||
|
|
||||||
<div class=" flex flex-col justify-between w-full overflow-y-auto h-full">
|
<div class=" flex flex-col justify-between w-full overflow-y-auto h-full">
|
||||||
<div class="mx-auto w-full md:px-0 h-full">
|
<div class="mx-auto w-full md:px-0 h-full">
|
||||||
<form
|
<form
|
||||||
@ -205,7 +214,7 @@ class Tools:
|
|||||||
<div class="flex-1">
|
<div class="flex-1">
|
||||||
<Tooltip content={$i18n.t('e.g. My Tools')} placement="top-start">
|
<Tooltip content={$i18n.t('e.g. My Tools')} placement="top-start">
|
||||||
<input
|
<input
|
||||||
class="w-full text-2xl font-medium bg-transparent outline-none"
|
class="w-full text-2xl font-semibold bg-transparent outline-none"
|
||||||
type="text"
|
type="text"
|
||||||
placeholder={$i18n.t('Tool Name')}
|
placeholder={$i18n.t('Tool Name')}
|
||||||
bind:value={name}
|
bind:value={name}
|
||||||
@ -215,7 +224,19 @@ class Tools:
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<Badge type="muted" content={$i18n.t('Tool')} />
|
<button
|
||||||
|
class="bg-gray-50 hover:bg-gray-100 text-black transition px-2 py-1 rounded-full flex gap-1 items-center"
|
||||||
|
type="button"
|
||||||
|
on:click={() => {
|
||||||
|
showAccessControlModal = true;
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<LockClosed strokeWidth="2.5" />
|
||||||
|
|
||||||
|
<div class="text-sm font-medium flex-shrink-0">
|
||||||
|
{$i18n.t('Share')}
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -9,6 +9,8 @@
|
|||||||
import UserCircleSolid from '$lib/components/icons/UserCircleSolid.svelte';
|
import UserCircleSolid from '$lib/components/icons/UserCircleSolid.svelte';
|
||||||
import XMark from '$lib/components/icons/XMark.svelte';
|
import XMark from '$lib/components/icons/XMark.svelte';
|
||||||
|
|
||||||
|
export let onChange: Function = () => {};
|
||||||
|
|
||||||
export let accessControl = null;
|
export let accessControl = null;
|
||||||
|
|
||||||
let selectedGroupId = '';
|
let selectedGroupId = '';
|
||||||
@ -17,6 +19,8 @@
|
|||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
groups = await getGroups(localStorage.token);
|
groups = await getGroups(localStorage.token);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$: onChange(accessControl);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class=" rounded-lg flex flex-col gap-2">
|
<div class=" rounded-lg flex flex-col gap-2">
|
||||||
|
@ -0,0 +1,43 @@
|
|||||||
|
<script>
|
||||||
|
import { getContext } from 'svelte';
|
||||||
|
const i18n = getContext('i18n');
|
||||||
|
|
||||||
|
import Modal from '$lib/components/common/Modal.svelte';
|
||||||
|
import AccessControl from './AccessControl.svelte';
|
||||||
|
|
||||||
|
export let show = false;
|
||||||
|
export let accessControl = null;
|
||||||
|
|
||||||
|
export let onChange = () => {};
|
||||||
|
</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-medium self-center font-primary">
|
||||||
|
{$i18n.t('Share')}
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
class="self-center"
|
||||||
|
on:click={() => {
|
||||||
|
show = false;
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
fill="currentColor"
|
||||||
|
class="w-5 h-5"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="w-full px-5 pb-4">
|
||||||
|
<AccessControl bind:accessControl {onChange} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
@ -36,7 +36,7 @@
|
|||||||
? 'md:max-w-[calc(100%-260px)]'
|
? 'md:max-w-[calc(100%-260px)]'
|
||||||
: ''}"
|
: ''}"
|
||||||
>
|
>
|
||||||
<div class=" px-2.5 py-1 backdrop-blur-xl">
|
<div class=" px-2.5 pt-1 backdrop-blur-xl">
|
||||||
<div class=" flex items-center gap-1">
|
<div class=" flex items-center gap-1">
|
||||||
<div class="{$showSidebar ? 'md:hidden' : ''} self-center flex flex-none items-center">
|
<div class="{$showSidebar ? 'md:hidden' : ''} self-center flex flex-none items-center">
|
||||||
<button
|
<button
|
||||||
@ -97,7 +97,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class=" -mt-1 pb-1 px-[18px] flex-1 max-h-full overflow-y-auto" id="workspace-container">
|
<div class=" pb-1 px-[18px] flex-1 max-h-full overflow-y-auto" id="workspace-container">
|
||||||
<slot />
|
<slot />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -36,7 +36,8 @@
|
|||||||
id: data.id,
|
id: data.id,
|
||||||
name: data.name,
|
name: data.name,
|
||||||
meta: data.meta,
|
meta: data.meta,
|
||||||
content: data.content
|
content: data.content,
|
||||||
|
access_control: data.access_control
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
toast.error(error);
|
toast.error(error);
|
||||||
return null;
|
return null;
|
||||||
@ -86,6 +87,7 @@
|
|||||||
name={tool?.name ?? ''}
|
name={tool?.name ?? ''}
|
||||||
meta={tool?.meta ?? { description: '' }}
|
meta={tool?.meta ?? { description: '' }}
|
||||||
content={tool?.content ?? ''}
|
content={tool?.content ?? ''}
|
||||||
|
access_control={null}
|
||||||
{clone}
|
{clone}
|
||||||
on:save={(e) => {
|
on:save={(e) => {
|
||||||
saveHandler(e.detail);
|
saveHandler(e.detail);
|
||||||
|
@ -36,7 +36,8 @@
|
|||||||
id: data.id,
|
id: data.id,
|
||||||
name: data.name,
|
name: data.name,
|
||||||
meta: data.meta,
|
meta: data.meta,
|
||||||
content: data.content
|
content: data.content,
|
||||||
|
access_control: data.access_control
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
toast.error(error);
|
toast.error(error);
|
||||||
return null;
|
return null;
|
||||||
@ -73,6 +74,7 @@
|
|||||||
name={tool.name}
|
name={tool.name}
|
||||||
meta={tool.meta}
|
meta={tool.meta}
|
||||||
content={tool.content}
|
content={tool.content}
|
||||||
|
accessControl={tool.access_control}
|
||||||
on:save={(e) => {
|
on:save={(e) => {
|
||||||
saveHandler(e.detail);
|
saveHandler(e.detail);
|
||||||
}}
|
}}
|
||||||
|
Loading…
Reference in New Issue
Block a user