diff --git a/web/app/(shareLayout)/components/authenticated-layout.tsx b/web/app/(shareLayout)/components/authenticated-layout.tsx
index 7fe55c3060..9fbda545ff 100644
--- a/web/app/(shareLayout)/components/authenticated-layout.tsx
+++ b/web/app/(shareLayout)/components/authenticated-layout.tsx
@@ -1,25 +1,81 @@
'use client'
+import AppUnavailable from '@/app/components/base/app-unavailable'
import Loading from '@/app/components/base/loading'
+import { removeAccessToken } from '@/app/components/share/utils'
import { useWebAppStore } from '@/context/web-app-context'
-import React, { useEffect, useState } from 'react'
+import { useGetUserCanAccessApp } from '@/service/access-control'
+import { useGetWebAppInfo, useGetWebAppMeta, useGetWebAppParams } from '@/service/use-share'
+import { usePathname, useRouter, useSearchParams } from 'next/navigation'
+import React, { useCallback, useEffect } from 'react'
+import { useTranslation } from 'react-i18next'
const AuthenticatedLayout = ({ children }: { children: React.ReactNode }) => {
+ const { t } = useTranslation()
const shareCode = useWebAppStore(s => s.shareCode)
const webAppAccessMode = useWebAppStore(s => s.webAppAccessMode)
- const [isLoading, setIsLoading] = useState(true)
+ const updateAppInfo = useWebAppStore(s => s.updateAppInfo)
+ const updateAppParams = useWebAppStore(s => s.updateAppParams)
+ const updateWebAppMeta = useWebAppStore(s => s.updateWebAppMeta)
+ const updateUserCanAccessApp = useWebAppStore(s => s.updateUserCanAccessApp)
+ const { isFetching: isFetchingAppParams, data: appParams, error: appParamsError } = useGetWebAppParams()
+ const { isFetching: isFetchingAppInfo, data: appInfo, error: appInfoError } = useGetWebAppInfo()
+ const { isFetching: isFetchingAppMeta, data: appMeta, error: appMetaError } = useGetWebAppMeta()
+ const { isFetching: isFetchingUserCanAccessApp, data: userCanAccessApp, error: useCanAccessAppError } = useGetUserCanAccessApp({ appId: appInfo?.app_id, isInstalledApp: false })
+
useEffect(() => {
- (async () => {
- try {
- setIsLoading(true)
- }
- catch (error) { console.error(error) }
- finally {
- setIsLoading(false)
- }
- })()
- }, [webAppAccessMode, shareCode])
- if (isLoading) {
+ if (appInfo)
+ updateAppInfo(appInfo)
+ if (appParams)
+ updateAppParams(appParams)
+ if (appMeta)
+ updateWebAppMeta(appMeta)
+ updateUserCanAccessApp(Boolean(userCanAccessApp && userCanAccessApp?.result))
+ }, [appInfo, appMeta, appParams, updateAppInfo, updateAppParams, updateUserCanAccessApp, updateWebAppMeta, userCanAccessApp])
+
+ const router = useRouter()
+ const pathname = usePathname()
+ const searchParams = useSearchParams()
+ 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 (appInfoError) {
+ return
+ }
+ if (appParamsError) {
+ return
+ }
+ if (appMetaError) {
+ return
+ }
+ if (useCanAccessAppError) {
+ return
+ }
+ if (userCanAccessApp && !userCanAccessApp.result) {
+ return
+
+
{t('common.userProfile.logout')}
+
+ }
+ if (isFetchingAppInfo || isFetchingAppParams || isFetchingAppMeta || isFetchingUserCanAccessApp) {
return
diff --git a/web/app/components/base/chat/types.ts b/web/app/components/base/chat/types.ts
index 91f9bc976b..c463879a53 100644
--- a/web/app/components/base/chat/types.ts
+++ b/web/app/components/base/chat/types.ts
@@ -49,6 +49,16 @@ export type ChatConfig = Omit & {
questionEditEnable?: boolean
supportFeedback?: boolean
supportCitationHitInfo?: boolean
+ system_parameters: {
+ audio_file_size_limit: number
+ file_size_limit: number
+ image_file_size_limit: number
+ video_file_size_limit: number
+ workflow_file_upload_limit: number
+ }
+ more_like_this: {
+ enabled: boolean
+ }
}
export type WorkflowProcess = {
diff --git a/web/app/components/share/text-generation/index.tsx b/web/app/components/share/text-generation/index.tsx
index 6397b57785..9b2d2d38a6 100644
--- a/web/app/components/share/text-generation/index.tsx
+++ b/web/app/components/share/text-generation/index.tsx
@@ -7,16 +7,14 @@ import {
RiErrorWarningFill,
} from '@remixicon/react'
import { useBoolean } from 'ahooks'
-import { usePathname, useRouter, useSearchParams } from 'next/navigation'
+import { useSearchParams } from 'next/navigation'
import TabHeader from '../../base/tab-header'
-import { removeAccessToken } from '../utils'
import MenuDropdown from './menu-dropdown'
import RunBatch from './run-batch'
import ResDownload from './run-batch/res-download'
-import AppUnavailable from '../../base/app-unavailable'
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
import RunOnce from '@/app/components/share/text-generation/run-once'
-import { fetchSavedMessage as doFetchSavedMessage, fetchAppInfo, fetchAppParams, removeMessage, saveMessage } from '@/service/share'
+import { fetchSavedMessage as doFetchSavedMessage, removeMessage, saveMessage } from '@/service/share'
import type { SiteInfo } from '@/models/share'
import type {
MoreLikeThisConfig,
@@ -39,10 +37,10 @@ import { Resolution, TransferMethod } from '@/types/app'
import { useAppFavicon } from '@/hooks/use-app-favicon'
import DifyLogo from '@/app/components/base/logo/dify-logo'
import cn from '@/utils/classnames'
-import { useGetAppAccessMode, useGetUserCanAccessApp } from '@/service/access-control'
import { AccessMode } from '@/models/access-control'
import { useGlobalPublicStore } from '@/context/global-public-context'
import useDocumentTitle from '@/hooks/use-document-title'
+import { useWebAppStore } from '@/context/web-app-context'
const GROUP_SIZE = 5 // to avoid RPM(Request per minute) limit. The group task finished then the next group.
enum TaskStatus {
@@ -83,9 +81,6 @@ const TextGeneration: FC = ({
const mode = searchParams.get('mode') || 'create'
const [currentTab, setCurrentTab] = useState(['create', 'batch'].includes(mode) ? mode : 'create')
- const router = useRouter()
- const pathname = usePathname()
-
// Notice this situation isCallBatchAPI but not in batch tab
const [isCallBatchAPI, setIsCallBatchAPI] = useState(false)
const isInBatchTab = currentTab === 'batch'
@@ -103,23 +98,12 @@ const TextGeneration: FC = ({
const [moreLikeThisConfig, setMoreLikeThisConfig] = useState(null)
const [textToSpeechConfig, setTextToSpeechConfig] = useState(null)
- const { isPending: isGettingAccessMode, data: appAccessMode } = useGetAppAccessMode({
- appId,
- isInstalledApp,
- enabled: systemFeatures.webapp_auth.enabled,
- })
- const { isPending: isCheckingPermission, data: userCanAccessResult } = useGetUserCanAccessApp({
- appId,
- isInstalledApp,
- enabled: systemFeatures.webapp_auth.enabled,
- })
-
// save message
const [savedMessages, setSavedMessages] = useState([])
- const fetchSavedMessage = async () => {
+ const fetchSavedMessage = useCallback(async () => {
const res: any = await doFetchSavedMessage(isInstalledApp, installedAppInfo?.id)
setSavedMessages(res.data)
- }
+ }, [isInstalledApp, installedAppInfo?.id])
const handleSaveMessage = async (messageId: string) => {
await saveMessage(messageId, isInstalledApp, installedAppInfo?.id)
notify({ type: 'success', message: t('common.api.saved') })
@@ -375,34 +359,14 @@ const TextGeneration: FC = ({
}
}
- const fetchInitData = async () => {
- // if (!isInstalledApp)
- // await checkOrSetAccessToken()
-
- return Promise.all([
- isInstalledApp
- ? {
- app_id: installedAppInfo?.id,
- site: {
- title: installedAppInfo?.app.name,
- prompt_public: false,
- copyright: '',
- icon: installedAppInfo?.app.icon,
- icon_background: installedAppInfo?.app.icon_background,
- },
- plan: 'basic',
- }
- : fetchAppInfo(),
- fetchAppParams(isInstalledApp, installedAppInfo?.id),
- !isWorkflow
- ? fetchSavedMessage()
- : {},
- ])
- }
-
+ const appData = useWebAppStore(s => s.appInfo)
+ const appParams = useWebAppStore(s => s.appParams)
+ const accessMode = useWebAppStore(s => s.webAppAccessMode)
useEffect(() => {
(async () => {
- const [appData, appParams]: any = await fetchInitData()
+ if (!appData || !appParams)
+ return
+ !isWorkflow && fetchSavedMessage()
const { app_id: appId, site: siteInfo, custom_config } = appData
setAppId(appId)
setSiteInfo(siteInfo as SiteInfo)
@@ -413,11 +377,11 @@ const TextGeneration: FC = ({
setVisionConfig({
// legacy of image upload compatible
...file_upload,
- transfer_methods: file_upload.allowed_file_upload_methods || file_upload.allowed_upload_methods,
+ transfer_methods: file_upload?.allowed_file_upload_methods || file_upload?.allowed_upload_methods,
// legacy of image upload compatible
- image_file_size_limit: appParams?.system_parameters?.image_file_size_limit,
+ image_file_size_limit: appParams?.system_parameters.image_file_size_limit,
fileUploadConfig: appParams?.system_parameters,
- })
+ } as any)
const prompt_variables = userInputsFormToPromptVariables(user_input_form)
setPromptConfig({
prompt_template: '', // placeholder for future
@@ -426,7 +390,7 @@ const TextGeneration: FC = ({
setMoreLikeThisConfig(more_like_this)
setTextToSpeechConfig(text_to_speech)
})()
- }, [])
+ }, [appData, appParams, fetchSavedMessage, isWorkflow])
// Can Use metadata(https://beta.nextjs.org/docs/api-reference/metadata) to set title. But it only works in server side client.
useDocumentTitle(siteInfo?.title || t('share.generation.title'))
@@ -528,32 +492,12 @@ const TextGeneration: FC = ({
)
- 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))) {
+ if (!appId || !siteInfo || !promptConfig) {
return (
)
}
- if (systemFeatures.webapp_auth.enabled && !userCanAccessResult?.result) {
- return
-
- {!isInstalledApp &&
{t('common.userProfile.logout')}}
-
- }
-
return (
= ({
imageUrl={siteInfo.icon_url}
/>
{siteInfo.title}
-
+
{siteInfo.description && (
{siteInfo.description}
diff --git a/web/context/web-app-context.tsx b/web/context/web-app-context.tsx
index 28a3312b0d..55f95e4811 100644
--- a/web/context/web-app-context.tsx
+++ b/web/context/web-app-context.tsx
@@ -1,9 +1,10 @@
'use client'
+import type { ChatConfig } from '@/app/components/base/chat/types'
import Loading from '@/app/components/base/loading'
import { AccessMode } from '@/models/access-control'
-import { useAppAccessModeByCode } from '@/service/use-share'
-import type { App } from '@/types/app'
+import type { AppData, AppMeta } from '@/models/share'
+import { useGetWebAppAccessModeByCode } from '@/service/use-share'
import { usePathname, useSearchParams } from 'next/navigation'
import type { FC, PropsWithChildren } from 'react'
import { useEffect } from 'react'
@@ -13,19 +14,31 @@ import { create } from 'zustand'
type WebAppStore = {
shareCode: string | null
updateShareCode: (shareCode: string | null) => void
- appInfo: App | null
- updateAppInfo: (appInfo: App | null) => void
+ appInfo: AppData | null
+ updateAppInfo: (appInfo: AppData | null) => void
+ appParams: ChatConfig | null
+ updateAppParams: (appParams: ChatConfig | null) => void
webAppAccessMode: AccessMode
updateWebAppAccessMode: (accessMode: AccessMode) => void
+ appMeta: AppMeta | null
+ updateWebAppMeta: (appMeta: AppMeta | null) => void
+ userCanAccessApp: boolean
+ updateUserCanAccessApp: (canAccess: boolean) => void
}
export const useWebAppStore = create(set => ({
shareCode: null,
updateShareCode: (shareCode: string | null) => set(() => ({ shareCode })),
appInfo: null,
- updateAppInfo: (appInfo: App | null) => set(() => ({ appInfo })),
+ updateAppInfo: (appInfo: AppData | null) => set(() => ({ appInfo })),
+ appParams: null,
+ updateAppParams: (appParams: ChatConfig | null) => set(() => ({ appParams })),
webAppAccessMode: AccessMode.SPECIFIC_GROUPS_MEMBERS,
updateWebAppAccessMode: (accessMode: AccessMode) => set(() => ({ webAppAccessMode: accessMode })),
+ appMeta: null,
+ updateWebAppMeta: (appMeta: AppMeta | null) => set(() => ({ appMeta })),
+ userCanAccessApp: false,
+ updateUserCanAccessApp: (canAccess: boolean) => set(() => ({ userCanAccessApp: canAccess })),
}))
const getShareCodeFromRedirectUrl = (redirectUrl: string | null): string | null => {
@@ -55,7 +68,7 @@ const WebAppStoreProvider: FC = ({ children }) => {
setShareCode(newShareCode)
updateShareCode(newShareCode)
}, [pathname, redirectUrlParam, updateShareCode])
- const { isFetching, data: accessModeResult } = useAppAccessModeByCode(shareCode)
+ const { isFetching, data: accessModeResult } = useGetWebAppAccessModeByCode(shareCode)
useEffect(() => {
if (accessModeResult?.accessMode)
updateWebAppAccessMode(accessModeResult.accessMode)
diff --git a/web/models/share.ts b/web/models/share.ts
index 3521365e82..b4fde6d89d 100644
--- a/web/models/share.ts
+++ b/web/models/share.ts
@@ -35,7 +35,7 @@ export type AppMeta = {
export type AppData = {
app_id: string
can_replace_logo?: boolean
- custom_config?: Record
+ custom_config: Record
enable_site?: boolean
end_user_id?: string
site: SiteInfo
diff --git a/web/service/access-control.ts b/web/service/access-control.ts
index 36999bf8f3..07984bd57c 100644
--- a/web/service/access-control.ts
+++ b/web/service/access-control.ts
@@ -3,6 +3,7 @@ import { get, post } from './base'
import { getAppAccessMode, getUserCanAccess } from './share'
import type { AccessControlAccount, AccessControlGroup, AccessMode, Subject } from '@/models/access-control'
import type { App } from '@/types/app'
+import { useGlobalPublicStore } from '@/context/global-public-context'
const NAME_SPACE = 'access-control'
@@ -79,15 +80,18 @@ export const useGetAppAccessMode = ({ appId, isInstalledApp = true, enabled }: {
})
}
-export const useGetUserCanAccessApp = ({ appId, isInstalledApp = true, enabled }: { appId?: string; isInstalledApp?: boolean; enabled: boolean }) => {
+export const useGetUserCanAccessApp = ({ appId, isInstalledApp = true }: { appId?: string; isInstalledApp?: boolean; }) => {
+ const systemFeatures = useGlobalPublicStore(s => s.systemFeatures)
return useQuery({
queryKey: [NAME_SPACE, 'user-can-access-app', appId],
- queryFn: () => getUserCanAccess(appId!, isInstalledApp),
- enabled: !!appId && enabled,
+ queryFn: () => {
+ if (systemFeatures.webapp_auth.enabled)
+ return getUserCanAccess(appId!, isInstalledApp)
+ else
+ return { result: true }
+ },
+ enabled: !!appId,
staleTime: 0,
gcTime: 0,
- initialData: {
- result: !enabled,
- },
})
}
diff --git a/web/service/use-share.ts b/web/service/use-share.ts
index 46181c4878..63f18bf0e0 100644
--- a/web/service/use-share.ts
+++ b/web/service/use-share.ts
@@ -1,11 +1,11 @@
import { useGlobalPublicStore } from '@/context/global-public-context'
import { AccessMode } from '@/models/access-control'
import { useQuery } from '@tanstack/react-query'
-import { getAppAccessModeByAppCode } from './share'
+import { fetchAppInfo, fetchAppMeta, fetchAppParams, getAppAccessModeByAppCode } from './share'
const NAME_SPACE = 'webapp'
-export const useAppAccessModeByCode = (code: string | null) => {
+export const useGetWebAppAccessModeByCode = (code: string | null) => {
const systemFeatures = useGlobalPublicStore(s => s.systemFeatures)
return useQuery({
queryKey: [NAME_SPACE, 'appAccessMode', code],
@@ -23,3 +23,30 @@ export const useAppAccessModeByCode = (code: string | null) => {
enabled: !!code,
})
}
+
+export const useGetWebAppInfo = () => {
+ return useQuery({
+ queryKey: [NAME_SPACE, 'appInfo'],
+ queryFn: () => {
+ return fetchAppInfo()
+ },
+ })
+}
+
+export const useGetWebAppParams = () => {
+ return useQuery({
+ queryKey: [NAME_SPACE, 'appParams'],
+ queryFn: () => {
+ return fetchAppParams(false)
+ },
+ })
+}
+
+export const useGetWebAppMeta = () => {
+ return useQuery({
+ queryKey: [NAME_SPACE, 'appMeta'],
+ queryFn: () => {
+ return fetchAppMeta(false)
+ },
+ })
+}