From 4aedfdc5471a1f13c1084b34b48ea3ed6311cd42 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Tue, 10 Feb 2026 15:47:21 -0600 Subject: [PATCH] refac --- backend/open_webui/models/prompts.py | 8 +-- backend/open_webui/routers/knowledge.py | 49 +++++++++++++++++++ backend/open_webui/routers/prompts.py | 49 ++++++++++++++++++- src/lib/apis/knowledge/index.ts | 33 +++++++++++++ src/lib/apis/prompts/index.ts | 38 ++++++++++++-- .../workspace/Knowledge/KnowledgeBase.svelte | 10 +++- .../workspace/Prompts/PromptEditor.svelte | 15 ++++-- 7 files changed, 184 insertions(+), 18 deletions(-) diff --git a/backend/open_webui/models/prompts.py b/backend/open_webui/models/prompts.py index 9562d957d..7bc1f9dee 100644 --- a/backend/open_webui/models/prompts.py +++ b/backend/open_webui/models/prompts.py @@ -502,10 +502,9 @@ class PromptsTable: name: str, command: str, tags: Optional[list[str]] = None, - access_grants: Optional[list[dict]] = None, db: Optional[Session] = None, ) -> Optional[PromptModel]: - """Update only name, command, tags, and access grants (no history created).""" + """Update only name, command, and tags (no history created).""" try: with get_db_context(db) as db: prompt = db.query(Prompt).filter_by(id=prompt_id).first() @@ -517,11 +516,6 @@ class PromptsTable: if tags is not None: prompt.tags = tags - - if access_grants is not None: - AccessGrants.set_access_grants( - "prompt", prompt_id, access_grants, db=db - ) prompt.updated_at = int(time.time()) db.commit() diff --git a/backend/open_webui/routers/knowledge.py b/backend/open_webui/routers/knowledge.py index 7fff5e5f2..ff4b92414 100644 --- a/backend/open_webui/routers/knowledge.py +++ b/backend/open_webui/routers/knowledge.py @@ -501,6 +501,55 @@ async def update_knowledge_by_id( ) +############################ +# UpdateKnowledgeAccessById +############################ + + +class KnowledgeAccessGrantsForm(BaseModel): + access_grants: list[dict] + + +@router.post("/{id}/access/update", response_model=Optional[KnowledgeFilesResponse]) +async def update_knowledge_access_by_id( + id: str, + form_data: KnowledgeAccessGrantsForm, + user=Depends(get_verified_user), + db: Session = Depends(get_session), +): + knowledge = Knowledges.get_knowledge_by_id(id=id, db=db) + if not knowledge: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail=ERROR_MESSAGES.NOT_FOUND, + ) + + if ( + knowledge.user_id != user.id + and not AccessGrants.has_access( + user_id=user.id, + resource_type="knowledge", + resource_id=knowledge.id, + permission="write", + db=db, + ) + and user.role != "admin" + ): + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail=ERROR_MESSAGES.ACCESS_PROHIBITED, + ) + + AccessGrants.set_access_grants( + "knowledge", id, form_data.access_grants, db=db + ) + + return KnowledgeFilesResponse( + **Knowledges.get_knowledge_by_id(id=id, db=db).model_dump(), + files=Knowledges.get_file_metadatas_by_id(id, db=db), + ) + + ############################ # GetKnowledgeFilesById ############################ diff --git a/backend/open_webui/routers/prompts.py b/backend/open_webui/routers/prompts.py index f2580ef93..1bec0950c 100644 --- a/backend/open_webui/routers/prompts.py +++ b/backend/open_webui/routers/prompts.py @@ -33,7 +33,6 @@ class PromptMetadataForm(BaseModel): name: str command: str tags: Optional[list[str]] = None - access_grants: Optional[list[dict]] = None router = APIRouter() @@ -373,7 +372,7 @@ async def update_prompt_metadata( ) updated_prompt = Prompts.update_prompt_metadata( - prompt.id, form_data.name, form_data.command, form_data.tags, form_data.access_grants, db=db + prompt.id, form_data.name, form_data.command, form_data.tags, db=db ) if updated_prompt: return updated_prompt @@ -426,6 +425,52 @@ async def set_prompt_version( ) +############################ +# UpdatePromptAccessById +############################ + + +class PromptAccessGrantsForm(BaseModel): + access_grants: list[dict] + + +@router.post("/id/{prompt_id}/access/update", response_model=Optional[PromptModel]) +async def update_prompt_access_by_id( + prompt_id: str, + form_data: PromptAccessGrantsForm, + user=Depends(get_verified_user), + db: Session = Depends(get_session), +): + prompt = Prompts.get_prompt_by_id(prompt_id, db=db) + if not prompt: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail=ERROR_MESSAGES.NOT_FOUND, + ) + + if ( + prompt.user_id != user.id + and not AccessGrants.has_access( + user_id=user.id, + resource_type="prompt", + resource_id=prompt.id, + permission="write", + db=db, + ) + and user.role != "admin" + ): + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, + detail=ERROR_MESSAGES.ACCESS_PROHIBITED, + ) + + AccessGrants.set_access_grants( + "prompt", prompt_id, form_data.access_grants, db=db + ) + + return Prompts.get_prompt_by_id(prompt_id, db=db) + + ############################ # DeletePromptById ############################ diff --git a/src/lib/apis/knowledge/index.ts b/src/lib/apis/knowledge/index.ts index 4c7c90484..f314bae63 100644 --- a/src/lib/apis/knowledge/index.ts +++ b/src/lib/apis/knowledge/index.ts @@ -289,6 +289,39 @@ export const updateKnowledgeById = async (token: string, id: string, form: Knowl return res; }; +export const updateKnowledgeAccessGrants = async ( + token: string, + id: string, + accessGrants: any[] +) => { + let error = null; + + const res = await fetch(`${WEBUI_API_BASE_URL}/knowledge/${id}/access/update`, { + method: 'POST', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + authorization: `Bearer ${token}` + }, + body: JSON.stringify({ access_grants: accessGrants }) + }) + .then(async (res) => { + if (!res.ok) throw await res.json(); + return res.json(); + }) + .catch((err) => { + error = err.detail; + console.error(err); + return null; + }); + + if (error) { + throw error; + } + + return res; +}; + export const addFileToKnowledgeById = async (token: string, id: string, fileId: string) => { let error = null; diff --git a/src/lib/apis/prompts/index.ts b/src/lib/apis/prompts/index.ts index 7b2704cf0..3c35a2ced 100644 --- a/src/lib/apis/prompts/index.ts +++ b/src/lib/apis/prompts/index.ts @@ -331,8 +331,7 @@ export const updatePromptMetadata = async ( promptId: string, name: string, command: string, - tags: string[] = [], - accessGrants: any[] | null = null + tags: string[] = [] ) => { let error = null; @@ -343,7 +342,7 @@ export const updatePromptMetadata = async ( 'Content-Type': 'application/json', authorization: `Bearer ${token}` }, - body: JSON.stringify({ name, command, tags, access_grants: accessGrants }) + body: JSON.stringify({ name, command, tags }) }) .then(async (res) => { if (!res.ok) throw await res.json(); @@ -429,6 +428,39 @@ export const deletePromptById = async (token: string, promptId: string) => { return res; }; +export const updatePromptAccessGrants = async ( + token: string, + promptId: string, + accessGrants: any[] +) => { + let error = null; + + const res = await fetch(`${WEBUI_API_BASE_URL}/prompts/id/${promptId}/access/update`, { + method: 'POST', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + authorization: `Bearer ${token}` + }, + body: JSON.stringify({ access_grants: accessGrants }) + }) + .then(async (res) => { + if (!res.ok) throw await res.json(); + return res.json(); + }) + .catch((err) => { + error = err.detail; + console.error(err); + return null; + }); + + if (error) { + throw error; + } + + return res; +}; + //////////////////////////// // Prompt History APIs //////////////////////////// diff --git a/src/lib/components/workspace/Knowledge/KnowledgeBase.svelte b/src/lib/components/workspace/Knowledge/KnowledgeBase.svelte index 6d8d364dc..50b443033 100644 --- a/src/lib/components/workspace/Knowledge/KnowledgeBase.svelte +++ b/src/lib/components/workspace/Knowledge/KnowledgeBase.svelte @@ -31,6 +31,7 @@ resetKnowledgeById, updateFileFromKnowledgeById, updateKnowledgeById, + updateKnowledgeAccessGrants, searchKnowledgeFilesById } from '$lib/apis/knowledge'; import { processWeb, processYoutubeVideo } from '$lib/apis/retrieval'; @@ -838,8 +839,13 @@ sharePublic={$user?.permissions?.sharing?.public_knowledge || $user?.role === 'admin' || knowledge?.write_access} - onChange={() => { - changeDebounceHandler(); + onChange={async () => { + try { + await updateKnowledgeAccessGrants(localStorage.token, id, knowledge.access_grants ?? []); + toast.success($i18n.t('Saved')); + } catch (error) { + toast.error(`${error}`); + } }} accessRoles={['read', 'write']} /> diff --git a/src/lib/components/workspace/Prompts/PromptEditor.svelte b/src/lib/components/workspace/Prompts/PromptEditor.svelte index acd3c3fff..288d674c0 100644 --- a/src/lib/components/workspace/Prompts/PromptEditor.svelte +++ b/src/lib/components/workspace/Prompts/PromptEditor.svelte @@ -18,6 +18,7 @@ setProductionPromptVersion, deletePromptHistoryVersion, updatePromptMetadata, + updatePromptAccessGrants, getPromptTags } from '$lib/apis/prompts'; import dayjs from 'dayjs'; @@ -227,8 +228,7 @@ prompt?.id, name, command, - tags.map((tag) => tag.name), - accessGrants + tags.map((tag) => tag.name) ); // Update originals on success originalName = name; @@ -283,8 +283,15 @@ accessRoles={['read', 'write']} share={$user?.permissions?.sharing?.prompts || $user?.role === 'admin'} sharePublic={$user?.permissions?.sharing?.public_prompts || $user?.role === 'admin' || edit} - onChange={() => { - debouncedSaveMetadata(); + onChange={async () => { + if (edit && prompt?.id) { + try { + await updatePromptAccessGrants(localStorage.token, prompt.id, accessGrants); + toast.success($i18n.t('Saved')); + } catch (error) { + toast.error(`${error}`); + } + } }} />