From 33bb464ce3f5598bf3107a8598d86fef9a4011d7 Mon Sep 17 00:00:00 2001 From: euvre <93761161+euvre@users.noreply.github.com> Date: Mon, 27 Apr 2026 03:27:39 +0000 Subject: [PATCH] fix: skip canvas SSE fetch in chat shared page to eliminate spurious 103 error (#14190) ## What does this PR do? Fixes the `hint : 103 Only owner of canvas authorized for this operation` error that appears when opening a **Chat** shared link (`/chats/share?shared_id=...&from=chat`). ## Root Cause The Chat shared page (`web/src/pages/next-chats/share/index.tsx`) unconditionally calls `useFetchFlowSSE()`, which requests `/api/canvas/getsse/{sharedId}`. This is an Agent Canvas endpoint that validates canvas ownership. When sharing a **Chat** dialog (not an Agent): 1. `sharedId` is a `dialog_id`, not a `canvas_id` 2. The API token's `tenant_id` doesn't match any canvas owner 3. The backend returns `code: 103, message: "Only owner of canvas authorized for this operation."` 4. The global error interceptor in `request.ts` displays it as a notification: `hint : 103 Only owner of canvas authorized for this operation.` ## Changes - **`web/src/hooks/use-agent-request.ts`**: Added an `enabled` parameter to `useFetchFlowSSE` so callers can conditionally skip the query. - **`web/src/pages/next-chats/share/index.tsx`**: Only enable `useFetchFlowSSE` when `from === SharedFrom.Agent`. For Chat shares, the hook is disabled, avoiding the unnecessary canvas API call entirely. ## Related Issue Closes #14115 ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) --------- Signed-off-by: noob --- web/src/hooks/use-agent-request.ts | 3 ++- web/src/locales/zh.ts | 3 ++- web/src/pages/next-chats/share/index.tsx | 8 ++------ .../data-source/data-source-detail-page/index.tsx | 2 +- web/src/pages/user-setting/data-source/hooks.ts | 5 +++-- 5 files changed, 10 insertions(+), 11 deletions(-) diff --git a/web/src/hooks/use-agent-request.ts b/web/src/hooks/use-agent-request.ts index bb7ed7cbc4..b524ccbc31 100644 --- a/web/src/hooks/use-agent-request.ts +++ b/web/src/hooks/use-agent-request.ts @@ -794,7 +794,7 @@ export function useCancelConversation() { return { data, loading, cancelConversation: mutateAsync }; } -export const useFetchSharedAgent = (): { +export const useFetchFlowSSE = (): { data: IFlow; loading: boolean; refetch: () => void; @@ -808,6 +808,7 @@ export const useFetchSharedAgent = (): { } = useQuery({ queryKey: [AgentApiAction.FetchSharedAgent, sharedId], initialData: {} as IFlow, + enabled: !!sharedId, refetchOnReconnect: false, refetchOnMount: false, refetchOnWindowFocus: false, diff --git a/web/src/locales/zh.ts b/web/src/locales/zh.ts index 8043849144..1a49402c2a 100644 --- a/web/src/locales/zh.ts +++ b/web/src/locales/zh.ts @@ -1265,7 +1265,8 @@ General:实体和关系提取提示来自 GitHub - microsoft/graphrag:基于 includeHeadingContentTip: '启用后,标题下的直接内容将作为一个独立的块保留。子块仅保留标题路径。', rootAsHeading: '将首个切片设为 H0 标题', - rootAsHeadingTip: '将首个切片设为全局标题,以确保整个文档层级结构中拥有一致的上下文信息。该功能尤其适用于首段包含关键信息的简历。', + rootAsHeadingTip: + '将首个切片设为全局标题,以确保整个文档层级结构中拥有一致的上下文信息。该功能尤其适用于首段包含关键信息的简历。', hierarchyTip: `构建标题树并生成独立的块,每个块携带其完整的祖先标题路径(例如 第1部分 › 第3章 › 第2节 + 正文)。\n 适用场景:具有独立的、结构性重要章节的文档——如法律条款、法规、合同和技术规范——其中每个块即使没有上下文也能通过其结构位置来识别。`, groupTip: `在选定的标题级别将文档扁平分割,并自动合并相邻的小节以保持内容连续性。不注入父标题路径。\n diff --git a/web/src/pages/next-chats/share/index.tsx b/web/src/pages/next-chats/share/index.tsx index 8a25e07b72..96c44ea463 100644 --- a/web/src/pages/next-chats/share/index.tsx +++ b/web/src/pages/next-chats/share/index.tsx @@ -4,8 +4,7 @@ import MessageItem from '@/components/message-item'; import PdfSheet from '@/components/pdf-drawer'; import { useClickDrawer } from '@/components/pdf-drawer/hooks'; import { useSyncThemeFromParams } from '@/components/theme-provider'; -import { MessageType, SharedFrom } from '@/constants/chat'; -import { useFetchSharedAgent } from '@/hooks/use-agent-request'; +import { MessageType } from '@/constants/chat'; import { useFetchExternalChatInfo } from '@/hooks/use-chat-request'; import i18n, { changeLanguageAsync } from '@/locales/config'; import { buildMessageUuidWithRole } from '@/utils/chat'; @@ -20,7 +19,6 @@ import { buildMessageItemReference } from '../utils'; const ChatContainer = () => { const { sharedId: conversationId, - from, locale, theme, visibleAvatar, @@ -44,15 +42,13 @@ const ChatContainer = () => { const sendDisabled = useSendButtonDisabled(value); const { data: chatInfo } = useFetchExternalChatInfo(); - const { data: flowData } = useFetchSharedAgent(); React.useEffect(() => { if (locale && i18n.language !== locale) { changeLanguageAsync(locale); } }, [locale, visibleAvatar]); - const avatarDialogSrc = - from === SharedFrom.Agent ? flowData?.avatar : chatInfo.avatar; + const avatarDialogSrc = chatInfo.avatar; if (!conversationId) { return
empty
; diff --git a/web/src/pages/user-setting/data-source/data-source-detail-page/index.tsx b/web/src/pages/user-setting/data-source/data-source-detail-page/index.tsx index ee547bcdeb..64f44aff14 100644 --- a/web/src/pages/user-setting/data-source/data-source-detail-page/index.tsx +++ b/web/src/pages/user-setting/data-source/data-source-detail-page/index.tsx @@ -144,7 +144,7 @@ const SourceDetailPage = () => { ]; }, [detail, runSchedule]); - const { addLoading, handleAddOk } = useAddDataSource({isEdit:true}); + const { addLoading, handleAddOk } = useAddDataSource({ isEdit: true }); const onSubmit = useCallback(() => { formRef?.current?.submit(); diff --git a/web/src/pages/user-setting/data-source/hooks.ts b/web/src/pages/user-setting/data-source/hooks.ts index 73744cb5bb..b78aad49b1 100644 --- a/web/src/pages/user-setting/data-source/hooks.ts +++ b/web/src/pages/user-setting/data-source/hooks.ts @@ -3,7 +3,8 @@ import { useSetModalState } from '@/hooks/common-hooks'; import { useGetPaginationWithRouter } from '@/hooks/logic-hooks'; import dataSourceService, { dataSourceRebuild, - dataSourceResume, dataSourceUpdate, + dataSourceResume, + dataSourceUpdate, deleteDataSource, featchDataSourceDetail, getDataSourceLogs, @@ -68,7 +69,7 @@ export const useListDataSource = () => { return { list, categorizedList: updatedDataSourceTemplates, isFetching }; }; -export const useAddDataSource = ({isEdit=false}:{isEdit?:boolean} ) => { +export const useAddDataSource = ({ isEdit = false }: { isEdit?: boolean }) => { const [addSource, setAddSource] = useState( undefined, );