From 26f291396df23e79d42c7db6b85cee1e4f4bca4f Mon Sep 17 00:00:00 2001 From: NFish Date: Fri, 6 Jun 2025 14:27:25 +0800 Subject: [PATCH] Fix/webapp no permission page 260 (#20730) --- web/app/(shareLayout)/webapp-signin/page.tsx | 6 ++- .../components/app/app-publisher/index.tsx | 6 ++- .../base/chat/chat-with-history/index.tsx | 35 +++++++++++++++-- .../base/chat/embedded-chatbot/index.tsx | 39 +++++++++++++++---- .../share/text-generation/index.tsx | 29 +++++++++++--- web/service/base.ts | 14 ++++--- 6 files changed, 105 insertions(+), 24 deletions(-) diff --git a/web/app/(shareLayout)/webapp-signin/page.tsx b/web/app/(shareLayout)/webapp-signin/page.tsx index 90f081f75f..adc1253ac8 100644 --- a/web/app/(shareLayout)/webapp-signin/page.tsx +++ b/web/app/(shareLayout)/webapp-signin/page.tsx @@ -23,10 +23,12 @@ const WebSSOForm: FC = () => { const redirectUrl = searchParams.get('redirect_url') const tokenFromUrl = searchParams.get('web_sso_token') const message = searchParams.get('message') + const code = searchParams.get('code') const getSigninUrl = useCallback(() => { const params = new URLSearchParams(searchParams) params.delete('message') + params.delete('code') return `/webapp-signin?${params.toString()}` }, [searchParams]) @@ -85,8 +87,8 @@ const WebSSOForm: FC = () => { if (message) { return
- - {t('share.login.backToHome')} + + {code === '403' ? t('common.userProfile.logout') : t('share.login.backToHome')}
} if (!redirectUrl) { diff --git a/web/app/components/app/app-publisher/index.tsx b/web/app/components/app/app-publisher/index.tsx index 1a5a56e40f..7f1dcdf6f8 100644 --- a/web/app/components/app/app-publisher/index.tsx +++ b/web/app/components/app/app-publisher/index.tsx @@ -253,7 +253,7 @@ const AppPublisher = ({ onClick={() => { setShowAppAccessControl(true) }}> -
+
{appDetail?.access_mode === AccessMode.ORGANIZATION && <> @@ -263,7 +263,9 @@ const AppPublisher = ({ {appDetail?.access_mode === AccessMode.SPECIFIC_GROUPS_MEMBERS && <> -

{t('app.accessControlDialog.accessItems.specific')}

+
+ {t('app.accessControlDialog.accessItems.specific')} +
} {appDetail?.access_mode === AccessMode.PUBLIC diff --git a/web/app/components/base/chat/chat-with-history/index.tsx b/web/app/components/base/chat/chat-with-history/index.tsx index 0470389ec3..7c87975394 100644 --- a/web/app/components/base/chat/chat-with-history/index.tsx +++ b/web/app/components/base/chat/chat-with-history/index.tsx @@ -1,9 +1,13 @@ +'use client' import type { FC } from 'react' import { + useCallback, useEffect, useState, } from 'react' import { useAsyncEffect } from 'ahooks' +import { useTranslation } from 'react-i18next' +import { usePathname, useRouter, useSearchParams } from 'next/navigation' import { useThemeContext } from '../embedded-chatbot/theme/theme-context' import { ChatWithHistoryContext, @@ -17,8 +21,9 @@ import ChatWrapper from './chat-wrapper' import type { InstalledApp } from '@/models/explore' import Loading from '@/app/components/base/loading' import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints' -import { checkOrSetAccessToken } from '@/app/components/share/utils' +import { checkOrSetAccessToken, removeAccessToken } from '@/app/components/share/utils' import AppUnavailable from '@/app/components/base/app-unavailable' +import useDocumentTitle from '@/hooks/use-document-title' type ChatWithHistoryProps = { className?: string @@ -37,6 +42,7 @@ const ChatWithHistory: FC = ({ chatShouldReloadKey, isMobile, themeBuilder, + isInstalledApp, } = useChatWithHistoryContext() const chatReady = (!showConfigPanelBeforeChat || !!appPrevChatTree.length) @@ -53,13 +59,36 @@ const ChatWithHistory: FC = ({ } }, [site, customConfig, themeBuilder]) + useDocumentTitle(site?.title || 'Chat') + + const { t } = useTranslation() + const searchParams = useSearchParams() + const router = useRouter() + const pathname = usePathname() + const getSigninUrl = useCallback(() => { + const params = new URLSearchParams(searchParams) + params.delete('message') + params.set('redirect_url', pathname) + return `/webapp-signin?${params.toString()}` + }, [searchParams, pathname]) + + const backToHome = useCallback(() => { + removeAccessToken() + const url = getSigninUrl() + router.replace(url) + }, [getSigninUrl, router]) + if (appInfoLoading) { return ( ) } - if (!userCanAccess) - return + if (!userCanAccess) { + return
+ + {!isInstalledApp && {t('common.userProfile.logout')}} +
+ } if (appInfoError) { return ( diff --git a/web/app/components/base/chat/embedded-chatbot/index.tsx b/web/app/components/base/chat/embedded-chatbot/index.tsx index 201f38a7f1..431a7a7ee2 100644 --- a/web/app/components/base/chat/embedded-chatbot/index.tsx +++ b/web/app/components/base/chat/embedded-chatbot/index.tsx @@ -1,10 +1,14 @@ +'use client' import { + useCallback, useEffect, useState, } from 'react' import { useAsyncEffect } from 'ahooks' import { useTranslation } from 'react-i18next' import { RiLoopLeftLine } from '@remixicon/react' +import { usePathname, useRouter, useSearchParams } from 'next/navigation' +import Tooltip from '../../tooltip' import { EmbeddedChatbotContext, useEmbeddedChatbotContext, @@ -12,8 +16,7 @@ import { import { useEmbeddedChatbot } from './hooks' import { isDify } from './utils' import { useThemeContext } from './theme/theme-context' -import cn from '@/utils/classnames' -import { checkOrSetAccessToken } from '@/app/components/share/utils' +import { checkOrSetAccessToken, removeAccessToken } from '@/app/components/share/utils' import AppUnavailable from '@/app/components/base/app-unavailable' import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints' import Loading from '@/app/components/base/loading' @@ -21,7 +24,8 @@ import LogoHeader from '@/app/components/base/logo/logo-embedded-chat-header' import Header from '@/app/components/base/chat/embedded-chatbot/header' import ConfigPanel from '@/app/components/base/chat/embedded-chatbot/config-panel' import ChatWrapper from '@/app/components/base/chat/embedded-chatbot/chat-wrapper' -import Tooltip from '@/app/components/base/tooltip' +import cn from '@/utils/classnames' +import useDocumentTitle from '@/hooks/use-document-title' const Chatbot = () => { const { t } = useTranslation() @@ -36,6 +40,7 @@ const Chatbot = () => { appChatListDataLoading, handleNewConversation, themeBuilder, + isInstalledApp, } = useEmbeddedChatbotContext() const chatReady = (!showConfigPanelBeforeChat || !!appPrevChatList.length) @@ -54,14 +59,36 @@ const Chatbot = () => { } }, [site, customConfig, themeBuilder]) + useDocumentTitle(site?.title || 'Chat') + + const searchParams = useSearchParams() + const router = useRouter() + const pathname = usePathname() + const getSigninUrl = useCallback(() => { + const params = new URLSearchParams(searchParams) + params.delete('message') + params.set('redirect_url', pathname) + return `/webapp-signin?${params.toString()}` + }, [searchParams, pathname]) + + const backToHome = useCallback(() => { + removeAccessToken() + const url = getSigninUrl() + router.replace(url) + }, [getSigninUrl, router]) + if (appInfoLoading) { return ( ) } - if (!userCanAccess) - return + if (!userCanAccess) { + return
+ + {!isInstalledApp && {t('common.userProfile.logout')}} +
+ } if (appInfoError) { return ( @@ -118,7 +145,6 @@ const EmbeddedChatbotWrapper = () => { appInfoError, appInfoLoading, appData, - accessMode, userCanAccess, appParams, appMeta, @@ -146,7 +172,6 @@ const EmbeddedChatbotWrapper = () => { return = ({ const { isPending: isGettingAccessMode, data: appAccessMode } = useGetAppAccessMode({ appId, isInstalledApp }) const { isPending: isCheckingPermission, data: userCanAccessResult } = useGetUserCanAccessApp({ appId, isInstalledApp }) + const systemFeatures = useGlobalPublicStore(s => s.systemFeatures) // save message const [savedMessages, setSavedMessages] = useState([]) const fetchSavedMessage = async () => { @@ -544,14 +546,31 @@ const TextGeneration: FC = ({
) - if (!appId || !siteInfo || !promptConfig || isGettingAccessMode || isCheckingPermission) { + const getSigninUrl = useCallback(() => { + const params = new URLSearchParams(searchParams) + params.delete('message') + params.set('redirect_url', pathname) + return `/webapp-signin?${params.toString()}` + }, [searchParams, pathname]) + + const backToHome = useCallback(() => { + removeAccessToken() + const url = getSigninUrl() + router.replace(url) + }, [getSigninUrl, router]) + + if (!appId || !siteInfo || !promptConfig || (systemFeatures.webapp_auth.enabled && (isGettingAccessMode || isCheckingPermission))) { return (
) } - if (!userCanAccessResult?.result) - return + if (systemFeatures.webapp_auth.enabled && !userCanAccessResult?.result) { + return
+ + {!isInstalledApp && {t('common.userProfile.logout')}} +
+ } return ( <> diff --git a/web/service/base.ts b/web/service/base.ts index cf025b22db..79c7439b21 100644 --- a/web/service/base.ts +++ b/web/service/base.ts @@ -122,12 +122,13 @@ function unicodeToChar(text: string) { }) } -function requiredWebSSOLogin(message?: string) { - removeAccessToken() +function requiredWebSSOLogin(message?: string, code?: number) { const params = new URLSearchParams() params.append('redirect_url', globalThis.location.pathname) if (message) params.append('message', message) + if (code) + params.append('code', String(code)) globalThis.location.href = `/webapp-signin?${params.toString()}` } @@ -518,10 +519,12 @@ export const ssePost = ( res.json().then((data: any) => { if (isPublicAPI) { if (data.code === 'web_app_access_denied') - requiredWebSSOLogin(data.message) + requiredWebSSOLogin(data.message, 403) - if (data.code === 'web_sso_auth_required') + if (data.code === 'web_sso_auth_required') { + removeAccessToken() requiredWebSSOLogin() + } if (data.code === 'unauthorized') { removeAccessToken() @@ -575,10 +578,11 @@ export const request = async(url: string, options = {}, otherOptions?: IOther const { code, message } = errRespData // webapp sso if (code === 'web_app_access_denied') { - requiredWebSSOLogin(message) + requiredWebSSOLogin(message, 403) return Promise.reject(err) } if (code === 'web_sso_auth_required') { + removeAccessToken() requiredWebSSOLogin() return Promise.reject(err) }