mirror of
https://git.mirrors.martin98.com/https://github.com/langgenius/dify.git
synced 2025-08-18 03:45:55 +08:00
fix: check user access permission when visit at explore page
This commit is contained in:
parent
1126b9d1ec
commit
411a7d346f
@ -17,7 +17,7 @@ const AppUnavailable: FC<IAppUnavailableProps> = ({
|
|||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='flex items-center justify-center w-screen h-screen'>
|
<div className='flex items-center justify-center w-full h-full'>
|
||||||
<h1 className='mr-5 h-[50px] leading-[50px] pr-5 text-[24px] font-medium'
|
<h1 className='mr-5 h-[50px] leading-[50px] pr-5 text-[24px] font-medium'
|
||||||
style={{
|
style={{
|
||||||
borderRight: '1px solid rgba(0,0,0,.3)',
|
borderRight: '1px solid rgba(0,0,0,.3)',
|
||||||
|
@ -15,12 +15,16 @@ import type {
|
|||||||
AppMeta,
|
AppMeta,
|
||||||
ConversationItem,
|
ConversationItem,
|
||||||
} from '@/models/share'
|
} from '@/models/share'
|
||||||
|
import { AccessMode } from '@/models/access-control'
|
||||||
|
|
||||||
export type ChatWithHistoryContextValue = {
|
export type ChatWithHistoryContextValue = {
|
||||||
|
isFromExplore: boolean
|
||||||
appInfoError?: any
|
appInfoError?: any
|
||||||
appInfoLoading?: boolean
|
appInfoLoading?: boolean
|
||||||
appMeta?: AppMeta
|
appMeta?: AppMeta
|
||||||
appData?: AppData
|
appData?: AppData
|
||||||
|
accessMode?: AccessMode
|
||||||
|
userCanAccess?: boolean
|
||||||
appParams?: ChatConfig
|
appParams?: ChatConfig
|
||||||
appChatListDataLoading?: boolean
|
appChatListDataLoading?: boolean
|
||||||
currentConversationId: string
|
currentConversationId: string
|
||||||
@ -52,6 +56,9 @@ export type ChatWithHistoryContextValue = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const ChatWithHistoryContext = createContext<ChatWithHistoryContextValue>({
|
export const ChatWithHistoryContext = createContext<ChatWithHistoryContextValue>({
|
||||||
|
isFromExplore: false,
|
||||||
|
accessMode: AccessMode.SPECIFIC_GROUPS_MEMBERS,
|
||||||
|
userCanAccess: false,
|
||||||
currentConversationId: '',
|
currentConversationId: '',
|
||||||
appPrevChatTree: [],
|
appPrevChatTree: [],
|
||||||
pinnedConversationList: [],
|
pinnedConversationList: [],
|
||||||
@ -59,21 +66,21 @@ export const ChatWithHistoryContext = createContext<ChatWithHistoryContextValue>
|
|||||||
showConfigPanelBeforeChat: false,
|
showConfigPanelBeforeChat: false,
|
||||||
newConversationInputs: {},
|
newConversationInputs: {},
|
||||||
newConversationInputsRef: { current: {} },
|
newConversationInputsRef: { current: {} },
|
||||||
handleNewConversationInputsChange: () => {},
|
handleNewConversationInputsChange: () => { },
|
||||||
inputsForms: [],
|
inputsForms: [],
|
||||||
handleNewConversation: () => {},
|
handleNewConversation: () => { },
|
||||||
handleStartChat: () => {},
|
handleStartChat: () => { },
|
||||||
handleChangeConversation: () => {},
|
handleChangeConversation: () => { },
|
||||||
handlePinConversation: () => {},
|
handlePinConversation: () => { },
|
||||||
handleUnpinConversation: () => {},
|
handleUnpinConversation: () => { },
|
||||||
handleDeleteConversation: () => {},
|
handleDeleteConversation: () => { },
|
||||||
conversationRenaming: false,
|
conversationRenaming: false,
|
||||||
handleRenameConversation: () => {},
|
handleRenameConversation: () => { },
|
||||||
handleNewConversationCompleted: () => {},
|
handleNewConversationCompleted: () => { },
|
||||||
chatShouldReloadKey: '',
|
chatShouldReloadKey: '',
|
||||||
isMobile: false,
|
isMobile: false,
|
||||||
isInstalledApp: false,
|
isInstalledApp: false,
|
||||||
handleFeedback: () => {},
|
handleFeedback: () => { },
|
||||||
currentChatInstanceRef: { current: { handleStop: () => {} } },
|
currentChatInstanceRef: { current: { handleStop: () => { } } },
|
||||||
})
|
})
|
||||||
export const useChatWithHistoryContext = () => useContext(ChatWithHistoryContext)
|
export const useChatWithHistoryContext = () => useContext(ChatWithHistoryContext)
|
||||||
|
@ -42,6 +42,7 @@ import { changeLanguage } from '@/i18n/i18next-config'
|
|||||||
import { useAppFavicon } from '@/hooks/use-app-favicon'
|
import { useAppFavicon } from '@/hooks/use-app-favicon'
|
||||||
import { InputVarType } from '@/app/components/workflow/types'
|
import { InputVarType } from '@/app/components/workflow/types'
|
||||||
import { TransferMethod } from '@/types/app'
|
import { TransferMethod } from '@/types/app'
|
||||||
|
import { useGetAppAccessMode, useGetUserCanAccessApp } from '@/service/access-control'
|
||||||
|
|
||||||
function getFormattedChatList(messages: any[]) {
|
function getFormattedChatList(messages: any[]) {
|
||||||
const newChatList: ChatItem[] = []
|
const newChatList: ChatItem[] = []
|
||||||
@ -72,6 +73,8 @@ function getFormattedChatList(messages: any[]) {
|
|||||||
export const useChatWithHistory = (installedAppInfo?: InstalledApp) => {
|
export const useChatWithHistory = (installedAppInfo?: InstalledApp) => {
|
||||||
const isInstalledApp = useMemo(() => !!installedAppInfo, [installedAppInfo])
|
const isInstalledApp = useMemo(() => !!installedAppInfo, [installedAppInfo])
|
||||||
const { data: appInfo, isLoading: appInfoLoading, error: appInfoError } = useSWR(installedAppInfo ? null : 'appInfo', fetchAppInfo)
|
const { data: appInfo, isLoading: appInfoLoading, error: appInfoError } = useSWR(installedAppInfo ? null : 'appInfo', fetchAppInfo)
|
||||||
|
const { isPending: isGettingAccessMode, data: appAccessMode } = useGetAppAccessMode(installedAppInfo?.app.id || appInfo?.app_id)
|
||||||
|
const { isPending: isCheckingPermission, data: userCanAccessResult } = useGetUserCanAccessApp(installedAppInfo?.app.id || appInfo?.app_id)
|
||||||
|
|
||||||
useAppFavicon({
|
useAppFavicon({
|
||||||
enable: !installedAppInfo,
|
enable: !installedAppInfo,
|
||||||
@ -418,7 +421,9 @@ export const useChatWithHistory = (installedAppInfo?: InstalledApp) => {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
appInfoError,
|
appInfoError,
|
||||||
appInfoLoading,
|
appInfoLoading: appInfoLoading || isGettingAccessMode || isCheckingPermission,
|
||||||
|
accessMode: appAccessMode?.accessMode,
|
||||||
|
userCanAccess: userCanAccessResult?.result,
|
||||||
isInstalledApp,
|
isInstalledApp,
|
||||||
appId,
|
appId,
|
||||||
currentConversationId,
|
currentConversationId,
|
||||||
|
@ -27,6 +27,7 @@ const ChatWithHistory: FC<ChatWithHistoryProps> = ({
|
|||||||
className,
|
className,
|
||||||
}) => {
|
}) => {
|
||||||
const {
|
const {
|
||||||
|
userCanAccess,
|
||||||
appInfoError,
|
appInfoError,
|
||||||
appData,
|
appData,
|
||||||
appInfoLoading,
|
appInfoLoading,
|
||||||
@ -57,6 +58,8 @@ const ChatWithHistory: FC<ChatWithHistoryProps> = ({
|
|||||||
<Loading type='app' />
|
<Loading type='app' />
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
if (!userCanAccess)
|
||||||
|
return <AppUnavailable code={403} unknownReason='no permission.' />
|
||||||
|
|
||||||
if (appInfoError) {
|
if (appInfoError) {
|
||||||
return (
|
return (
|
||||||
@ -102,10 +105,12 @@ const ChatWithHistory: FC<ChatWithHistoryProps> = ({
|
|||||||
export type ChatWithHistoryWrapProps = {
|
export type ChatWithHistoryWrapProps = {
|
||||||
installedAppInfo?: InstalledApp
|
installedAppInfo?: InstalledApp
|
||||||
className?: string
|
className?: string
|
||||||
|
isFromExplore?: boolean
|
||||||
}
|
}
|
||||||
const ChatWithHistoryWrap: FC<ChatWithHistoryWrapProps> = ({
|
const ChatWithHistoryWrap: FC<ChatWithHistoryWrapProps> = ({
|
||||||
installedAppInfo,
|
installedAppInfo,
|
||||||
className,
|
className,
|
||||||
|
isFromExplore,
|
||||||
}) => {
|
}) => {
|
||||||
const media = useBreakpoints()
|
const media = useBreakpoints()
|
||||||
const isMobile = media === MediaType.mobile
|
const isMobile = media === MediaType.mobile
|
||||||
@ -114,6 +119,8 @@ const ChatWithHistoryWrap: FC<ChatWithHistoryWrapProps> = ({
|
|||||||
const {
|
const {
|
||||||
appInfoError,
|
appInfoError,
|
||||||
appInfoLoading,
|
appInfoLoading,
|
||||||
|
accessMode,
|
||||||
|
userCanAccess,
|
||||||
appData,
|
appData,
|
||||||
appParams,
|
appParams,
|
||||||
appMeta,
|
appMeta,
|
||||||
@ -146,9 +153,12 @@ const ChatWithHistoryWrap: FC<ChatWithHistoryWrapProps> = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<ChatWithHistoryContext.Provider value={{
|
<ChatWithHistoryContext.Provider value={{
|
||||||
|
isFromExplore: !!isFromExplore,
|
||||||
appInfoError,
|
appInfoError,
|
||||||
appInfoLoading,
|
appInfoLoading,
|
||||||
appData,
|
appData,
|
||||||
|
accessMode,
|
||||||
|
userCanAccess,
|
||||||
appParams,
|
appParams,
|
||||||
appMeta,
|
appMeta,
|
||||||
appChatListDataLoading,
|
appChatListDataLoading,
|
||||||
@ -187,6 +197,7 @@ const ChatWithHistoryWrap: FC<ChatWithHistoryWrapProps> = ({
|
|||||||
const ChatWithHistoryWrapWithCheckToken: FC<ChatWithHistoryWrapProps> = ({
|
const ChatWithHistoryWrapWithCheckToken: FC<ChatWithHistoryWrapProps> = ({
|
||||||
installedAppInfo,
|
installedAppInfo,
|
||||||
className,
|
className,
|
||||||
|
isFromExplore = false,
|
||||||
}) => {
|
}) => {
|
||||||
const [initialized, setInitialized] = useState(false)
|
const [initialized, setInitialized] = useState(false)
|
||||||
const [appUnavailable, setAppUnavailable] = useState<boolean>(false)
|
const [appUnavailable, setAppUnavailable] = useState<boolean>(false)
|
||||||
@ -222,6 +233,7 @@ const ChatWithHistoryWrapWithCheckToken: FC<ChatWithHistoryWrapProps> = ({
|
|||||||
<ChatWithHistoryWrap
|
<ChatWithHistoryWrap
|
||||||
installedAppInfo={installedAppInfo}
|
installedAppInfo={installedAppInfo}
|
||||||
className={className}
|
className={className}
|
||||||
|
isFromExplore={isFromExplore}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -12,10 +12,13 @@ import type { ConversationItem } from '@/models/share'
|
|||||||
import Confirm from '@/app/components/base/confirm'
|
import Confirm from '@/app/components/base/confirm'
|
||||||
import RenameModal from '@/app/components/base/chat/chat-with-history/sidebar/rename-modal'
|
import RenameModal from '@/app/components/base/chat/chat-with-history/sidebar/rename-modal'
|
||||||
import MenuDropdown from '@/app/components/share/text-generation/menu-dropdown'
|
import MenuDropdown from '@/app/components/share/text-generation/menu-dropdown'
|
||||||
|
import { AccessMode } from '@/models/access-control'
|
||||||
|
|
||||||
const Sidebar = () => {
|
const Sidebar = () => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const {
|
const {
|
||||||
|
isFromExplore,
|
||||||
|
accessMode,
|
||||||
appData,
|
appData,
|
||||||
pinnedConversationList,
|
pinnedConversationList,
|
||||||
conversationList,
|
conversationList,
|
||||||
@ -117,7 +120,7 @@ const Sidebar = () => {
|
|||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
<div className='flex items-center justify-between px-4 pb-4 '>
|
<div className='flex items-center justify-between px-4 pb-4 '>
|
||||||
<MenuDropdown placement='top-start' data={appData?.site} />
|
<MenuDropdown hideLogout={isFromExplore || accessMode === AccessMode.PUBLIC} placement='top-start' data={appData?.site} />
|
||||||
{appData?.site.copyright && (
|
{appData?.site.copyright && (
|
||||||
<div className='text-xs text-gray-400 truncate'>
|
<div className='text-xs text-gray-400 truncate'>
|
||||||
© {(new Date()).getFullYear()} {appData?.site.copyright}
|
© {(new Date()).getFullYear()} {appData?.site.copyright}
|
||||||
|
@ -14,8 +14,12 @@ import type {
|
|||||||
AppMeta,
|
AppMeta,
|
||||||
ConversationItem,
|
ConversationItem,
|
||||||
} from '@/models/share'
|
} from '@/models/share'
|
||||||
|
import { AccessMode } from '@/models/access-control'
|
||||||
|
|
||||||
export type EmbeddedChatbotContextValue = {
|
export type EmbeddedChatbotContextValue = {
|
||||||
|
isFromExplore: boolean
|
||||||
|
accessMode?: AccessMode
|
||||||
|
userCanAccess?: boolean
|
||||||
appInfoError?: any
|
appInfoError?: any
|
||||||
appInfoLoading?: boolean
|
appInfoLoading?: boolean
|
||||||
appMeta?: AppMeta
|
appMeta?: AppMeta
|
||||||
@ -46,6 +50,9 @@ export type EmbeddedChatbotContextValue = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const EmbeddedChatbotContext = createContext<EmbeddedChatbotContextValue>({
|
export const EmbeddedChatbotContext = createContext<EmbeddedChatbotContextValue>({
|
||||||
|
isFromExplore: false,
|
||||||
|
userCanAccess: false,
|
||||||
|
accessMode: AccessMode.SPECIFIC_GROUPS_MEMBERS,
|
||||||
currentConversationId: '',
|
currentConversationId: '',
|
||||||
appPrevChatList: [],
|
appPrevChatList: [],
|
||||||
pinnedConversationList: [],
|
pinnedConversationList: [],
|
||||||
@ -53,16 +60,16 @@ export const EmbeddedChatbotContext = createContext<EmbeddedChatbotContextValue>
|
|||||||
showConfigPanelBeforeChat: false,
|
showConfigPanelBeforeChat: false,
|
||||||
newConversationInputs: {},
|
newConversationInputs: {},
|
||||||
newConversationInputsRef: { current: {} },
|
newConversationInputsRef: { current: {} },
|
||||||
handleNewConversationInputsChange: () => {},
|
handleNewConversationInputsChange: () => { },
|
||||||
inputsForms: [],
|
inputsForms: [],
|
||||||
handleNewConversation: () => {},
|
handleNewConversation: () => { },
|
||||||
handleStartChat: () => {},
|
handleStartChat: () => { },
|
||||||
handleChangeConversation: () => {},
|
handleChangeConversation: () => { },
|
||||||
handleNewConversationCompleted: () => {},
|
handleNewConversationCompleted: () => { },
|
||||||
chatShouldReloadKey: '',
|
chatShouldReloadKey: '',
|
||||||
isMobile: false,
|
isMobile: false,
|
||||||
isInstalledApp: false,
|
isInstalledApp: false,
|
||||||
handleFeedback: () => {},
|
handleFeedback: () => { },
|
||||||
currentChatInstanceRef: { current: { handleStop: () => {} } },
|
currentChatInstanceRef: { current: { handleStop: () => { } } },
|
||||||
})
|
})
|
||||||
export const useEmbeddedChatbotContext = () => useContext(EmbeddedChatbotContext)
|
export const useEmbeddedChatbotContext = () => useContext(EmbeddedChatbotContext)
|
||||||
|
@ -35,6 +35,7 @@ import { changeLanguage } from '@/i18n/i18next-config'
|
|||||||
import { InputVarType } from '@/app/components/workflow/types'
|
import { InputVarType } from '@/app/components/workflow/types'
|
||||||
import { TransferMethod } from '@/types/app'
|
import { TransferMethod } from '@/types/app'
|
||||||
import { addFileInfos, sortAgentSorts } from '@/app/components/tools/utils'
|
import { addFileInfos, sortAgentSorts } from '@/app/components/tools/utils'
|
||||||
|
import { useGetAppAccessMode, useGetUserCanAccessApp } from '@/service/access-control'
|
||||||
|
|
||||||
function getFormattedChatList(messages: any[]) {
|
function getFormattedChatList(messages: any[]) {
|
||||||
const newChatList: ChatItem[] = []
|
const newChatList: ChatItem[] = []
|
||||||
@ -65,6 +66,8 @@ function getFormattedChatList(messages: any[]) {
|
|||||||
export const useEmbeddedChatbot = () => {
|
export const useEmbeddedChatbot = () => {
|
||||||
const isInstalledApp = false
|
const isInstalledApp = false
|
||||||
const { data: appInfo, isLoading: appInfoLoading, error: appInfoError } = useSWR('appInfo', fetchAppInfo)
|
const { data: appInfo, isLoading: appInfoLoading, error: appInfoError } = useSWR('appInfo', fetchAppInfo)
|
||||||
|
const { isPending: isGettingAccessMode, data: appAccessMode } = useGetAppAccessMode(appInfo?.app_id)
|
||||||
|
const { isPending: isCheckingPermission, data: userCanAccessResult } = useGetUserCanAccessApp(appInfo?.app_id)
|
||||||
|
|
||||||
const appData = useMemo(() => {
|
const appData = useMemo(() => {
|
||||||
return appInfo
|
return appInfo
|
||||||
@ -319,7 +322,9 @@ export const useEmbeddedChatbot = () => {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
appInfoError,
|
appInfoError,
|
||||||
appInfoLoading,
|
appInfoLoading: appInfoLoading || isGettingAccessMode || isCheckingPermission,
|
||||||
|
accessMode: appAccessMode?.accessMode,
|
||||||
|
userCanAccess: userCanAccessResult?.result,
|
||||||
isInstalledApp,
|
isInstalledApp,
|
||||||
appId,
|
appId,
|
||||||
currentConversationId,
|
currentConversationId,
|
||||||
|
@ -26,6 +26,8 @@ import Tooltip from '@/app/components/base/tooltip'
|
|||||||
const Chatbot = () => {
|
const Chatbot = () => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const {
|
const {
|
||||||
|
isFromExplore,
|
||||||
|
userCanAccess,
|
||||||
isMobile,
|
isMobile,
|
||||||
appInfoError,
|
appInfoError,
|
||||||
appInfoLoading,
|
appInfoLoading,
|
||||||
@ -59,6 +61,9 @@ const Chatbot = () => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!userCanAccess)
|
||||||
|
return <AppUnavailable code={403} unknownReason='no permission.' />
|
||||||
|
|
||||||
if (appInfoError) {
|
if (appInfoError) {
|
||||||
return (
|
return (
|
||||||
<AppUnavailable />
|
<AppUnavailable />
|
||||||
@ -91,7 +96,7 @@ const Chatbot = () => {
|
|||||||
popupContent={t('share.chat.resetChat')}
|
popupContent={t('share.chat.resetChat')}
|
||||||
>
|
>
|
||||||
<div className='p-1.5 bg-white border-[0.5px] border-gray-100 rounded-lg shadow-md cursor-pointer' onClick={handleNewConversation}>
|
<div className='p-1.5 bg-white border-[0.5px] border-gray-100 rounded-lg shadow-md cursor-pointer' onClick={handleNewConversation}>
|
||||||
<RiLoopLeftLine className="h-4 w-4 text-gray-500"/>
|
<RiLoopLeftLine className="h-4 w-4 text-gray-500" />
|
||||||
</div>
|
</div>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</div>
|
</div>
|
||||||
@ -105,7 +110,11 @@ const Chatbot = () => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const EmbeddedChatbotWrapper = () => {
|
type EmbeddedChatbotProps = {
|
||||||
|
isFromExplore?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
const EmbeddedChatbotWrapper = ({ isFromExplore }: EmbeddedChatbotProps) => {
|
||||||
const media = useBreakpoints()
|
const media = useBreakpoints()
|
||||||
const isMobile = media === MediaType.mobile
|
const isMobile = media === MediaType.mobile
|
||||||
const themeBuilder = useThemeContext()
|
const themeBuilder = useThemeContext()
|
||||||
@ -114,6 +123,8 @@ const EmbeddedChatbotWrapper = () => {
|
|||||||
appInfoError,
|
appInfoError,
|
||||||
appInfoLoading,
|
appInfoLoading,
|
||||||
appData,
|
appData,
|
||||||
|
accessMode,
|
||||||
|
userCanAccess,
|
||||||
appParams,
|
appParams,
|
||||||
appMeta,
|
appMeta,
|
||||||
appChatListDataLoading,
|
appChatListDataLoading,
|
||||||
@ -139,6 +150,9 @@ const EmbeddedChatbotWrapper = () => {
|
|||||||
} = useEmbeddedChatbot()
|
} = useEmbeddedChatbot()
|
||||||
|
|
||||||
return <EmbeddedChatbotContext.Provider value={{
|
return <EmbeddedChatbotContext.Provider value={{
|
||||||
|
isFromExplore: !!isFromExplore,
|
||||||
|
userCanAccess,
|
||||||
|
accessMode,
|
||||||
appInfoError,
|
appInfoError,
|
||||||
appInfoLoading,
|
appInfoLoading,
|
||||||
appData,
|
appData,
|
||||||
@ -171,7 +185,7 @@ const EmbeddedChatbotWrapper = () => {
|
|||||||
</EmbeddedChatbotContext.Provider>
|
</EmbeddedChatbotContext.Provider>
|
||||||
}
|
}
|
||||||
|
|
||||||
const EmbeddedChatbot = () => {
|
const EmbeddedChatbot = ({ isFromExplore = false }: EmbeddedChatbotProps) => {
|
||||||
const [initialized, setInitialized] = useState(false)
|
const [initialized, setInitialized] = useState(false)
|
||||||
const [appUnavailable, setAppUnavailable] = useState<boolean>(false)
|
const [appUnavailable, setAppUnavailable] = useState<boolean>(false)
|
||||||
const [isUnknownReason, setIsUnknownReason] = useState<boolean>(false)
|
const [isUnknownReason, setIsUnknownReason] = useState<boolean>(false)
|
||||||
@ -200,7 +214,7 @@ const EmbeddedChatbot = () => {
|
|||||||
if (appUnavailable)
|
if (appUnavailable)
|
||||||
return <AppUnavailable isUnknownReason={isUnknownReason} />
|
return <AppUnavailable isUnknownReason={isUnknownReason} />
|
||||||
|
|
||||||
return <EmbeddedChatbotWrapper />
|
return <EmbeddedChatbotWrapper isFromExplore={isFromExplore} />
|
||||||
}
|
}
|
||||||
|
|
||||||
export default EmbeddedChatbot
|
export default EmbeddedChatbot
|
||||||
|
@ -26,15 +26,15 @@ const InstalledApp: FC<IInstalledAppProps> = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='h-full py-2 pl-0 pr-2 sm:p-2'>
|
<div className='h-full py-2 pl-0 pr-2 sm:p-2 bg-background-default'>
|
||||||
{installedApp.app.mode !== 'completion' && installedApp.app.mode !== 'workflow' && (
|
{installedApp.app.mode !== 'completion' && installedApp.app.mode !== 'workflow' && (
|
||||||
<ChatWithHistory installedAppInfo={installedApp} className='rounded-2xl shadow-md overflow-hidden' />
|
<ChatWithHistory isFromExplore installedAppInfo={installedApp} className='rounded-2xl shadow-md overflow-hidden' />
|
||||||
)}
|
)}
|
||||||
{installedApp.app.mode === 'completion' && (
|
{installedApp.app.mode === 'completion' && (
|
||||||
<TextGenerationApp isInstalledApp installedAppInfo={installedApp}/>
|
<TextGenerationApp isFromExplore isInstalledApp installedAppInfo={installedApp} />
|
||||||
)}
|
)}
|
||||||
{installedApp.app.mode === 'workflow' && (
|
{installedApp.app.mode === 'workflow' && (
|
||||||
<TextGenerationApp isWorkflow isInstalledApp installedAppInfo={installedApp}/>
|
<TextGenerationApp isFromExplore isWorkflow isInstalledApp installedAppInfo={installedApp} />
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
@ -11,6 +11,7 @@ import { usePathname, useRouter, useSearchParams } from 'next/navigation'
|
|||||||
import TabHeader from '../../base/tab-header'
|
import TabHeader from '../../base/tab-header'
|
||||||
import Button from '../../base/button'
|
import Button from '../../base/button'
|
||||||
import { checkOrSetAccessToken } from '../utils'
|
import { checkOrSetAccessToken } from '../utils'
|
||||||
|
import AppUnavailable from '../../base/app-unavailable'
|
||||||
import s from './style.module.css'
|
import s from './style.module.css'
|
||||||
import RunBatch from './run-batch'
|
import RunBatch from './run-batch'
|
||||||
import ResDownload from './run-batch/res-download'
|
import ResDownload from './run-batch/res-download'
|
||||||
@ -38,6 +39,8 @@ import Toast from '@/app/components/base/toast'
|
|||||||
import type { VisionFile, VisionSettings } from '@/types/app'
|
import type { VisionFile, VisionSettings } from '@/types/app'
|
||||||
import { Resolution, TransferMethod } from '@/types/app'
|
import { Resolution, TransferMethod } from '@/types/app'
|
||||||
import { useAppFavicon } from '@/hooks/use-app-favicon'
|
import { useAppFavicon } from '@/hooks/use-app-favicon'
|
||||||
|
import { useGetAppAccessMode, useGetUserCanAccessApp } from '@/service/access-control'
|
||||||
|
import { AccessMode } from '@/models/access-control'
|
||||||
|
|
||||||
const GROUP_SIZE = 5 // to avoid RPM(Request per minute) limit. The group task finished then the next group.
|
const GROUP_SIZE = 5 // to avoid RPM(Request per minute) limit. The group task finished then the next group.
|
||||||
enum TaskStatus {
|
enum TaskStatus {
|
||||||
@ -61,12 +64,14 @@ export type IMainProps = {
|
|||||||
isInstalledApp?: boolean
|
isInstalledApp?: boolean
|
||||||
installedAppInfo?: InstalledApp
|
installedAppInfo?: InstalledApp
|
||||||
isWorkflow?: boolean
|
isWorkflow?: boolean
|
||||||
|
isFromExplore?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const TextGeneration: FC<IMainProps> = ({
|
const TextGeneration: FC<IMainProps> = ({
|
||||||
isInstalledApp = false,
|
isInstalledApp = false,
|
||||||
installedAppInfo,
|
installedAppInfo,
|
||||||
isWorkflow = false,
|
isWorkflow = false,
|
||||||
|
isFromExplore = false,
|
||||||
}) => {
|
}) => {
|
||||||
const { notify } = Toast
|
const { notify } = Toast
|
||||||
|
|
||||||
@ -107,6 +112,9 @@ const TextGeneration: FC<IMainProps> = ({
|
|||||||
const [moreLikeThisConfig, setMoreLikeThisConfig] = useState<MoreLikeThisConfig | null>(null)
|
const [moreLikeThisConfig, setMoreLikeThisConfig] = useState<MoreLikeThisConfig | null>(null)
|
||||||
const [textToSpeechConfig, setTextToSpeechConfig] = useState<TextToSpeechConfig | null>(null)
|
const [textToSpeechConfig, setTextToSpeechConfig] = useState<TextToSpeechConfig | null>(null)
|
||||||
|
|
||||||
|
const { isPending: isGettingAccessMode, data: appAccessMode } = useGetAppAccessMode(appId)
|
||||||
|
const { isPending: isCheckingPermission, data: userCanAccessResult } = useGetUserCanAccessApp(appId)
|
||||||
|
|
||||||
// save message
|
// save message
|
||||||
const [savedMessages, setSavedMessages] = useState<SavedMessage[]>([])
|
const [savedMessages, setSavedMessages] = useState<SavedMessage[]>([])
|
||||||
const fetchSavedMessage = async () => {
|
const fetchSavedMessage = async () => {
|
||||||
@ -538,12 +546,14 @@ const TextGeneration: FC<IMainProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
||||||
if (!appId || !siteInfo || !promptConfig) {
|
if (!appId || !siteInfo || !promptConfig || isGettingAccessMode || isCheckingPermission) {
|
||||||
return (
|
return (
|
||||||
<div className='flex items-center h-screen'>
|
<div className='flex items-center h-screen'>
|
||||||
<Loading type='app' />
|
<Loading type='app' />
|
||||||
</div>)
|
</div>)
|
||||||
}
|
}
|
||||||
|
if (!userCanAccessResult?.result)
|
||||||
|
return <AppUnavailable code={403} unknownReason='no permission.' />
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@ -571,7 +581,7 @@ const TextGeneration: FC<IMainProps> = ({
|
|||||||
/>
|
/>
|
||||||
<div className='text-lg font-semibold text-gray-800'>{siteInfo.title}</div>
|
<div className='text-lg font-semibold text-gray-800'>{siteInfo.title}</div>
|
||||||
</div>
|
</div>
|
||||||
<MenuDropdown />
|
<MenuDropdown hideLogout={isFromExplore || appAccessMode?.accessMode === AccessMode.PUBLIC} />
|
||||||
</div>
|
</div>
|
||||||
{!isPC && (
|
{!isPC && (
|
||||||
<Button
|
<Button
|
||||||
|
@ -22,11 +22,13 @@ import cn from '@/utils/classnames'
|
|||||||
type Props = {
|
type Props = {
|
||||||
data?: SiteInfo
|
data?: SiteInfo
|
||||||
placement?: Placement
|
placement?: Placement
|
||||||
|
hideLogout?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const MenuDropdown: FC<Props> = ({
|
const MenuDropdown: FC<Props> = ({
|
||||||
data,
|
data,
|
||||||
placement,
|
placement,
|
||||||
|
hideLogout,
|
||||||
}) => {
|
}) => {
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
@ -44,7 +46,7 @@ const MenuDropdown: FC<Props> = ({
|
|||||||
const handleLogout = useCallback(() => {
|
const handleLogout = useCallback(() => {
|
||||||
removeAccessToken()
|
removeAccessToken()
|
||||||
router.replace(`/webapp-signin?redirect_url=${window.location.href}`)
|
router.replace(`/webapp-signin?redirect_url=${window.location.href}`)
|
||||||
}, [])
|
}, [router])
|
||||||
|
|
||||||
const [show, setShow] = useState(false)
|
const [show, setShow] = useState(false)
|
||||||
|
|
||||||
@ -81,13 +83,17 @@ const MenuDropdown: FC<Props> = ({
|
|||||||
}}
|
}}
|
||||||
className='system-md-regular cursor-pointer rounded-lg px-3 py-1.5 text-text-secondary hover:bg-state-base-hover'
|
className='system-md-regular cursor-pointer rounded-lg px-3 py-1.5 text-text-secondary hover:bg-state-base-hover'
|
||||||
>{t('common.userProfile.about')}</div>
|
>{t('common.userProfile.about')}</div>
|
||||||
<Divider />
|
{!hideLogout && (
|
||||||
<div
|
<>
|
||||||
onClick={() => {
|
<Divider />
|
||||||
handleLogout()
|
<div
|
||||||
}}
|
onClick={() => {
|
||||||
className='system-md-regular cursor-pointer rounded-lg px-3 py-1.5 text-text-destructive hover:bg-state-base-hover'
|
handleLogout()
|
||||||
>{t('common.userProfile.logout')}</div>
|
}}
|
||||||
|
className='system-md-regular cursor-pointer rounded-lg px-3 py-1.5 text-text-destructive hover:bg-state-base-hover'
|
||||||
|
>{t('common.userProfile.logout')}</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</PortalToFollowElemContent>
|
</PortalToFollowElemContent>
|
||||||
|
@ -9,7 +9,8 @@ export default function useDocumentTitle(title: string) {
|
|||||||
if (systemFeatures.branding.enabled) {
|
if (systemFeatures.branding.enabled) {
|
||||||
document.title = `${prefix}${systemFeatures.branding.application_title}`
|
document.title = `${prefix}${systemFeatures.branding.application_title}`
|
||||||
const faviconEle = document.querySelector('link[rel*=\'icon\']') as HTMLLinkElement
|
const faviconEle = document.querySelector('link[rel*=\'icon\']') as HTMLLinkElement
|
||||||
faviconEle.href = systemFeatures.branding.favicon
|
if (faviconEle)
|
||||||
|
faviconEle.href = systemFeatures.branding.favicon
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
document.title = `${prefix}Dify`
|
document.title = `${prefix}Dify`
|
||||||
|
@ -63,3 +63,19 @@ export const useUpdateAccessMode = () => {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const useGetAppAccessMode = (appId?: string) => {
|
||||||
|
return useQuery({
|
||||||
|
queryKey: [NAME_SPACE, 'app-access-mode', appId],
|
||||||
|
queryFn: () => get<{ accessMode: AccessMode }>(`/enterprise/webapp/app/access-mode?appId=${appId}`),
|
||||||
|
enabled: !!appId,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useGetUserCanAccessApp = (appId?: string) => {
|
||||||
|
return useQuery({
|
||||||
|
queryKey: [NAME_SPACE, 'user-can-access-app', appId],
|
||||||
|
queryFn: () => get<{ result: boolean }>(`/enterprise/webapp/permission?appId=${appId}`),
|
||||||
|
enabled: !!appId,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user