diff --git a/backend/open_webui/config.py b/backend/open_webui/config.py index 6dfec3c42e..3ff2578251 100644 --- a/backend/open_webui/config.py +++ b/backend/open_webui/config.py @@ -1843,6 +1843,10 @@ USER_PERMISSIONS_CHAT_ALLOW_PUBLIC_SHARING = ( os.getenv('USER_PERMISSIONS_CHAT_ALLOW_PUBLIC_SHARING', 'False').lower() == 'true' ) +USER_PERMISSIONS_CHAT_ALLOW_OPEN_SHARING = ( + os.getenv('USER_PERMISSIONS_CHAT_ALLOW_OPEN_SHARING', 'False').lower() == 'true' +) + USER_PERMISSIONS_CHAT_EXPORT = os.getenv('USER_PERMISSIONS_CHAT_EXPORT', 'True').lower() == 'true' USER_PERMISSIONS_CHAT_IMPORT = os.getenv('USER_PERMISSIONS_CHAT_IMPORT', 'True').lower() == 'true' @@ -1929,6 +1933,7 @@ DEFAULT_USER_PERMISSIONS = { 'public_notes': USER_PERMISSIONS_NOTES_ALLOW_PUBLIC_SHARING, 'folders': USER_PERMISSIONS_FOLDERS_ALLOW_SHARING, 'public_chats': USER_PERMISSIONS_CHAT_ALLOW_PUBLIC_SHARING, + 'open_chats': USER_PERMISSIONS_CHAT_ALLOW_OPEN_SHARING, 'public_calendars': USER_PERMISSIONS_CALENDAR_ALLOW_PUBLIC_SHARING, }, 'access_grants': { diff --git a/backend/open_webui/models/access_grants.py b/backend/open_webui/models/access_grants.py index f944ff8655..eb23740273 100644 --- a/backend/open_webui/models/access_grants.py +++ b/backend/open_webui/models/access_grants.py @@ -11,6 +11,11 @@ from sqlalchemy.ext.asyncio import AsyncSession log = logging.getLogger(__name__) +PRINCIPAL_TYPE_ANYONE = 'anyone' +PRINCIPAL_TYPE_GROUP = 'group' +PRINCIPAL_TYPE_USER = 'user' +WILDCARD_PRINCIPAL_ID = '*' + #################### # AccessGrant DB Schema @@ -23,7 +28,7 @@ class AccessGrant(Base): id = Column(Text, primary_key=True) resource_type = Column(Text, nullable=False) # "knowledge", "model", "prompt", "tool", "note", "channel", "file" resource_id = Column(Text, nullable=False) - principal_type = Column(Text, nullable=False) # "user" or "group" + principal_type = Column(Text, nullable=False) # "user", "group", or "anyone" principal_id = Column(Text, nullable=False) # user_id, group_id, or "*" (wildcard for public) permission = Column(Text, nullable=False) # "read" or "write" created_at = Column(BigInteger, nullable=False) @@ -163,12 +168,16 @@ def normalize_access_grants(access_grants: Optional[list]) -> list[dict]: principal_id = grant.get('principal_id') permission = grant.get('permission') - if principal_type not in ('user', 'group'): + if principal_type not in (PRINCIPAL_TYPE_USER, PRINCIPAL_TYPE_GROUP, PRINCIPAL_TYPE_ANYONE): continue if permission not in ('read', 'write'): continue if not isinstance(principal_id, str) or not principal_id: continue + if principal_type == PRINCIPAL_TYPE_ANYONE and ( + principal_id != WILDCARD_PRINCIPAL_ID or permission != 'read' + ): + continue key = (principal_type, principal_id, permission) deduped[key] = { @@ -186,7 +195,11 @@ def has_public_read_access_grant(access_grants: Optional[list]) -> bool: Returns True when a direct grant list includes wildcard public-read. """ for grant in normalize_access_grants(access_grants): - if grant['principal_type'] == 'user' and grant['principal_id'] == '*' and grant['permission'] == 'read': + if ( + grant['principal_type'] == PRINCIPAL_TYPE_USER + and grant['principal_id'] == WILDCARD_PRINCIPAL_ID + and grant['permission'] == 'read' + ): return True return False @@ -196,7 +209,25 @@ def has_public_write_access_grant(access_grants: Optional[list]) -> bool: Returns True when a direct grant list includes wildcard public-write. """ for grant in normalize_access_grants(access_grants): - if grant['principal_type'] == 'user' and grant['principal_id'] == '*' and grant['permission'] == 'write': + if ( + grant['principal_type'] == PRINCIPAL_TYPE_USER + and grant['principal_id'] == WILDCARD_PRINCIPAL_ID + and grant['permission'] == 'write' + ): + return True + return False + + +def has_anyone_read_access_grant(access_grants: Optional[list]) -> bool: + """ + Returns True when a direct grant list includes no-auth anyone-read. + """ + for grant in normalize_access_grants(access_grants): + if ( + grant['principal_type'] == PRINCIPAL_TYPE_ANYONE + and grant['principal_id'] == WILDCARD_PRINCIPAL_ID + and grant['permission'] == 'read' + ): return True return False @@ -206,7 +237,7 @@ def has_user_access_grant(access_grants: Optional[list]) -> bool: Returns True when a direct grant list includes any non-wildcard user grant. """ for grant in normalize_access_grants(access_grants): - if grant['principal_type'] == 'user' and grant['principal_id'] != '*': + if grant['principal_type'] == PRINCIPAL_TYPE_USER and grant['principal_id'] != WILDCARD_PRINCIPAL_ID: return True return False @@ -223,12 +254,27 @@ def strip_user_access_grants(access_grants: Optional[list]) -> list: for grant in access_grants if not ( (grant.get('principal_type') if isinstance(grant, dict) else getattr(grant, 'principal_type', None)) - == 'user' - and (grant.get('principal_id') if isinstance(grant, dict) else getattr(grant, 'principal_id', None)) != '*' + == PRINCIPAL_TYPE_USER + and (grant.get('principal_id') if isinstance(grant, dict) else getattr(grant, 'principal_id', None)) + != WILDCARD_PRINCIPAL_ID ) ] +def strip_anyone_access_grants(access_grants: Optional[list]) -> list: + """ + Remove no-auth anyone grants from the list. + """ + if not access_grants: + return [] + return [ + grant + for grant in access_grants + if (grant.get('principal_type') if isinstance(grant, dict) else getattr(grant, 'principal_type', None)) + != PRINCIPAL_TYPE_ANYONE + ] + + def grants_to_access_control(grants: list) -> Optional[dict]: """ Convert a list of grant objects (AccessGrantModel or AccessGrantResponse) @@ -493,6 +539,28 @@ class AccessGrantsTable: result_dict[g.resource_id].append(AccessGrantModel.model_validate(g)) return result_dict + async def has_anyone_access( + self, + resource_type: str, + resource_id: str, + permission: str = 'read', + db: Optional[AsyncSession] = None, + ) -> bool: + """Check for a no-auth anyone:* grant. Callers must opt in explicitly.""" + async with get_async_db_context(db) as db: + result = await db.execute( + select(AccessGrant) + .filter( + AccessGrant.resource_type == resource_type, + AccessGrant.resource_id == resource_id, + AccessGrant.principal_type == PRINCIPAL_TYPE_ANYONE, + AccessGrant.principal_id == WILDCARD_PRINCIPAL_ID, + AccessGrant.permission == permission, + ) + .limit(1) + ) + return result.scalars().first() is not None + async def has_access( self, user_id: str, diff --git a/backend/open_webui/routers/chats.py b/backend/open_webui/routers/chats.py index 1a707607e0..0c657bedba 100644 --- a/backend/open_webui/routers/chats.py +++ b/backend/open_webui/routers/chats.py @@ -6,8 +6,9 @@ import logging from typing import Optional from uuid import uuid4 -from fastapi import APIRouter, Depends, HTTPException, Request, status +from fastapi import APIRouter, BackgroundTasks, Depends, HTTPException, Request, Response, status from fastapi.responses import StreamingResponse +from fastapi.security import HTTPAuthorizationCredentials from open_webui.config import ENABLE_ADMIN_CHAT_ACCESS, ENABLE_ADMIN_EXPORT from open_webui.constants import ERROR_MESSAGES from open_webui.events import EVENTS, publish_event @@ -37,7 +38,7 @@ from open_webui.socket.main import get_event_emitter from open_webui.tasks import has_active_tasks, stop_item_tasks from open_webui.utils.access_control import filter_allowed_access_grants, has_permission from open_webui.utils.access_control.folders import has_folder_access -from open_webui.utils.auth import get_admin_user, get_verified_user +from open_webui.utils.auth import bearer_security, get_admin_user, get_current_user, get_verified_user from open_webui.utils.chat_fork import build_fork_history from open_webui.utils.context_compaction import compact_chat_branch, get_chat_context_usage from open_webui.utils.misc import get_message_list @@ -60,6 +61,47 @@ CHAT_CONFIG_KEYS = { } +async def get_optional_verified_user( + request: Request, + response: Response, + background_tasks: BackgroundTasks, + auth_token: HTTPAuthorizationCredentials | None = Depends(bearer_security), +): + try: + user = await get_current_user(request, response, background_tasks, auth_token) + except HTTPException: + return None + + if user.role not in {'user', 'admin'}: + return None + return user + + +async def is_open_shared_chat(shared, db: AsyncSession) -> bool: + return await AccessGrants.has_anyone_access( + resource_type='shared_chat', + resource_id=shared.chat_id, + permission='read', + db=db, + ) + + +async def can_read_shared_chat(user, shared, db: AsyncSession) -> bool: + if user.role == 'pending': + return False + if user.role == 'admin' and ENABLE_ADMIN_CHAT_ACCESS: + return True + if shared.user_id == user.id: + return True + return await AccessGrants.has_access( + user_id=user.id, + resource_type='shared_chat', + resource_id=shared.chat_id, + permission='read', + db=db, + ) + + async def add_active_state_to_chat_list( request: Request, chat_list: list[ChatTitleIdResponse] ) -> list[ChatTitleIdResponse]: @@ -1082,38 +1124,30 @@ async def get_shared_session_user_chat_list( @router.get('/share/{share_id}', response_model=ChatResponse | None) async def get_shared_chat_by_id( - share_id: str, user=Depends(get_verified_user), db: AsyncSession = Depends(get_async_session) + share_id: str, user=Depends(get_optional_verified_user), db: AsyncSession = Depends(get_async_session) ): - if user.role == 'pending': - raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail=ERROR_MESSAGES.NOT_FOUND) + shared = await SharedChats.get_by_id(share_id, db=db) + if shared: + if await is_open_shared_chat(shared, db=db) or ( + user is not None and await can_read_shared_chat(user, shared, db=db) + ): + chat = await Chats.get_chat_by_share_id(share_id, db=db) + if chat: + return ChatResponse(**chat.model_dump()) - chat = await Chats.get_chat_by_share_id(share_id, db=db) + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, + detail=ERROR_MESSAGES.ACCESS_PROHIBITED if user else ERROR_MESSAGES.INVALID_TOKEN, + ) # Fallback: admins can also access any chat directly by chat ID - if not chat and user.role == 'admin' and ENABLE_ADMIN_CHAT_ACCESS: + chat = None + if user is not None and user.role == 'admin' and ENABLE_ADMIN_CHAT_ACCESS: chat = await Chats.get_chat_by_id(share_id, db=db) + if chat: + return ChatResponse(**chat.model_dump()) - if not chat: - raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail=ERROR_MESSAGES.NOT_FOUND) - - # Look up the original chat_id to check access grants (admins bypass) - if user.role != 'admin' or not ENABLE_ADMIN_CHAT_ACCESS: - shared = await SharedChats.get_by_id(share_id, db=db) - if shared and shared.user_id != user.id: - has_grant = await AccessGrants.has_access( - user_id=user.id, - resource_type='shared_chat', - resource_id=shared.chat_id, - permission='read', - db=db, - ) - if not has_grant: - raise HTTPException( - status_code=status.HTTP_401_UNAUTHORIZED, - detail=ERROR_MESSAGES.ACCESS_PROHIBITED, - ) - - return ChatResponse(**chat.model_dump()) + raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail=ERROR_MESSAGES.NOT_FOUND) ############################ @@ -1753,7 +1787,7 @@ async def clone_shared_chat_by_id( # Enforce access grants (owner and admins bypass) shared = await SharedChats.get_by_id(id, db=db) if shared and user.role != 'admin' and shared.user_id != user.id: - has_grant = await AccessGrants.has_access( + has_grant = await is_open_shared_chat(shared, db=db) or await AccessGrants.has_access( user_id=user.id, resource_type='shared_chat', resource_id=shared.chat_id, @@ -1948,6 +1982,8 @@ async def update_shared_chat_access_by_id( user.role, form_data.access_grants, 'sharing.public_chats', + 'sharing.open_chats', + db=db, ) await AccessGrants.set_access_grants('shared_chat', id, form_data.access_grants, db=db) diff --git a/backend/open_webui/utils/access_control/__init__.py b/backend/open_webui/utils/access_control/__init__.py index 975008b4c1..a81aac812a 100644 --- a/backend/open_webui/utils/access_control/__init__.py +++ b/backend/open_webui/utils/access_control/__init__.py @@ -3,9 +3,11 @@ from typing import Any from open_webui.config import DEFAULT_USER_PERMISSIONS from open_webui.models.access_grants import ( + has_anyone_read_access_grant, has_public_read_access_grant, has_public_write_access_grant, has_user_access_grant, + strip_anyone_access_grants, strip_user_access_grants, ) from open_webui.models.groups import Groups @@ -215,13 +217,31 @@ async def filter_allowed_access_grants( user_role: str, access_grants: list, public_permission_key: str, + anyone_permission_key: str | None = None, db: AsyncSession | None = None, ) -> list: """ Checks if the user has the required permissions to grant access to a resource. Returns the filtered list of access grants if permissions are missing. """ - if user_role == 'admin' or not access_grants: + if not access_grants: + return access_grants + + if has_anyone_read_access_grant(access_grants) and ( + not anyone_permission_key + or ( + user_role != 'admin' + and not await has_permission( + user_id, + anyone_permission_key, + default_permissions, + db=db, + ) + ) + ): + access_grants = strip_anyone_access_grants(access_grants) + + if user_role == 'admin': return access_grants # Check if user can share publicly diff --git a/src/lib/components/admin/Users/Groups/Permissions.svelte b/src/lib/components/admin/Users/Groups/Permissions.svelte index 710ca0cb77..2f276ab364 100644 --- a/src/lib/components/admin/Users/Groups/Permissions.svelte +++ b/src/lib/components/admin/Users/Groups/Permissions.svelte @@ -494,6 +494,25 @@ {/if} + +
+
+
+ {$i18n.t('Chats Open Sharing')} +
+ +
+ {#if defaultPermissions?.sharing?.open_chats && !permissions.sharing.open_chats} +
+
+ {$i18n.t('This is a default user permission and will remain enabled.')} +
+
+ {/if} +
{/if} {#if permissions.features.calendar} diff --git a/src/lib/components/chat/ShareChatModal.svelte b/src/lib/components/chat/ShareChatModal.svelte index 842808b439..e906e6177f 100644 --- a/src/lib/components/chat/ShareChatModal.svelte +++ b/src/lib/components/chat/ShareChatModal.svelte @@ -158,6 +158,7 @@ bind:accessGrants accessRoles={['read']} sharePublic={$user?.permissions?.sharing?.public_chats || $user?.role === 'admin'} + shareOpen={$user?.permissions?.sharing?.open_chats || $user?.role === 'admin'} shareUsers={($user?.permissions?.access_grants?.allow_users ?? true) || $user?.role === 'admin'} onChange={saveAccessGrants} diff --git a/src/lib/components/workspace/common/AccessControl.svelte b/src/lib/components/workspace/common/AccessControl.svelte index f3cfa1ef6f..c0654c6850 100644 --- a/src/lib/components/workspace/common/AccessControl.svelte +++ b/src/lib/components/workspace/common/AccessControl.svelte @@ -16,7 +16,7 @@ type AccessGrant = { id?: string; - principal_type: 'user' | 'group'; + principal_type: 'user' | 'group' | 'anyone'; principal_id: string; permission: 'read' | 'write'; }; @@ -34,6 +34,7 @@ export let share = true; export let sharePublic = true; + export let shareOpen = false; export let shareUsers = true; export let allowGroups = true; export let defaultPermission: 'read' | 'write' = 'read'; @@ -162,6 +163,14 @@ grant.principal_type === 'user' && grant.principal_id === '*' && grant.permission === 'read' ); + const hasAnyoneReadGrant = (grants: AccessGrant[]): boolean => + grants.some( + (grant) => + grant.principal_type === 'anyone' && + grant.principal_id === '*' && + grant.permission === 'read' + ); + const hasPublicWriteGrant = (grants: AccessGrant[]): boolean => grants.some( (grant) => @@ -170,16 +179,17 @@ grant.permission === 'write' ); - const currentGrants = (): AccessGrant[] => - Array.isArray(accessGrants) ? (accessGrants as AccessGrant[]) : []; + const currentGrants = (grants: AccessGrant[] | any = accessGrants): AccessGrant[] => + Array.isArray(grants) ? (grants as AccessGrant[]) : []; const getPrincipalIdsByPermission = ( principalType: 'user' | 'group', - permission: 'read' | 'write' + permission: 'read' | 'write', + grants: AccessGrant[] | any = accessGrants ): string[] => Array.from( new Set( - currentGrants() + currentGrants(grants) .filter( (grant) => grant.principal_type === principalType && grant.permission === permission ) @@ -188,7 +198,7 @@ ); const hasPrincipalGrant = ( - principalType: 'user' | 'group', + principalType: 'user' | 'group' | 'anyone', principalId: string, permission: 'read' | 'write' ): boolean => @@ -204,17 +214,32 @@ onChange(accessGrants); }; - const setPublic = (isPublic: boolean) => { - // Remove all user:* grants + const getVisibility = (grants: AccessGrant[]): 'private' | 'public' | 'open' => { + if (hasAnyoneReadGrant(grants)) return 'open'; + if (hasPublicReadGrant(grants)) return 'public'; + return 'private'; + }; + + const setVisibility = (visibility: 'private' | 'public' | 'open') => { const filtered = currentGrants().filter( - (grant) => !(grant.principal_type === 'user' && grant.principal_id === '*') + (grant) => + !( + (grant.principal_type === 'user' || grant.principal_type === 'anyone') && + grant.principal_id === '*' + ) ); - if (isPublic) { + if (visibility === 'public') { filtered.push({ principal_type: 'user', principal_id: '*', permission: 'read' }); + } else if (visibility === 'open') { + filtered.push({ + principal_type: 'anyone', + principal_id: '*', + permission: 'read' + }); } commitAccessGrants(filtered); }; @@ -237,7 +262,7 @@ }; const upsertPrincipalGrant = ( - principalType: 'user' | 'group', + principalType: 'user' | 'group' | 'anyone', principalId: string, permission: 'read' | 'write', grants: AccessGrant[] @@ -263,7 +288,7 @@ }; const removePrincipalGrant = ( - principalType: 'user' | 'group', + principalType: 'user' | 'group' | 'anyone', principalId: string, permission: 'read' | 'write', grants: AccessGrant[] @@ -277,14 +302,14 @@ ) ); - const removePrincipal = (principalType: 'user' | 'group', principalId: string) => { + const removePrincipal = (principalType: 'user' | 'group' | 'anyone', principalId: string) => { let next = [...currentGrants()]; next = removePrincipalGrant(principalType, principalId, 'read', next); next = removePrincipalGrant(principalType, principalId, 'write', next); commitAccessGrants(next); }; - const togglePrincipalWrite = (principalType: 'user' | 'group', principalId: string) => { + const togglePrincipalWrite = (principalType: 'user' | 'group' | 'anyone', principalId: string) => { let next = [...currentGrants()]; const hasWrite = hasPrincipalGrant(principalType, principalId, 'write'); if (hasWrite) { @@ -379,12 +404,14 @@ $: if (readGroupIds.length > 0 || writeGroupIds.length > 0) { void ensureGroupsByIds([...readGroupIds, ...writeGroupIds]); } - $: readGroupIds = (accessGrants, getPrincipalIdsByPermission('group', 'read')); - $: writeGroupIds = (accessGrants, getPrincipalIdsByPermission('group', 'write')); - $: readUserIds = - (accessGrants, getPrincipalIdsByPermission('user', 'read').filter((id) => id !== '*')); - $: writeUserIds = - (accessGrants, getPrincipalIdsByPermission('user', 'write').filter((id) => id !== '*')); + $: readGroupIds = getPrincipalIdsByPermission('group', 'read', accessGrants); + $: writeGroupIds = getPrincipalIdsByPermission('group', 'write', accessGrants); + $: readUserIds = getPrincipalIdsByPermission('user', 'read', accessGrants).filter( + (id) => id !== '*' + ); + $: writeUserIds = getPrincipalIdsByPermission('user', 'write', accessGrants).filter( + (id) => id !== '*' + ); $: selectedUserIds = Array.from(new Set([...readUserIds, ...writeUserIds])); @@ -450,7 +477,7 @@
- {#if !hasPublicReadGrant(accessGrants ?? [])} + {#if getVisibility(accessGrants ?? []) === 'private'}
- {#if !hasPublicReadGrant(accessGrants ?? [])} + {#if getVisibility(accessGrants ?? []) === 'private'} {$i18n.t('Only select users and groups with permission can access')} - {:else} + {:else if getVisibility(accessGrants ?? []) === 'public'} {$i18n.t('Accessible to all users')} + {:else} + {$i18n.t('Anyone with the link can view')} {/if}
- {#if hasPublicReadGrant(accessGrants ?? []) && accessRoles.includes('write')} + {#if hasPublicReadGrant(accessGrants ?? []) && !hasAnyoneReadGrant(accessGrants ?? []) && accessRoles.includes('write')}
{$i18n.t('Allow public write access')} @@ -660,7 +692,7 @@ {/each} {/if} - {#if !hasPublicReadGrant(accessGrants ?? []) && accessGroups.length === 0 && selectedUsers.length === 0} + {#if getVisibility(accessGrants ?? []) === 'private' && accessGroups.length === 0 && selectedUsers.length === 0}
{$i18n.t('No access grants. Private to you.')}
diff --git a/src/lib/components/workspace/common/AccessControlModal.svelte b/src/lib/components/workspace/common/AccessControlModal.svelte index c03aa1fa40..3d67775a89 100644 --- a/src/lib/components/workspace/common/AccessControlModal.svelte +++ b/src/lib/components/workspace/common/AccessControlModal.svelte @@ -8,7 +8,7 @@ type AccessGrant = { id?: string; - principal_type: 'user' | 'group'; + principal_type: 'user' | 'group' | 'anyone'; principal_id: string; permission: 'read' | 'write'; }; @@ -20,6 +20,7 @@ export let share = true; export let sharePublic = true; + export let shareOpen = false; export let shareUsers = true; export let onChange = () => {}; @@ -49,6 +50,7 @@ {accessRoles} {share} {sharePublic} + {shareOpen} {shareUsers} />
diff --git a/src/lib/constants/permissions.ts b/src/lib/constants/permissions.ts index 25a618c79f..7eec950a50 100644 --- a/src/lib/constants/permissions.ts +++ b/src/lib/constants/permissions.ts @@ -29,6 +29,7 @@ export const DEFAULT_PERMISSIONS = { public_notes: false, folders: false, public_chats: false, + open_chats: false, public_calendars: false }, access_grants: { diff --git a/src/lib/i18n/locales/en-US/translation.json b/src/lib/i18n/locales/en-US/translation.json index c14841a9a7..6f7d28d4f8 100644 --- a/src/lib/i18n/locales/en-US/translation.json +++ b/src/lib/i18n/locales/en-US/translation.json @@ -223,6 +223,7 @@ "and create a new shared link.": "", "Android": "", "Anyone": "", + "Anyone with the link can view": "", "API Auth String": "", "API Base URL": "", "API Key": "", @@ -432,6 +433,7 @@ "Chat unshared successfully.": "", "chats": "", "Chats": "", + "Chats Open Sharing": "", "Chats Public Sharing": "", "Check Again": "", "Check for updates": "", @@ -1908,6 +1910,7 @@ "Open Modal To Manage Floating Quick Actions": "", "Open Modal To Manage Image Compression": "", "Open Model Selector": "", + "Open": "", "Open note": "", "Open Settings": "", "Open Sidebar": "", diff --git a/src/routes/s/[id]/+page.svelte b/src/routes/s/[id]/+page.svelte index cfec88bcfc..c1f300b0e3 100644 --- a/src/routes/s/[id]/+page.svelte +++ b/src/routes/s/[id]/+page.svelte @@ -43,6 +43,8 @@ }; $: messages = createMessagesList(history, history.currentId); + $: canClone = + $sessionUser && ($sessionUser.role === 'admin' || ($sessionUser.permissions?.chat?.import ?? true)); $: if ($page.params.id) { (async () => { @@ -60,10 +62,16 @@ ////////////////////////// const loadSharedChat = async () => { - const userSettings = await getUserSettings(localStorage.token).catch((error) => { - console.error(error); - return null; - }); + const token = localStorage.token ?? ''; + const shareId = $page.params.id; + if (!shareId) return null; + + const userSettings = token + ? await getUserSettings(token).catch((error) => { + console.error(error); + return null; + }) + : null; if (userSettings) { settings.set(userSettings.ui); @@ -80,22 +88,31 @@ } await models.set( - await getModels( - localStorage.token, - $config?.features?.enable_direct_connections && ($settings?.directConnections ?? null) - ) + token + ? await getModels( + token, + $config?.features?.enable_direct_connections + ? ($settings?.directConnections ?? null) + : null + ).catch((error) => { + console.error(error); + return []; + }) + : [] ); - await chatId.set($page.params.id); - chat = await getChatByShareId(localStorage.token, $chatId).catch(async (error) => { + await chatId.set(shareId); + chat = await getChatByShareId(token, shareId).catch(async (error) => { await goto('/'); return null; }); if (chat) { - user = await getUserInfoById(localStorage.token, chat.user_id).catch((error) => { - console.error(error); - return null; - }); + user = token + ? await getUserInfoById(token, chat.user_id).catch((error) => { + console.error(error); + return null; + }) + : null; const chatContent = chat.chat; @@ -128,7 +145,7 @@ }; const cloneSharedChat = async () => { - if (!($sessionUser?.role === 'admin' || ($sessionUser?.permissions?.chat?.import ?? true))) { + if (!canClone) { toast.error($i18n.t('Access prohibited')); return; } @@ -152,6 +169,7 @@ ? `${title.length > 30 ? `${title.slice(0, 30)}...` : title} / ${$WEBUI_NAME}` : `${$WEBUI_NAME}`} + {#if loaded} @@ -202,7 +220,7 @@
- {#if $sessionUser?.role === 'admin' || ($sessionUser?.permissions?.chat?.import ?? true)} + {#if canClone}