mirror of
https://github.com/langgenius/dify.git
synced 2026-05-04 17:38:04 +08:00
Compare commits
3 Commits
1-24-kubb-
...
refactor/s
| Author | SHA1 | Date | |
|---|---|---|---|
| 915a6cc090 | |||
| c63a73ef83 | |||
| 3216b67bfa |
@ -107,10 +107,11 @@ class AnnotationReplyActionApi(Resource):
|
||||
def post(self, app_id, action: Literal["enable", "disable"]):
|
||||
app_id = str(app_id)
|
||||
args = AnnotationReplyPayload.model_validate(console_ns.payload)
|
||||
if action == "enable":
|
||||
result = AppAnnotationService.enable_app_annotation(args.model_dump(), app_id)
|
||||
elif action == "disable":
|
||||
result = AppAnnotationService.disable_app_annotation(app_id)
|
||||
match action:
|
||||
case "enable":
|
||||
result = AppAnnotationService.enable_app_annotation(args.model_dump(), app_id)
|
||||
case "disable":
|
||||
result = AppAnnotationService.disable_app_annotation(app_id)
|
||||
return result, 200
|
||||
|
||||
|
||||
|
||||
@ -155,43 +155,43 @@ class OAuthServerUserTokenApi(Resource):
|
||||
grant_type = OAuthGrantType(payload.grant_type)
|
||||
except ValueError:
|
||||
raise BadRequest("invalid grant_type")
|
||||
match grant_type:
|
||||
case OAuthGrantType.AUTHORIZATION_CODE:
|
||||
if not payload.code:
|
||||
raise BadRequest("code is required")
|
||||
|
||||
if grant_type == OAuthGrantType.AUTHORIZATION_CODE:
|
||||
if not payload.code:
|
||||
raise BadRequest("code is required")
|
||||
if payload.client_secret != oauth_provider_app.client_secret:
|
||||
raise BadRequest("client_secret is invalid")
|
||||
|
||||
if payload.client_secret != oauth_provider_app.client_secret:
|
||||
raise BadRequest("client_secret is invalid")
|
||||
if payload.redirect_uri not in oauth_provider_app.redirect_uris:
|
||||
raise BadRequest("redirect_uri is invalid")
|
||||
|
||||
if payload.redirect_uri not in oauth_provider_app.redirect_uris:
|
||||
raise BadRequest("redirect_uri is invalid")
|
||||
access_token, refresh_token = OAuthServerService.sign_oauth_access_token(
|
||||
grant_type, code=payload.code, client_id=oauth_provider_app.client_id
|
||||
)
|
||||
return jsonable_encoder(
|
||||
{
|
||||
"access_token": access_token,
|
||||
"token_type": "Bearer",
|
||||
"expires_in": OAUTH_ACCESS_TOKEN_EXPIRES_IN,
|
||||
"refresh_token": refresh_token,
|
||||
}
|
||||
)
|
||||
case OAuthGrantType.REFRESH_TOKEN:
|
||||
if not payload.refresh_token:
|
||||
raise BadRequest("refresh_token is required")
|
||||
|
||||
access_token, refresh_token = OAuthServerService.sign_oauth_access_token(
|
||||
grant_type, code=payload.code, client_id=oauth_provider_app.client_id
|
||||
)
|
||||
return jsonable_encoder(
|
||||
{
|
||||
"access_token": access_token,
|
||||
"token_type": "Bearer",
|
||||
"expires_in": OAUTH_ACCESS_TOKEN_EXPIRES_IN,
|
||||
"refresh_token": refresh_token,
|
||||
}
|
||||
)
|
||||
elif grant_type == OAuthGrantType.REFRESH_TOKEN:
|
||||
if not payload.refresh_token:
|
||||
raise BadRequest("refresh_token is required")
|
||||
|
||||
access_token, refresh_token = OAuthServerService.sign_oauth_access_token(
|
||||
grant_type, refresh_token=payload.refresh_token, client_id=oauth_provider_app.client_id
|
||||
)
|
||||
return jsonable_encoder(
|
||||
{
|
||||
"access_token": access_token,
|
||||
"token_type": "Bearer",
|
||||
"expires_in": OAUTH_ACCESS_TOKEN_EXPIRES_IN,
|
||||
"refresh_token": refresh_token,
|
||||
}
|
||||
)
|
||||
access_token, refresh_token = OAuthServerService.sign_oauth_access_token(
|
||||
grant_type, refresh_token=payload.refresh_token, client_id=oauth_provider_app.client_id
|
||||
)
|
||||
return jsonable_encoder(
|
||||
{
|
||||
"access_token": access_token,
|
||||
"token_type": "Bearer",
|
||||
"expires_in": OAUTH_ACCESS_TOKEN_EXPIRES_IN,
|
||||
"refresh_token": refresh_token,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@console_ns.route("/oauth/provider/account")
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -1 +0,0 @@
|
||||
{"openapi":"3.0.0","info":{"title":"Dify API (FastOpenAPI PoC)","version":"1.0","description":"FastOpenAPI proof of concept for Dify API"},"paths":{"/console/api/ping":{"get":{"summary":"Health check endpoint for connection testing.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PingResponse"}}}}},"tags":["console"]}}},"components":{"schemas":{"ErrorSchema":{"type":"object","properties":{"error":{"type":"object","properties":{"type":{"type":"string"},"message":{"type":"string"},"status":{"type":"integer"},"details":{"type":"string"}},"required":["type","message","status"]}},"required":["error"]},"PingResponse":{"properties":{"result":{"description":"Health check result","examples":["pong"],"title":"Result","type":"string"}},"required":["result"],"title":"PingResponse","type":"object"}}}}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -26,37 +26,11 @@ vi.mock('@/app/components/base/chat/utils', () => ({
|
||||
getProcessedSystemVariablesFromUrlParams: (...args: any[]) => mockGetProcessedSystemVariablesFromUrlParams(...args),
|
||||
}))
|
||||
|
||||
// Use vi.hoisted to define mock state before vi.mock hoisting
|
||||
const { mockGlobalStoreState } = vi.hoisted(() => ({
|
||||
mockGlobalStoreState: {
|
||||
isGlobalPending: false,
|
||||
setIsGlobalPending: vi.fn(),
|
||||
systemFeatures: {},
|
||||
setSystemFeatures: vi.fn(),
|
||||
},
|
||||
vi.mock('@/context/global-public-context', () => ({
|
||||
useSystemFeatures: vi.fn(() => ({})),
|
||||
useIsSystemFeaturesPending: () => false,
|
||||
}))
|
||||
|
||||
vi.mock('@/context/global-public-context', () => {
|
||||
const useGlobalPublicStore = Object.assign(
|
||||
(selector?: (state: typeof mockGlobalStoreState) => any) =>
|
||||
selector ? selector(mockGlobalStoreState) : mockGlobalStoreState,
|
||||
{
|
||||
setState: (updater: any) => {
|
||||
if (typeof updater === 'function')
|
||||
Object.assign(mockGlobalStoreState, updater(mockGlobalStoreState) ?? {})
|
||||
|
||||
else
|
||||
Object.assign(mockGlobalStoreState, updater)
|
||||
},
|
||||
__mockState: mockGlobalStoreState,
|
||||
},
|
||||
)
|
||||
return {
|
||||
useGlobalPublicStore,
|
||||
useIsSystemFeaturesPending: () => false,
|
||||
}
|
||||
})
|
||||
|
||||
const TestConsumer = () => {
|
||||
const embeddedUserId = useWebAppStore(state => state.embeddedUserId)
|
||||
const embeddedConversationId = useWebAppStore(state => state.embeddedConversationId)
|
||||
@ -91,7 +65,6 @@ const initialWebAppStore = (() => {
|
||||
})()
|
||||
|
||||
beforeEach(() => {
|
||||
mockGlobalStoreState.isGlobalPending = false
|
||||
mockGetProcessedSystemVariablesFromUrlParams.mockReset()
|
||||
useWebAppStore.setState(initialWebAppStore, true)
|
||||
})
|
||||
|
||||
39
web/app/(commonLayout)/layout-client.tsx
Normal file
39
web/app/(commonLayout)/layout-client.tsx
Normal file
@ -0,0 +1,39 @@
|
||||
'use client'
|
||||
|
||||
import type { ReactNode } from 'react'
|
||||
import { AppInitializer } from '@/app/components/app-initializer'
|
||||
import AmplitudeProvider from '@/app/components/base/amplitude'
|
||||
import GotoAnything from '@/app/components/goto-anything'
|
||||
import Header from '@/app/components/header'
|
||||
import HeaderWrapper from '@/app/components/header/header-wrapper'
|
||||
import ReadmePanel from '@/app/components/plugins/readme-panel'
|
||||
import { AppContextProvider } from '@/context/app-context'
|
||||
import { EventEmitterContextProvider } from '@/context/event-emitter'
|
||||
import { ModalContextProvider } from '@/context/modal-context'
|
||||
import { ProviderContextProvider } from '@/context/provider-context'
|
||||
import PartnerStack from '../components/billing/partner-stack'
|
||||
|
||||
export const CommonLayoutClient = ({ children }: { children: ReactNode }) => {
|
||||
return (
|
||||
<>
|
||||
<AmplitudeProvider />
|
||||
<AppInitializer>
|
||||
<AppContextProvider>
|
||||
<EventEmitterContextProvider>
|
||||
<ProviderContextProvider>
|
||||
<ModalContextProvider>
|
||||
<HeaderWrapper>
|
||||
<Header />
|
||||
</HeaderWrapper>
|
||||
{children}
|
||||
<PartnerStack />
|
||||
<ReadmePanel />
|
||||
<GotoAnything />
|
||||
</ModalContextProvider>
|
||||
</ProviderContextProvider>
|
||||
</EventEmitterContextProvider>
|
||||
</AppContextProvider>
|
||||
</AppInitializer>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@ -1,45 +1,46 @@
|
||||
import type { ReactNode } from 'react'
|
||||
import * as React from 'react'
|
||||
import { AppInitializer } from '@/app/components/app-initializer'
|
||||
import AmplitudeProvider from '@/app/components/base/amplitude'
|
||||
import { dehydrate, HydrationBoundary } from '@tanstack/react-query'
|
||||
import GA, { GaType } from '@/app/components/base/ga'
|
||||
import Zendesk from '@/app/components/base/zendesk'
|
||||
import GotoAnything from '@/app/components/goto-anything'
|
||||
import Header from '@/app/components/header'
|
||||
import HeaderWrapper from '@/app/components/header/header-wrapper'
|
||||
import ReadmePanel from '@/app/components/plugins/readme-panel'
|
||||
import { AppContextProvider } from '@/context/app-context'
|
||||
import { EventEmitterContextProvider } from '@/context/event-emitter'
|
||||
import { ModalContextProvider } from '@/context/modal-context'
|
||||
import { ProviderContextProvider } from '@/context/provider-context'
|
||||
import PartnerStack from '../components/billing/partner-stack'
|
||||
import Splash from '../components/splash'
|
||||
import { getQueryClientServer } from '@/context/query-client-server'
|
||||
import { serverFetchWithAuth } from '@/utils/ssr-fetch'
|
||||
import { CommonLayoutClient } from './layout-client'
|
||||
|
||||
const IS_DEV = process.env.NODE_ENV === 'development'
|
||||
|
||||
async function fetchUserProfileForSSR() {
|
||||
const { data: profile, headers } = await serverFetchWithAuth('/account/profile')
|
||||
return {
|
||||
profile,
|
||||
meta: {
|
||||
currentVersion: headers.get('x-version'),
|
||||
currentEnv: IS_DEV ? 'DEVELOPMENT' : headers.get('x-env'),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export default async function CommonLayout({ children }: { children: ReactNode }) {
|
||||
const queryClient = getQueryClientServer()
|
||||
|
||||
await Promise.all([
|
||||
queryClient.prefetchQuery({
|
||||
queryKey: ['common', 'user-profile'],
|
||||
queryFn: fetchUserProfileForSSR,
|
||||
}),
|
||||
queryClient.prefetchQuery({
|
||||
queryKey: ['common', 'current-workspace'],
|
||||
queryFn: async () => {
|
||||
const { data } = await serverFetchWithAuth('/workspaces/current', 'POST', {})
|
||||
return data
|
||||
},
|
||||
}),
|
||||
])
|
||||
|
||||
const Layout = ({ children }: { children: ReactNode }) => {
|
||||
return (
|
||||
<>
|
||||
<HydrationBoundary state={dehydrate(queryClient)}>
|
||||
<GA gaType={GaType.admin} />
|
||||
<AmplitudeProvider />
|
||||
<AppInitializer>
|
||||
<AppContextProvider>
|
||||
<EventEmitterContextProvider>
|
||||
<ProviderContextProvider>
|
||||
<ModalContextProvider>
|
||||
<HeaderWrapper>
|
||||
<Header />
|
||||
</HeaderWrapper>
|
||||
{children}
|
||||
<PartnerStack />
|
||||
<ReadmePanel />
|
||||
<GotoAnything />
|
||||
<Splash />
|
||||
</ModalContextProvider>
|
||||
</ProviderContextProvider>
|
||||
</EventEmitterContextProvider>
|
||||
</AppContextProvider>
|
||||
<Zendesk />
|
||||
</AppInitializer>
|
||||
</>
|
||||
<CommonLayoutClient>{children}</CommonLayoutClient>
|
||||
<Zendesk />
|
||||
</HydrationBoundary>
|
||||
)
|
||||
}
|
||||
export default Layout
|
||||
|
||||
21
web/app/(commonLayout)/loading.tsx
Normal file
21
web/app/(commonLayout)/loading.tsx
Normal file
@ -0,0 +1,21 @@
|
||||
import '@/app/components/base/loading/style.css'
|
||||
|
||||
export default function CommonLayoutLoading() {
|
||||
return (
|
||||
<div className="flex h-full w-full items-center justify-center">
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" className="spin-animation">
|
||||
<g clipPath="url(#clip0_324_2488)">
|
||||
<path d="M15 0H10C9.44772 0 9 0.447715 9 1V6C9 6.55228 9.44772 7 10 7H15C15.5523 7 16 6.55228 16 6V1C16 0.447715 15.5523 0 15 0Z" fill="#1C64F2" />
|
||||
<path opacity="0.5" d="M15 9H10C9.44772 9 9 9.44772 9 10V15C9 15.5523 9.44772 16 10 16H15C15.5523 16 16 15.5523 16 15V10C16 9.44772 15.5523 9 15 9Z" fill="#1C64F2" />
|
||||
<path opacity="0.1" d="M6 9H1C0.447715 9 0 9.44772 0 10V15C0 15.5523 0.447715 16 1 16H6C6.55228 16 7 15.5523 7 15V10C7 9.44772 6.55228 9 6 9Z" fill="#1C64F2" />
|
||||
<path opacity="0.2" d="M6 0H1C0.447715 0 0 0.447715 0 1V6C0 6.55228 0.447715 7 1 7H6C6.55228 7 7 6.55228 7 6V1C7 0.447715 6.55228 0 6 0Z" fill="#1C64F2" />
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_324_2488">
|
||||
<rect width="16" height="16" fill="white" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@ -1,11 +1,11 @@
|
||||
'use client'
|
||||
import type * as React from 'react'
|
||||
import Header from '@/app/signin/_header'
|
||||
|
||||
import { useGlobalPublicStore } from '@/context/global-public-context'
|
||||
import { useSystemFeatures } from '@/hooks/use-global-public'
|
||||
import { cn } from '@/utils/classnames'
|
||||
|
||||
export default function SignInLayout({ children }: any) {
|
||||
const { systemFeatures } = useGlobalPublicStore()
|
||||
export default function SignInLayout({ children }: { children: React.ReactNode }) {
|
||||
const systemFeatures = useSystemFeatures()
|
||||
return (
|
||||
<>
|
||||
<div className={cn('flex min-h-screen w-full justify-center bg-background-default-burn p-6')}>
|
||||
|
||||
@ -5,12 +5,12 @@ import { useCallback, useEffect } from 'react'
|
||||
import AppUnavailable from '@/app/components/base/app-unavailable'
|
||||
import Loading from '@/app/components/base/loading'
|
||||
import Toast from '@/app/components/base/toast'
|
||||
import { useGlobalPublicStore } from '@/context/global-public-context'
|
||||
import { useSystemFeatures } from '@/hooks/use-global-public'
|
||||
import { fetchWebOAuth2SSOUrl, fetchWebOIDCSSOUrl, fetchWebSAMLSSOUrl } from '@/service/share'
|
||||
import { SSOProtocol } from '@/types/feature'
|
||||
|
||||
const ExternalMemberSSOAuth = () => {
|
||||
const systemFeatures = useGlobalPublicStore(s => s.systemFeatures)
|
||||
const systemFeatures = useSystemFeatures()
|
||||
const searchParams = useSearchParams()
|
||||
const router = useRouter()
|
||||
|
||||
|
||||
@ -2,13 +2,13 @@
|
||||
|
||||
import type { PropsWithChildren } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useGlobalPublicStore } from '@/context/global-public-context'
|
||||
import useDocumentTitle from '@/hooks/use-document-title'
|
||||
import { useSystemFeatures } from '@/hooks/use-global-public'
|
||||
import { cn } from '@/utils/classnames'
|
||||
|
||||
export default function SignInLayout({ children }: PropsWithChildren) {
|
||||
const { t } = useTranslation()
|
||||
const systemFeatures = useGlobalPublicStore(s => s.systemFeatures)
|
||||
const systemFeatures = useSystemFeatures()
|
||||
useDocumentTitle(t('webapp.login', { ns: 'login' }))
|
||||
return (
|
||||
<>
|
||||
|
||||
@ -6,7 +6,7 @@ import { useCallback, useEffect, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Loading from '@/app/components/base/loading'
|
||||
import { IS_CE_EDITION } from '@/config'
|
||||
import { useGlobalPublicStore } from '@/context/global-public-context'
|
||||
import { useSystemFeatures } from '@/hooks/use-global-public'
|
||||
import { LicenseStatus } from '@/types/feature'
|
||||
import { cn } from '@/utils/classnames'
|
||||
import MailAndCodeAuth from './components/mail-and-code-auth'
|
||||
@ -17,7 +17,7 @@ const NormalForm = () => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
const [isLoading, setIsLoading] = useState(true)
|
||||
const { systemFeatures } = useGlobalPublicStore()
|
||||
const systemFeatures = useSystemFeatures()
|
||||
const [authType, updateAuthType] = useState<'code' | 'password'>('password')
|
||||
const [showORLine, setShowORLine] = useState(false)
|
||||
const [allMethodsAreDisabled, setAllMethodsAreDisabled] = useState(false)
|
||||
|
||||
@ -5,8 +5,8 @@ import * as React from 'react'
|
||||
import { useCallback } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import AppUnavailable from '@/app/components/base/app-unavailable'
|
||||
import { useGlobalPublicStore } from '@/context/global-public-context'
|
||||
import { useWebAppStore } from '@/context/web-app-context'
|
||||
import { useSystemFeatures } from '@/hooks/use-global-public'
|
||||
import { AccessMode } from '@/models/access-control'
|
||||
import { webAppLogout } from '@/service/webapp-auth'
|
||||
import ExternalMemberSsoAuth from './components/external-member-sso-auth'
|
||||
@ -14,7 +14,7 @@ import NormalForm from './normalForm'
|
||||
|
||||
const WebSSOForm: FC = () => {
|
||||
const { t } = useTranslation()
|
||||
const systemFeatures = useGlobalPublicStore(s => s.systemFeatures)
|
||||
const systemFeatures = useSystemFeatures()
|
||||
const webAppAccessMode = useWebAppStore(s => s.webAppAccessMode)
|
||||
const searchParams = useSearchParams()
|
||||
const router = useRouter()
|
||||
|
||||
@ -16,8 +16,8 @@ import { ToastContext } from '@/app/components/base/toast'
|
||||
import Collapse from '@/app/components/header/account-setting/collapse'
|
||||
import { IS_CE_EDITION, validPassword } from '@/config'
|
||||
import { useAppContext } from '@/context/app-context'
|
||||
import { useGlobalPublicStore } from '@/context/global-public-context'
|
||||
import { useProviderContext } from '@/context/provider-context'
|
||||
import { useSystemFeatures } from '@/hooks/use-global-public'
|
||||
import { updateUserProfile } from '@/service/common'
|
||||
import { useAppList } from '@/service/use-apps'
|
||||
import DeleteAccount from '../delete-account'
|
||||
@ -34,7 +34,7 @@ const descriptionClassName = `
|
||||
|
||||
export default function AccountPage() {
|
||||
const { t } = useTranslation()
|
||||
const { systemFeatures } = useGlobalPublicStore()
|
||||
const systemFeatures = useSystemFeatures()
|
||||
const { data: appList } = useAppList({ page: 1, limit: 100, name: '' })
|
||||
const apps = appList?.data || []
|
||||
const { mutateUserProfile, userProfile } = useAppContext()
|
||||
|
||||
@ -5,13 +5,13 @@ import { useCallback } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Button from '@/app/components/base/button'
|
||||
import DifyLogo from '@/app/components/base/logo/dify-logo'
|
||||
import { useGlobalPublicStore } from '@/context/global-public-context'
|
||||
import { useSystemFeatures } from '@/hooks/use-global-public'
|
||||
import Avatar from './avatar'
|
||||
|
||||
const Header = () => {
|
||||
const { t } = useTranslation()
|
||||
const router = useRouter()
|
||||
const systemFeatures = useGlobalPublicStore(s => s.systemFeatures)
|
||||
const systemFeatures = useSystemFeatures()
|
||||
|
||||
const goToStudio = useCallback(() => {
|
||||
router.push('/apps')
|
||||
|
||||
@ -3,13 +3,13 @@ import Loading from '@/app/components/base/loading'
|
||||
|
||||
import Header from '@/app/signin/_header'
|
||||
import { AppContextProvider } from '@/context/app-context'
|
||||
import { useGlobalPublicStore } from '@/context/global-public-context'
|
||||
import useDocumentTitle from '@/hooks/use-document-title'
|
||||
import { useSystemFeatures } from '@/hooks/use-global-public'
|
||||
import { useIsLogin } from '@/service/use-common'
|
||||
import { cn } from '@/utils/classnames'
|
||||
|
||||
export default function SignInLayout({ children }: any) {
|
||||
const { systemFeatures } = useGlobalPublicStore()
|
||||
const systemFeatures = useSystemFeatures()
|
||||
useDocumentTitle('')
|
||||
const { isLoading, data: loginData } = useIsLogin()
|
||||
const isLoggedIn = loginData?.logged_in
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
'use client'
|
||||
import * as React from 'react'
|
||||
import { useGlobalPublicStore } from '@/context/global-public-context'
|
||||
import { useSystemFeatures } from '@/hooks/use-global-public'
|
||||
import { cn } from '@/utils/classnames'
|
||||
import Header from '../signin/_header'
|
||||
import ActivateForm from './activateForm'
|
||||
|
||||
const Activate = () => {
|
||||
const { systemFeatures } = useGlobalPublicStore()
|
||||
const systemFeatures = useSystemFeatures()
|
||||
return (
|
||||
<div className={cn('flex min-h-screen w-full justify-center bg-background-default-burn p-6')}>
|
||||
<div className={cn('flex w-full shrink-0 flex-col rounded-2xl border border-effects-highlight bg-background-default-subtle')}>
|
||||
|
||||
@ -2,15 +2,15 @@
|
||||
|
||||
import type { ReactNode } from 'react'
|
||||
import Cookies from 'js-cookie'
|
||||
import { usePathname, useRouter, useSearchParams } from 'next/navigation'
|
||||
import { useRouter, useSearchParams } from 'next/navigation'
|
||||
import { parseAsString, useQueryState } from 'nuqs'
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
import { useEffect, useReducer, useRef } from 'react'
|
||||
import {
|
||||
EDUCATION_VERIFY_URL_SEARCHPARAMS_ACTION,
|
||||
EDUCATION_VERIFYING_LOCALSTORAGE_ITEM,
|
||||
} from '@/app/education-apply/constants'
|
||||
import { useSetupStatusQuery } from '@/hooks/use-global-public'
|
||||
import { sendGAEvent } from '@/utils/gtag'
|
||||
import { fetchSetupStatusWithCache } from '@/utils/setup-status'
|
||||
import { resolvePostLoginRedirect } from '../signin/utils/post-login-redirect'
|
||||
import { trackEvent } from './base/amplitude'
|
||||
|
||||
@ -23,80 +23,68 @@ export const AppInitializer = ({
|
||||
}: AppInitializerProps) => {
|
||||
const router = useRouter()
|
||||
const searchParams = useSearchParams()
|
||||
// Tokens are now stored in cookies, no need to check localStorage
|
||||
const pathname = usePathname()
|
||||
const [init, setInit] = useState(false)
|
||||
const [init, markInit] = useReducer(() => true, false)
|
||||
const { data: setupStatus } = useSetupStatusQuery()
|
||||
const [oauthNewUser, setOauthNewUser] = useQueryState(
|
||||
'oauth_new_user',
|
||||
parseAsString.withOptions({ history: 'replace' }),
|
||||
)
|
||||
|
||||
const isSetupFinished = useCallback(async () => {
|
||||
try {
|
||||
const setUpStatus = await fetchSetupStatusWithCache()
|
||||
return setUpStatus.step === 'finished'
|
||||
}
|
||||
catch (error) {
|
||||
console.error(error)
|
||||
return false
|
||||
}
|
||||
}, [])
|
||||
const oauthTrackedRef = useRef(false)
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
const action = searchParams.get('action')
|
||||
|
||||
if (oauthNewUser === 'true') {
|
||||
let utmInfo = null
|
||||
const utmInfoStr = Cookies.get('utm_info')
|
||||
if (utmInfoStr) {
|
||||
try {
|
||||
utmInfo = JSON.parse(utmInfoStr)
|
||||
}
|
||||
catch (e) {
|
||||
console.error('Failed to parse utm_info cookie:', e)
|
||||
}
|
||||
}
|
||||
|
||||
// Track registration event with UTM params
|
||||
trackEvent(utmInfo ? 'user_registration_success_with_utm' : 'user_registration_success', {
|
||||
method: 'oauth',
|
||||
...utmInfo,
|
||||
})
|
||||
|
||||
sendGAEvent(utmInfo ? 'user_registration_success_with_utm' : 'user_registration_success', {
|
||||
method: 'oauth',
|
||||
...utmInfo,
|
||||
})
|
||||
|
||||
// Clean up: remove utm_info cookie and URL params
|
||||
Cookies.remove('utm_info')
|
||||
setOauthNewUser(null)
|
||||
}
|
||||
|
||||
if (action === EDUCATION_VERIFY_URL_SEARCHPARAMS_ACTION)
|
||||
localStorage.setItem(EDUCATION_VERIFYING_LOCALSTORAGE_ITEM, 'yes')
|
||||
if (oauthNewUser !== 'true' || oauthTrackedRef.current)
|
||||
return
|
||||
oauthTrackedRef.current = true
|
||||
|
||||
let utmInfo = null
|
||||
const utmInfoStr = Cookies.get('utm_info')
|
||||
if (utmInfoStr) {
|
||||
try {
|
||||
const isFinished = await isSetupFinished()
|
||||
if (!isFinished) {
|
||||
router.replace('/install')
|
||||
return
|
||||
}
|
||||
|
||||
const redirectUrl = resolvePostLoginRedirect(searchParams)
|
||||
if (redirectUrl) {
|
||||
location.replace(redirectUrl)
|
||||
return
|
||||
}
|
||||
|
||||
setInit(true)
|
||||
utmInfo = JSON.parse(utmInfoStr)
|
||||
}
|
||||
catch {
|
||||
router.replace('/signin')
|
||||
catch (e) {
|
||||
console.error('Failed to parse utm_info cookie:', e)
|
||||
}
|
||||
})()
|
||||
}, [isSetupFinished, router, pathname, searchParams, oauthNewUser, setOauthNewUser])
|
||||
}
|
||||
|
||||
trackEvent(utmInfo ? 'user_registration_success_with_utm' : 'user_registration_success', {
|
||||
method: 'oauth',
|
||||
...utmInfo,
|
||||
})
|
||||
|
||||
sendGAEvent(utmInfo ? 'user_registration_success_with_utm' : 'user_registration_success', {
|
||||
method: 'oauth',
|
||||
...utmInfo,
|
||||
})
|
||||
|
||||
Cookies.remove('utm_info')
|
||||
// eslint-disable-next-line react-hooks-extra/no-direct-set-state-in-use-effect -- setOauthNewUser is from nuqs useQueryState, not useState
|
||||
setOauthNewUser(null)
|
||||
}, [oauthNewUser, setOauthNewUser])
|
||||
|
||||
useEffect(() => {
|
||||
const action = searchParams.get('action')
|
||||
if (action === EDUCATION_VERIFY_URL_SEARCHPARAMS_ACTION)
|
||||
localStorage.setItem(EDUCATION_VERIFYING_LOCALSTORAGE_ITEM, 'yes')
|
||||
}, [searchParams])
|
||||
|
||||
useEffect(() => {
|
||||
if (!setupStatus)
|
||||
return
|
||||
|
||||
if (setupStatus.step !== 'finished') {
|
||||
router.replace('/install')
|
||||
return
|
||||
}
|
||||
|
||||
const redirectUrl = resolvePostLoginRedirect(searchParams)
|
||||
if (redirectUrl) {
|
||||
location.replace(redirectUrl)
|
||||
return
|
||||
}
|
||||
|
||||
markInit()
|
||||
}, [setupStatus, router, searchParams])
|
||||
|
||||
return init ? children : null
|
||||
}
|
||||
|
||||
@ -5,7 +5,7 @@ import { Description as DialogDescription, DialogTitle } from '@headlessui/react
|
||||
import { RiBuildingLine, RiGlobalLine, RiVerifiedBadgeLine } from '@remixicon/react'
|
||||
import { useCallback, useEffect } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useGlobalPublicStore } from '@/context/global-public-context'
|
||||
import { useSystemFeatures } from '@/hooks/use-global-public'
|
||||
import { AccessMode, SubjectType } from '@/models/access-control'
|
||||
import { useUpdateAccessMode } from '@/service/access-control'
|
||||
import useAccessControlStore from '../../../../context/access-control-store'
|
||||
@ -24,7 +24,7 @@ type AccessControlProps = {
|
||||
export default function AccessControl(props: AccessControlProps) {
|
||||
const { app, onClose, onConfirm } = props
|
||||
const { t } = useTranslation()
|
||||
const systemFeatures = useGlobalPublicStore(s => s.systemFeatures)
|
||||
const systemFeatures = useSystemFeatures()
|
||||
const setAppId = useAccessControlStore(s => s.setAppId)
|
||||
const specificGroups = useAccessControlStore(s => s.specificGroups)
|
||||
const specificMembers = useAccessControlStore(s => s.specificMembers)
|
||||
|
||||
@ -36,9 +36,9 @@ import {
|
||||
import UpgradeBtn from '@/app/components/billing/upgrade-btn'
|
||||
import WorkflowToolConfigureButton from '@/app/components/tools/workflow-tool/configure-button'
|
||||
import { appDefaultIconBackground } from '@/config'
|
||||
import { useGlobalPublicStore } from '@/context/global-public-context'
|
||||
import { useAsyncWindowOpen } from '@/hooks/use-async-window-open'
|
||||
import { useFormatTimeFromNow } from '@/hooks/use-format-time-from-now'
|
||||
import { useSystemFeatures } from '@/hooks/use-global-public'
|
||||
import { AccessMode } from '@/models/access-control'
|
||||
import { useAppWhiteListSubjects, useGetUserCanAccessApp } from '@/service/access-control'
|
||||
import { fetchAppDetailDirect } from '@/service/apps'
|
||||
@ -149,7 +149,7 @@ const AppPublisher = ({
|
||||
|
||||
const appDetail = useAppStore(state => state.appDetail)
|
||||
const setAppDetail = useAppStore(s => s.setAppDetail)
|
||||
const systemFeatures = useGlobalPublicStore(s => s.systemFeatures)
|
||||
const systemFeatures = useSystemFeatures()
|
||||
const { formatTimeFromNow } = useFormatTimeFromNow()
|
||||
const { app_base_url: appBaseURL = '', access_token: accessToken = '' } = appDetail?.site ?? {}
|
||||
|
||||
|
||||
@ -8,7 +8,7 @@ import { useContextSelector } from 'use-context-selector'
|
||||
import AppIcon from '@/app/components/base/app-icon'
|
||||
import Button from '@/app/components/base/button'
|
||||
import AppListContext from '@/context/app-list-context'
|
||||
import { useGlobalPublicStore } from '@/context/global-public-context'
|
||||
import { useSystemFeatures } from '@/hooks/use-global-public'
|
||||
import { cn } from '@/utils/classnames'
|
||||
import { AppTypeIcon, AppTypeLabel } from '../../type-selector'
|
||||
|
||||
@ -25,7 +25,7 @@ const AppCard = ({
|
||||
}: AppCardProps) => {
|
||||
const { t } = useTranslation()
|
||||
const { app: appBasicInfo } = app
|
||||
const { systemFeatures } = useGlobalPublicStore()
|
||||
const systemFeatures = useSystemFeatures()
|
||||
const isTrialApp = app.can_trial && systemFeatures.enable_trial_app
|
||||
const setShowTryAppPanel = useContextSelector(AppListContext, ctx => ctx.setShowTryAppPanel)
|
||||
const showTryAPPPanel = useCallback((appId: string) => {
|
||||
|
||||
@ -31,8 +31,8 @@ import SecretKeyButton from '@/app/components/develop/secret-key/secret-key-butt
|
||||
import Indicator from '@/app/components/header/indicator'
|
||||
import { BlockEnum } from '@/app/components/workflow/types'
|
||||
import { useAppContext } from '@/context/app-context'
|
||||
import { useGlobalPublicStore } from '@/context/global-public-context'
|
||||
import { useDocLink } from '@/context/i18n'
|
||||
import { useSystemFeatures } from '@/hooks/use-global-public'
|
||||
import { AccessMode } from '@/models/access-control'
|
||||
import { useAppWhiteListSubjects } from '@/service/access-control'
|
||||
import { fetchAppDetailDirect } from '@/service/apps'
|
||||
@ -85,7 +85,7 @@ function AppCard({
|
||||
const [showConfirmDelete, setShowConfirmDelete] = useState(false)
|
||||
const [showAccessControl, setShowAccessControl] = useState<boolean>(false)
|
||||
const { t } = useTranslation()
|
||||
const systemFeatures = useGlobalPublicStore(s => s.systemFeatures)
|
||||
const systemFeatures = useSystemFeatures()
|
||||
const { data: appAccessSubjects } = useAppWhiteListSubjects(appDetail?.id, systemFeatures.webapp_auth.enabled && appDetail?.access_mode === AccessMode.SPECIFIC_GROUPS_MEMBERS)
|
||||
|
||||
const OPERATIONS_MAP = useMemo(() => {
|
||||
|
||||
@ -51,11 +51,9 @@ vi.mock('@/context/provider-context', () => ({
|
||||
// Mock global public store - allow dynamic configuration
|
||||
let mockWebappAuthEnabled = false
|
||||
vi.mock('@/context/global-public-context', () => ({
|
||||
useGlobalPublicStore: (selector: (s: any) => any) => selector({
|
||||
systemFeatures: {
|
||||
webapp_auth: { enabled: mockWebappAuthEnabled },
|
||||
branding: { enabled: false },
|
||||
},
|
||||
useSystemFeatures: () => ({
|
||||
webapp_auth: { enabled: mockWebappAuthEnabled },
|
||||
branding: { enabled: false },
|
||||
}),
|
||||
}))
|
||||
|
||||
|
||||
@ -22,9 +22,9 @@ import Toast, { ToastContext } from '@/app/components/base/toast'
|
||||
import Tooltip from '@/app/components/base/tooltip'
|
||||
import { NEED_REFRESH_APP_LIST_KEY } from '@/config'
|
||||
import { useAppContext } from '@/context/app-context'
|
||||
import { useGlobalPublicStore } from '@/context/global-public-context'
|
||||
import { useProviderContext } from '@/context/provider-context'
|
||||
import { useAsyncWindowOpen } from '@/hooks/use-async-window-open'
|
||||
import { useSystemFeatures } from '@/hooks/use-global-public'
|
||||
import { AccessMode } from '@/models/access-control'
|
||||
import { useGetUserCanAccessApp } from '@/service/access-control'
|
||||
import { copyApp, deleteApp, exportAppConfig, updateAppInfo } from '@/service/apps'
|
||||
@ -64,7 +64,7 @@ export type AppCardProps = {
|
||||
const AppCard = ({ app, onRefresh }: AppCardProps) => {
|
||||
const { t } = useTranslation()
|
||||
const { notify } = useContext(ToastContext)
|
||||
const systemFeatures = useGlobalPublicStore(s => s.systemFeatures)
|
||||
const systemFeatures = useSystemFeatures()
|
||||
const { isCurrentWorkspaceEditor } = useAppContext()
|
||||
const { onPlanInfoChanged } = useProviderContext()
|
||||
const { push } = useRouter()
|
||||
|
||||
@ -26,10 +26,8 @@ vi.mock('@/context/app-context', () => ({
|
||||
|
||||
// Mock global public store
|
||||
vi.mock('@/context/global-public-context', () => ({
|
||||
useGlobalPublicStore: () => ({
|
||||
systemFeatures: {
|
||||
branding: { enabled: false },
|
||||
},
|
||||
useSystemFeatures: () => ({
|
||||
branding: { enabled: false },
|
||||
}),
|
||||
}))
|
||||
|
||||
|
||||
@ -24,7 +24,7 @@ import { useStore as useTagStore } from '@/app/components/base/tag-management/st
|
||||
import CheckboxWithLabel from '@/app/components/datasets/create/website/base/checkbox-with-label'
|
||||
import { NEED_REFRESH_APP_LIST_KEY } from '@/config'
|
||||
import { useAppContext } from '@/context/app-context'
|
||||
import { useGlobalPublicStore } from '@/context/global-public-context'
|
||||
import { useSystemFeatures } from '@/hooks/use-global-public'
|
||||
import { CheckModal } from '@/hooks/use-pay'
|
||||
import { useInfiniteAppList } from '@/service/use-apps'
|
||||
import { AppModeEnum } from '@/types/app'
|
||||
@ -61,7 +61,7 @@ const List: FC<Props> = ({
|
||||
controlRefreshList = 0,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const { systemFeatures } = useGlobalPublicStore()
|
||||
const systemFeatures = useSystemFeatures()
|
||||
const router = useRouter()
|
||||
const { isCurrentWorkspaceEditor, isCurrentWorkspaceDatasetOperator, isLoadingCurrentWorkspace } = useAppContext()
|
||||
const showTagManagementModal = useTagStore(s => s.showTagManagementModal)
|
||||
|
||||
@ -17,7 +17,7 @@ import RenameModal from '@/app/components/base/chat/chat-with-history/sidebar/re
|
||||
import Confirm from '@/app/components/base/confirm'
|
||||
import DifyLogo from '@/app/components/base/logo/dify-logo'
|
||||
import MenuDropdown from '@/app/components/share/text-generation/menu-dropdown'
|
||||
import { useGlobalPublicStore } from '@/context/global-public-context'
|
||||
import { useSystemFeatures } from '@/hooks/use-global-public'
|
||||
import { cn } from '@/utils/classnames'
|
||||
import { useChatWithHistoryContext } from '../context'
|
||||
|
||||
@ -47,7 +47,7 @@ const Sidebar = ({ isPanel, panelVisible }: Props) => {
|
||||
isResponding,
|
||||
} = useChatWithHistoryContext()
|
||||
const isSidebarCollapsed = sidebarCollapseState
|
||||
const systemFeatures = useGlobalPublicStore(s => s.systemFeatures)
|
||||
const systemFeatures = useSystemFeatures()
|
||||
const [showConfirm, setShowConfirm] = useState<ConversationItem | null>(null)
|
||||
const [showRename, setShowRename] = useState<ConversationItem | null>(null)
|
||||
|
||||
|
||||
@ -9,7 +9,7 @@ import ViewFormDropdown from '@/app/components/base/chat/embedded-chatbot/inputs
|
||||
import Divider from '@/app/components/base/divider'
|
||||
import DifyLogo from '@/app/components/base/logo/dify-logo'
|
||||
import Tooltip from '@/app/components/base/tooltip'
|
||||
import { useGlobalPublicStore } from '@/context/global-public-context'
|
||||
import { useSystemFeatures } from '@/hooks/use-global-public'
|
||||
import { cn } from '@/utils/classnames'
|
||||
import { isClient } from '@/utils/client'
|
||||
import {
|
||||
@ -45,7 +45,7 @@ const Header: FC<IHeaderProps> = ({
|
||||
const [parentOrigin, setParentOrigin] = useState('')
|
||||
const [showToggleExpandButton, setShowToggleExpandButton] = useState(false)
|
||||
const [expanded, setExpanded] = useState(false)
|
||||
const systemFeatures = useGlobalPublicStore(s => s.systemFeatures)
|
||||
const systemFeatures = useSystemFeatures()
|
||||
|
||||
const handleMessageReceived = useCallback((event: MessageEvent) => {
|
||||
let currentParentOrigin = parentOrigin
|
||||
|
||||
@ -9,9 +9,9 @@ import Header from '@/app/components/base/chat/embedded-chatbot/header'
|
||||
import Loading from '@/app/components/base/loading'
|
||||
import DifyLogo from '@/app/components/base/logo/dify-logo'
|
||||
import LogoHeader from '@/app/components/base/logo/logo-embedded-chat-header'
|
||||
import { useGlobalPublicStore } from '@/context/global-public-context'
|
||||
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
|
||||
import useDocumentTitle from '@/hooks/use-document-title'
|
||||
import { useSystemFeatures } from '@/hooks/use-global-public'
|
||||
import { AppSourceType } from '@/service/share'
|
||||
import { cn } from '@/utils/classnames'
|
||||
import {
|
||||
@ -34,7 +34,7 @@ const Chatbot = () => {
|
||||
themeBuilder,
|
||||
} = useEmbeddedChatbotContext()
|
||||
const { t } = useTranslation()
|
||||
const systemFeatures = useGlobalPublicStore(s => s.systemFeatures)
|
||||
const systemFeatures = useSystemFeatures()
|
||||
|
||||
const customConfig = appData?.custom_config
|
||||
const site = appData?.site
|
||||
|
||||
@ -23,8 +23,8 @@ const usePSInfo = () => {
|
||||
setTrue: setBind,
|
||||
}] = useBoolean(false)
|
||||
const { mutateAsync } = useBindPartnerStackInfo()
|
||||
// Save to top domain. cloud.dify.ai => .dify.ai
|
||||
const domain = globalThis.location.hostname.replace('cloud', '')
|
||||
|
||||
const getDomain = () => globalThis.location?.hostname.replace('cloud', '') ?? ''
|
||||
|
||||
const saveOrUpdate = useCallback(() => {
|
||||
if (!psPartnerKey || !psClickId)
|
||||
@ -37,7 +37,7 @@ const usePSInfo = () => {
|
||||
}), {
|
||||
expires: PARTNER_STACK_CONFIG.saveCookieDays,
|
||||
path: '/',
|
||||
domain,
|
||||
domain: getDomain(),
|
||||
})
|
||||
}, [psPartnerKey, psClickId, isPSChanged])
|
||||
|
||||
@ -56,7 +56,7 @@ const usePSInfo = () => {
|
||||
shouldRemoveCookie = true
|
||||
}
|
||||
if (shouldRemoveCookie)
|
||||
Cookies.remove(PARTNER_STACK_CONFIG.cookieName, { path: '/', domain })
|
||||
Cookies.remove(PARTNER_STACK_CONFIG.cookieName, { path: '/', domain: getDomain() })
|
||||
setBind()
|
||||
}
|
||||
}, [psPartnerKey, psClickId, mutateAsync, hasBind, setBind])
|
||||
|
||||
@ -4,8 +4,8 @@ import { getImageUploadErrorMessage, imageUpload } from '@/app/components/base/i
|
||||
import { useToastContext } from '@/app/components/base/toast'
|
||||
import { Plan } from '@/app/components/billing/type'
|
||||
import { useAppContext } from '@/context/app-context'
|
||||
import { useGlobalPublicStore } from '@/context/global-public-context'
|
||||
import { useProviderContext } from '@/context/provider-context'
|
||||
import { useSystemFeatures } from '@/hooks/use-global-public'
|
||||
import { updateCurrentWorkspace } from '@/service/common'
|
||||
import CustomWebAppBrand from './index'
|
||||
|
||||
@ -22,7 +22,7 @@ vi.mock('@/context/provider-context', () => ({
|
||||
useProviderContext: vi.fn(),
|
||||
}))
|
||||
vi.mock('@/context/global-public-context', () => ({
|
||||
useGlobalPublicStore: vi.fn(),
|
||||
useSystemFeatures: vi.fn(),
|
||||
}))
|
||||
vi.mock('@/app/components/base/image-uploader/utils', () => ({
|
||||
imageUpload: vi.fn(),
|
||||
@ -34,7 +34,7 @@ const mockUseToastContext = vi.mocked(useToastContext)
|
||||
const mockUpdateCurrentWorkspace = vi.mocked(updateCurrentWorkspace)
|
||||
const mockUseAppContext = vi.mocked(useAppContext)
|
||||
const mockUseProviderContext = vi.mocked(useProviderContext)
|
||||
const mockUseGlobalPublicStore = vi.mocked(useGlobalPublicStore)
|
||||
const mockUseSystemFeatures = vi.mocked(useSystemFeatures)
|
||||
const mockImageUpload = vi.mocked(imageUpload)
|
||||
const mockGetImageUploadErrorMessage = vi.mocked(getImageUploadErrorMessage)
|
||||
|
||||
@ -80,7 +80,7 @@ describe('CustomWebAppBrand', () => {
|
||||
workspace_logo: 'https://example.com/workspace-logo.png',
|
||||
},
|
||||
}
|
||||
mockUseGlobalPublicStore.mockImplementation(selector => selector ? selector({ systemFeatures: systemFeaturesState } as any) : { systemFeatures: systemFeaturesState })
|
||||
mockUseSystemFeatures.mockReturnValue(systemFeaturesState as ReturnType<typeof mockUseSystemFeatures>)
|
||||
mockGetImageUploadErrorMessage.mockReturnValue('upload error')
|
||||
})
|
||||
|
||||
|
||||
@ -19,8 +19,8 @@ import Switch from '@/app/components/base/switch'
|
||||
import { useToastContext } from '@/app/components/base/toast'
|
||||
import { Plan } from '@/app/components/billing/type'
|
||||
import { useAppContext } from '@/context/app-context'
|
||||
import { useGlobalPublicStore } from '@/context/global-public-context'
|
||||
import { useProviderContext } from '@/context/provider-context'
|
||||
import { useSystemFeatures } from '@/hooks/use-global-public'
|
||||
import {
|
||||
updateCurrentWorkspace,
|
||||
} from '@/service/common'
|
||||
@ -40,7 +40,7 @@ const CustomWebAppBrand = () => {
|
||||
const [fileId, setFileId] = useState('')
|
||||
const [imgKey, setImgKey] = useState(() => Date.now())
|
||||
const [uploadProgress, setUploadProgress] = useState(0)
|
||||
const systemFeatures = useGlobalPublicStore(s => s.systemFeatures)
|
||||
const systemFeatures = useSystemFeatures()
|
||||
const isSandbox = enableBilling && plan.type === Plan.sandbox
|
||||
const uploading = uploadProgress > 0 && uploadProgress < 100
|
||||
const webappLogo = currentWorkspace.custom_config?.replace_webapp_logo || ''
|
||||
|
||||
@ -25,10 +25,7 @@ vi.mock('@/context/i18n', () => ({
|
||||
}))
|
||||
|
||||
vi.mock('@/context/global-public-context', () => ({
|
||||
useGlobalPublicStore: vi.fn((selector) => {
|
||||
const state = { systemFeatures: { enable_marketplace: true } }
|
||||
return selector(state)
|
||||
}),
|
||||
useSystemFeatures: vi.fn(() => ({ enable_marketplace: true })),
|
||||
}))
|
||||
|
||||
const mockUsePipelineTemplateList = vi.fn()
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { useMemo } from 'react'
|
||||
import { useGlobalPublicStore } from '@/context/global-public-context'
|
||||
import { useLocale } from '@/context/i18n'
|
||||
import { useSystemFeatures } from '@/hooks/use-global-public'
|
||||
import { LanguagesSupported } from '@/i18n-config/language'
|
||||
import { usePipelineTemplateList } from '@/service/use-pipeline'
|
||||
import CreateCard from './create-card'
|
||||
@ -13,7 +13,7 @@ const BuiltInPipelineList = () => {
|
||||
return locale
|
||||
return LanguagesSupported[0]
|
||||
}, [locale])
|
||||
const enableMarketplace = useGlobalPublicStore(s => s.systemFeatures.enable_marketplace)
|
||||
const enableMarketplace = useSystemFeatures().enable_marketplace
|
||||
const { data: pipelineList, isLoading } = usePipelineTemplateList({ type: 'built-in', language }, enableMarketplace)
|
||||
const list = pipelineList?.pipeline_templates || []
|
||||
|
||||
|
||||
@ -34,10 +34,8 @@ vi.mock('@/context/app-context', () => ({
|
||||
|
||||
// Mock global public context
|
||||
vi.mock('@/context/global-public-context', () => ({
|
||||
useGlobalPublicStore: () => ({
|
||||
systemFeatures: {
|
||||
branding: { enabled: false },
|
||||
},
|
||||
useSystemFeatures: () => ({
|
||||
branding: { enabled: false },
|
||||
}),
|
||||
}))
|
||||
|
||||
@ -333,10 +331,8 @@ describe('List', () => {
|
||||
|
||||
it('should not show DatasetFooter when branding is enabled', async () => {
|
||||
vi.doMock('@/context/global-public-context', () => ({
|
||||
useGlobalPublicStore: () => ({
|
||||
systemFeatures: {
|
||||
branding: { enabled: true },
|
||||
},
|
||||
useSystemFeatures: () => ({
|
||||
branding: { enabled: true },
|
||||
}),
|
||||
}))
|
||||
|
||||
|
||||
@ -16,8 +16,8 @@ import { useStore as useTagStore } from '@/app/components/base/tag-management/st
|
||||
import CheckboxWithLabel from '@/app/components/datasets/create/website/base/checkbox-with-label'
|
||||
import { useAppContext, useSelector as useAppContextSelector } from '@/context/app-context'
|
||||
import { useExternalApiPanel } from '@/context/external-api-panel-context'
|
||||
import { useGlobalPublicStore } from '@/context/global-public-context'
|
||||
import useDocumentTitle from '@/hooks/use-document-title'
|
||||
import { useSystemFeatures } from '@/hooks/use-global-public'
|
||||
import { useDatasetApiBaseUrl } from '@/service/knowledge/use-dataset'
|
||||
// Components
|
||||
import ExternalAPIPanel from '../external-api/external-api-panel'
|
||||
@ -27,7 +27,7 @@ import Datasets from './datasets'
|
||||
|
||||
const List = () => {
|
||||
const { t } = useTranslation()
|
||||
const { systemFeatures } = useGlobalPublicStore()
|
||||
const systemFeatures = useSystemFeatures()
|
||||
const router = useRouter()
|
||||
const { currentWorkspace, isCurrentWorkspaceOwner } = useAppContext()
|
||||
const showTagManagementModal = useTagStore(s => s.showTagManagementModal)
|
||||
|
||||
@ -7,7 +7,7 @@ import { useTranslation } from 'react-i18next'
|
||||
import { useContextSelector } from 'use-context-selector'
|
||||
import AppIcon from '@/app/components/base/app-icon'
|
||||
import ExploreContext from '@/context/explore-context'
|
||||
import { useGlobalPublicStore } from '@/context/global-public-context'
|
||||
import { useSystemFeatures } from '@/hooks/use-global-public'
|
||||
import { AppModeEnum } from '@/types/app'
|
||||
import { cn } from '@/utils/classnames'
|
||||
import { AppTypeIcon } from '../../app/type-selector'
|
||||
@ -28,7 +28,7 @@ const AppCard = ({
|
||||
}: AppCardProps) => {
|
||||
const { t } = useTranslation()
|
||||
const { app: appBasicInfo } = app
|
||||
const { systemFeatures } = useGlobalPublicStore()
|
||||
const systemFeatures = useSystemFeatures()
|
||||
const isTrialApp = app.can_trial && systemFeatures.enable_trial_app
|
||||
const setShowTryAppPanel = useContextSelector(ExploreContext, ctx => ctx.setShowTryAppPanel)
|
||||
const showTryAPPPanel = useCallback((appId: string) => {
|
||||
|
||||
@ -17,7 +17,7 @@ import Banner from '@/app/components/explore/banner/banner'
|
||||
import Category from '@/app/components/explore/category'
|
||||
import CreateAppModal from '@/app/components/explore/create-app-modal'
|
||||
import ExploreContext from '@/context/explore-context'
|
||||
import { useGlobalPublicStore } from '@/context/global-public-context'
|
||||
import { useSystemFeatures } from '@/hooks/use-global-public'
|
||||
import { useImportDSL } from '@/hooks/use-import-dsl'
|
||||
import {
|
||||
DSLImportMode,
|
||||
@ -36,7 +36,7 @@ const Apps = ({
|
||||
onSuccess,
|
||||
}: AppsProps) => {
|
||||
const { t } = useTranslation()
|
||||
const { systemFeatures } = useGlobalPublicStore()
|
||||
const systemFeatures = useSystemFeatures()
|
||||
const { hasEditPermission } = useContext(ExploreContext)
|
||||
const allCategoriesEn = t('apps.allCategories', { ns: 'explore', lng: 'en' })
|
||||
|
||||
|
||||
@ -7,7 +7,7 @@ import * as React from 'react'
|
||||
import { useState } from 'react'
|
||||
import Loading from '@/app/components/base/loading'
|
||||
import Modal from '@/app/components/base/modal/index'
|
||||
import { useGlobalPublicStore } from '@/context/global-public-context'
|
||||
import { useSystemFeatures } from '@/hooks/use-global-public'
|
||||
import { useGetTryAppInfo } from '@/service/use-try-app'
|
||||
import Button from '../../base/button'
|
||||
import App from './app'
|
||||
@ -30,7 +30,7 @@ const TryApp: FC<Props> = ({
|
||||
onClose,
|
||||
onCreate,
|
||||
}) => {
|
||||
const { systemFeatures } = useGlobalPublicStore()
|
||||
const systemFeatures = useSystemFeatures()
|
||||
const isTrialApp = !!(app && app.can_trial && systemFeatures.enable_trial_app)
|
||||
const [type, setType] = useState<TypeEnum>(() => (app && !isTrialApp ? TypeEnum.DETAIL : TypeEnum.TRY))
|
||||
const { data: appDetail, isLoading } = useGetTryAppInfo(appId)
|
||||
|
||||
@ -9,7 +9,7 @@ import DifyLogo from '@/app/components/base/logo/dify-logo'
|
||||
import Modal from '@/app/components/base/modal'
|
||||
import { IS_CE_EDITION } from '@/config'
|
||||
|
||||
import { useGlobalPublicStore } from '@/context/global-public-context'
|
||||
import { useSystemFeatures } from '@/hooks/use-global-public'
|
||||
|
||||
type IAccountSettingProps = {
|
||||
langGeniusVersionInfo: LangGeniusVersionResponse
|
||||
@ -22,7 +22,7 @@ export default function AccountAbout({
|
||||
}: IAccountSettingProps) {
|
||||
const { t } = useTranslation()
|
||||
const isLatest = langGeniusVersionInfo.current_version === langGeniusVersionInfo.latest_version
|
||||
const systemFeatures = useGlobalPublicStore(s => s.systemFeatures)
|
||||
const systemFeatures = useSystemFeatures()
|
||||
|
||||
return (
|
||||
<Modal
|
||||
|
||||
@ -24,10 +24,10 @@ import ThemeSwitcher from '@/app/components/base/theme-switcher'
|
||||
import { ACCOUNT_SETTING_TAB } from '@/app/components/header/account-setting/constants'
|
||||
import { IS_CLOUD_EDITION } from '@/config'
|
||||
import { useAppContext } from '@/context/app-context'
|
||||
import { useGlobalPublicStore } from '@/context/global-public-context'
|
||||
import { useDocLink } from '@/context/i18n'
|
||||
import { useModalContext } from '@/context/modal-context'
|
||||
import { useProviderContext } from '@/context/provider-context'
|
||||
import { useSystemFeatures } from '@/hooks/use-global-public'
|
||||
import { useLogout } from '@/service/use-common'
|
||||
import { cn } from '@/utils/classnames'
|
||||
import AccountAbout from '../account-about'
|
||||
@ -43,7 +43,7 @@ export default function AppSelector() {
|
||||
`
|
||||
const router = useRouter()
|
||||
const [aboutVisible, setAboutVisible] = useState(false)
|
||||
const { systemFeatures } = useGlobalPublicStore()
|
||||
const systemFeatures = useSystemFeatures()
|
||||
|
||||
const { t } = useTranslation()
|
||||
const docLink = useDocLink()
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
import { memo } from 'react'
|
||||
import { useGlobalPublicStore } from '@/context/global-public-context'
|
||||
import { useSystemFeatures } from '@/hooks/use-global-public'
|
||||
import { useGetDataSourceListAuth } from '@/service/use-datasource'
|
||||
import Card from './card'
|
||||
import InstallFromMarketplace from './install-from-marketplace'
|
||||
|
||||
const DataSourcePage = () => {
|
||||
const { enable_marketplace } = useGlobalPublicStore(s => s.systemFeatures)
|
||||
const { enable_marketplace } = useSystemFeatures()
|
||||
const { data } = useGetDataSourceListAuth()
|
||||
|
||||
return (
|
||||
|
||||
@ -9,10 +9,10 @@ import { NUM_INFINITE } from '@/app/components/billing/config'
|
||||
import { Plan } from '@/app/components/billing/type'
|
||||
import UpgradeBtn from '@/app/components/billing/upgrade-btn'
|
||||
import { useAppContext } from '@/context/app-context'
|
||||
import { useGlobalPublicStore } from '@/context/global-public-context'
|
||||
import { useLocale } from '@/context/i18n'
|
||||
import { useProviderContext } from '@/context/provider-context'
|
||||
import { useFormatTimeFromNow } from '@/hooks/use-format-time-from-now'
|
||||
import { useSystemFeatures } from '@/hooks/use-global-public'
|
||||
import { LanguagesSupported } from '@/i18n-config/language'
|
||||
import { useMembers } from '@/service/use-common'
|
||||
import EditWorkspaceModal from './edit-workspace-modal'
|
||||
@ -36,7 +36,7 @@ const MembersPage = () => {
|
||||
|
||||
const { userProfile, currentWorkspace, isCurrentWorkspaceOwner, isCurrentWorkspaceManager } = useAppContext()
|
||||
const { data, refetch } = useMembers()
|
||||
const systemFeatures = useGlobalPublicStore(s => s.systemFeatures)
|
||||
const systemFeatures = useSystemFeatures()
|
||||
const { formatTimeFromNow } = useFormatTimeFromNow()
|
||||
const [inviteModalVisible, setInviteModalVisible] = useState(false)
|
||||
const [invitationResults, setInvitationResults] = useState<InvitationResult[]>([])
|
||||
|
||||
@ -3,7 +3,7 @@ import { useTranslation } from 'react-i18next'
|
||||
import Button from '@/app/components/base/button'
|
||||
import Loading from '@/app/components/base/loading'
|
||||
import { useAppContext } from '@/context/app-context'
|
||||
import { useGlobalPublicStore } from '@/context/global-public-context'
|
||||
import { useSystemFeatures } from '@/hooks/use-global-public'
|
||||
import { useWorkspacePermissions } from '@/service/use-workspace'
|
||||
|
||||
type InviteButtonProps = {
|
||||
@ -14,7 +14,7 @@ type InviteButtonProps = {
|
||||
const InviteButton = (props: InviteButtonProps) => {
|
||||
const { t } = useTranslation()
|
||||
const { currentWorkspace } = useAppContext()
|
||||
const systemFeatures = useGlobalPublicStore(s => s.systemFeatures)
|
||||
const systemFeatures = useSystemFeatures()
|
||||
const { data: workspacePermissions, isFetching: isFetchingWorkspacePermissions } = useWorkspacePermissions(currentWorkspace!.id, systemFeatures.branding.enabled)
|
||||
if (systemFeatures.branding.enabled) {
|
||||
if (isFetchingWorkspacePermissions) {
|
||||
|
||||
@ -7,7 +7,7 @@ import { Fragment } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Loading from '@/app/components/base/loading'
|
||||
import { useAppContext } from '@/context/app-context'
|
||||
import { useGlobalPublicStore } from '@/context/global-public-context'
|
||||
import { useSystemFeatures } from '@/hooks/use-global-public'
|
||||
import { useWorkspacePermissions } from '@/service/use-workspace'
|
||||
import { cn } from '@/utils/classnames'
|
||||
|
||||
@ -18,7 +18,7 @@ type Props = {
|
||||
const TransferOwnership = ({ onOperate }: Props) => {
|
||||
const { t } = useTranslation()
|
||||
const { currentWorkspace } = useAppContext()
|
||||
const systemFeatures = useGlobalPublicStore(s => s.systemFeatures)
|
||||
const systemFeatures = useSystemFeatures()
|
||||
const { data: workspacePermissions, isFetching: isFetchingWorkspacePermissions } = useWorkspacePermissions(currentWorkspace!.id, systemFeatures.branding.enabled)
|
||||
if (systemFeatures.branding.enabled) {
|
||||
if (isFetchingWorkspacePermissions) {
|
||||
|
||||
@ -10,8 +10,8 @@ import { useEffect, useMemo } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { IS_CLOUD_EDITION } from '@/config'
|
||||
import { useAppContext } from '@/context/app-context'
|
||||
import { useGlobalPublicStore } from '@/context/global-public-context'
|
||||
import { useProviderContext } from '@/context/provider-context'
|
||||
import { useSystemFeatures } from '@/hooks/use-global-public'
|
||||
import { cn } from '@/utils/classnames'
|
||||
import {
|
||||
CustomConfigurationStatusEnum,
|
||||
@ -41,7 +41,7 @@ const ModelProviderPage = ({ searchText }: Props) => {
|
||||
const { data: speech2textDefaultModel, isLoading: isSpeech2textDefaultModelLoading } = useDefaultModel(ModelTypeEnum.speech2text)
|
||||
const { data: ttsDefaultModel, isLoading: isTTSDefaultModelLoading } = useDefaultModel(ModelTypeEnum.tts)
|
||||
const { modelProviders: providers } = useProviderContext()
|
||||
const { enable_marketplace } = useGlobalPublicStore(s => s.systemFeatures)
|
||||
const { enable_marketplace } = useSystemFeatures()
|
||||
const isDefaultModelLoading = isTextGenerationDefaultModelLoading
|
||||
|| isEmbeddingsDefaultModelLoading
|
||||
|| isRerankDefaultModelLoading
|
||||
|
||||
@ -10,7 +10,7 @@ import Loading from '@/app/components/base/loading'
|
||||
import Tooltip from '@/app/components/base/tooltip'
|
||||
import InstallFromMarketplace from '@/app/components/plugins/install-plugin/install-from-marketplace'
|
||||
import { useAppContext } from '@/context/app-context'
|
||||
import { useGlobalPublicStore } from '@/context/global-public-context'
|
||||
import { useSystemFeatures } from '@/hooks/use-global-public'
|
||||
import useTimestamp from '@/hooks/use-timestamp'
|
||||
import { ModelProviderQuotaGetPaid } from '@/types/model-provider'
|
||||
import { cn } from '@/utils/classnames'
|
||||
@ -56,7 +56,7 @@ const QuotaPanel: FC<QuotaPanelProps> = ({
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const { currentWorkspace } = useAppContext()
|
||||
const { trial_models } = useGlobalPublicStore(s => s.systemFeatures)
|
||||
const { trial_models } = useSystemFeatures()
|
||||
const credits = Math.max((currentWorkspace.trial_credits - currentWorkspace.trial_credits_used) || 0, 0)
|
||||
const providerMap = useMemo(() => new Map(
|
||||
providers.map(p => [p.provider, p.preferred_provider_type]),
|
||||
|
||||
@ -5,11 +5,11 @@ import DifyLogo from '@/app/components/base/logo/dify-logo'
|
||||
import WorkplaceSelector from '@/app/components/header/account-dropdown/workplace-selector'
|
||||
import { ACCOUNT_SETTING_TAB } from '@/app/components/header/account-setting/constants'
|
||||
import { useAppContext } from '@/context/app-context'
|
||||
import { useGlobalPublicStore } from '@/context/global-public-context'
|
||||
import { useModalContext } from '@/context/modal-context'
|
||||
import { useProviderContext } from '@/context/provider-context'
|
||||
import { WorkspaceProvider } from '@/context/workspace-context'
|
||||
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
|
||||
import { useSystemFeatures } from '@/hooks/use-global-public'
|
||||
import { Plan } from '../billing/type'
|
||||
import AccountDropdown from './account-dropdown'
|
||||
import AppNav from './app-nav'
|
||||
@ -33,7 +33,7 @@ const Header = () => {
|
||||
const isMobile = media === MediaType.mobile
|
||||
const { enableBilling, plan } = useProviderContext()
|
||||
const { setShowPricingModal, setShowAccountSettingModal } = useModalContext()
|
||||
const systemFeatures = useGlobalPublicStore(s => s.systemFeatures)
|
||||
const systemFeatures = useSystemFeatures()
|
||||
const isFreePlan = plan.type === Plan.sandbox
|
||||
const isBrandingEnabled = systemFeatures.branding.enabled
|
||||
const handlePlanClick = useCallback(() => {
|
||||
|
||||
@ -3,13 +3,13 @@
|
||||
import { RiHourglass2Fill } from '@remixicon/react'
|
||||
import dayjs from 'dayjs'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useGlobalPublicStore } from '@/context/global-public-context'
|
||||
import { useSystemFeatures } from '@/hooks/use-global-public'
|
||||
import { LicenseStatus } from '@/types/feature'
|
||||
import PremiumBadge from '../../base/premium-badge'
|
||||
|
||||
const LicenseNav = () => {
|
||||
const { t } = useTranslation()
|
||||
const { systemFeatures } = useGlobalPublicStore()
|
||||
const systemFeatures = useSystemFeatures()
|
||||
|
||||
if (systemFeatures.license?.status === LicenseStatus.EXPIRING) {
|
||||
const expiredAt = systemFeatures.license?.expired_at
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import type { Plugin, PluginManifestInMarket } from '../../types'
|
||||
import type { SystemFeatures } from '@/types/feature'
|
||||
import { useGlobalPublicStore } from '@/context/global-public-context'
|
||||
import { useSystemFeatures } from '@/hooks/use-global-public'
|
||||
import { InstallationScope } from '@/types/feature'
|
||||
|
||||
type PluginProps = (Plugin | PluginManifestInMarket) & { from: 'github' | 'marketplace' | 'package' }
|
||||
@ -41,6 +41,6 @@ export function pluginInstallLimit(plugin: PluginProps, systemFeatures: SystemFe
|
||||
}
|
||||
|
||||
export default function usePluginInstallLimit(plugin: PluginProps) {
|
||||
const systemFeatures = useGlobalPublicStore(s => s.systemFeatures)
|
||||
const systemFeatures = useSystemFeatures()
|
||||
return pluginInstallLimit(plugin, systemFeatures)
|
||||
}
|
||||
|
||||
@ -181,7 +181,7 @@ vi.mock('@/context/mitt-context', () => ({
|
||||
|
||||
// Mock global public context
|
||||
vi.mock('@/context/global-public-context', () => ({
|
||||
useGlobalPublicStore: () => ({}),
|
||||
useSystemFeatures: () => ({}),
|
||||
}))
|
||||
|
||||
// Mock useCanInstallPluginFromMarketplace
|
||||
|
||||
@ -56,9 +56,9 @@ vi.mock('@/app/components/plugins/install-plugin/hooks/use-check-installed', ()
|
||||
}),
|
||||
}))
|
||||
|
||||
// Mock useGlobalPublicStore
|
||||
// Mock useSystemFeatures
|
||||
vi.mock('@/context/global-public-context', () => ({
|
||||
useGlobalPublicStore: () => ({}),
|
||||
useSystemFeatures: () => ({}),
|
||||
}))
|
||||
|
||||
// Mock pluginInstallLimit
|
||||
|
||||
@ -4,7 +4,7 @@ import { produce } from 'immer'
|
||||
import * as React from 'react'
|
||||
import { useCallback, useEffect, useImperativeHandle, useMemo, useState } from 'react'
|
||||
import useCheckInstalled from '@/app/components/plugins/install-plugin/hooks/use-check-installed'
|
||||
import { useGlobalPublicStore } from '@/context/global-public-context'
|
||||
import { useSystemFeatures } from '@/hooks/use-global-public'
|
||||
import { useFetchPluginsInMarketPlaceByInfo } from '@/service/use-plugins'
|
||||
import LoadingError from '../../base/loading-error'
|
||||
import { pluginInstallLimit } from '../../hooks/use-install-plugin-limit'
|
||||
@ -38,7 +38,7 @@ const InstallByDSLList = ({
|
||||
isFromMarketPlace,
|
||||
ref,
|
||||
}: Props) => {
|
||||
const systemFeatures = useGlobalPublicStore(s => s.systemFeatures)
|
||||
const systemFeatures = useSystemFeatures()
|
||||
// DSL has id, to get plugin info to show more info
|
||||
const { isLoading: isFetchingMarketplaceDataById, data: infoGetById, error: infoByIdError } = useFetchPluginsInMarketPlaceByInfo(allPlugins.filter(d => d.type === 'marketplace').map((d) => {
|
||||
const dependecy = (d as GitHubItemAndMarketPlaceDependency).value
|
||||
|
||||
@ -66,8 +66,7 @@ vi.mock('@/context/i18n', () => ({
|
||||
let mockEnableMarketplace = true
|
||||
|
||||
vi.mock('@/context/global-public-context', () => ({
|
||||
useGlobalPublicStore: (selector: (state: { systemFeatures: { enable_marketplace: boolean } }) => unknown) =>
|
||||
selector({ systemFeatures: { enable_marketplace: mockEnableMarketplace } }),
|
||||
useSystemFeatures: () => ({ enable_marketplace: mockEnableMarketplace }),
|
||||
}))
|
||||
|
||||
vi.mock('@/context/modal-context', () => ({
|
||||
|
||||
@ -25,10 +25,10 @@ import UpdateFromMarketplace from '@/app/components/plugins/update-plugin/from-m
|
||||
import PluginVersionPicker from '@/app/components/plugins/update-plugin/plugin-version-picker'
|
||||
import { API_PREFIX } from '@/config'
|
||||
import { useAppContext } from '@/context/app-context'
|
||||
import { useGlobalPublicStore } from '@/context/global-public-context'
|
||||
import { useGetLanguage, useLocale } from '@/context/i18n'
|
||||
import { useModalContext } from '@/context/modal-context'
|
||||
import { useProviderContext } from '@/context/provider-context'
|
||||
import { useSystemFeatures } from '@/hooks/use-global-public'
|
||||
import useTheme from '@/hooks/use-theme'
|
||||
import { uninstallPlugin } from '@/service/plugins'
|
||||
import { useAllToolProviders, useInvalidateAllToolProviders } from '@/service/use-tools'
|
||||
@ -72,7 +72,7 @@ const DetailHeader = ({
|
||||
const { setShowUpdatePluginModal } = useModalContext()
|
||||
const { refreshModelProviders } = useProviderContext()
|
||||
const invalidateAllToolProviders = useInvalidateAllToolProviders()
|
||||
const { enable_marketplace } = useGlobalPublicStore(s => s.systemFeatures)
|
||||
const { enable_marketplace } = useSystemFeatures()
|
||||
|
||||
const {
|
||||
id,
|
||||
|
||||
@ -11,8 +11,7 @@ vi.mock('react-i18next', () => ({
|
||||
}))
|
||||
|
||||
vi.mock('@/context/global-public-context', () => ({
|
||||
useGlobalPublicStore: <T,>(selector: (state: { systemFeatures: { enable_marketplace: boolean } }) => T): T =>
|
||||
selector({ systemFeatures: { enable_marketplace: true } }),
|
||||
useSystemFeatures: () => ({ enable_marketplace: true }),
|
||||
}))
|
||||
|
||||
vi.mock('@/utils/classnames', () => ({
|
||||
|
||||
@ -11,7 +11,7 @@ import {
|
||||
PortalToFollowElemContent,
|
||||
PortalToFollowElemTrigger,
|
||||
} from '@/app/components/base/portal-to-follow-elem'
|
||||
import { useGlobalPublicStore } from '@/context/global-public-context'
|
||||
import { useSystemFeatures } from '@/hooks/use-global-public'
|
||||
import { cn } from '@/utils/classnames'
|
||||
import { PluginSource } from '../types'
|
||||
|
||||
@ -42,7 +42,7 @@ const OperationDropdown: FC<Props> = ({
|
||||
setOpen(!openRef.current)
|
||||
}, [setOpen])
|
||||
|
||||
const { enable_marketplace } = useGlobalPublicStore(s => s.systemFeatures)
|
||||
const { enable_marketplace } = useSystemFeatures()
|
||||
|
||||
return (
|
||||
<PortalToFollowElem
|
||||
|
||||
@ -68,8 +68,7 @@ vi.mock('@/context/app-context', () => ({
|
||||
// Mock global public store
|
||||
const mockEnableMarketplace = vi.fn(() => true)
|
||||
vi.mock('@/context/global-public-context', () => ({
|
||||
useGlobalPublicStore: (selector: (s: any) => any) =>
|
||||
selector({ systemFeatures: { enable_marketplace: mockEnableMarketplace() } }),
|
||||
useSystemFeatures: () => ({ enable_marketplace: mockEnableMarketplace() }),
|
||||
}))
|
||||
|
||||
// Mock Action component
|
||||
|
||||
@ -16,7 +16,7 @@ import Tooltip from '@/app/components/base/tooltip'
|
||||
import useRefreshPluginList from '@/app/components/plugins/install-plugin/hooks/use-refresh-plugin-list'
|
||||
import { API_PREFIX } from '@/config'
|
||||
import { useAppContext } from '@/context/app-context'
|
||||
import { useGlobalPublicStore } from '@/context/global-public-context'
|
||||
import { useSystemFeatures } from '@/hooks/use-global-public'
|
||||
import { useRenderI18nObject } from '@/hooks/use-i18n'
|
||||
import useTheme from '@/hooks/use-theme'
|
||||
import { cn } from '@/utils/classnames'
|
||||
@ -85,7 +85,7 @@ const PluginItem: FC<Props> = ({
|
||||
const getValueFromI18nObject = useRenderI18nObject()
|
||||
const title = getValueFromI18nObject(label)
|
||||
const descriptionText = getValueFromI18nObject(description)
|
||||
const { enable_marketplace } = useGlobalPublicStore(s => s.systemFeatures)
|
||||
const { enable_marketplace } = useSystemFeatures()
|
||||
const iconFileName = theme === 'dark' && icon_dark ? icon_dark : icon
|
||||
const iconSrc = iconFileName
|
||||
? (iconFileName.startsWith('http') ? iconFileName : `${API_PREFIX}/workspaces/current/plugin/icon?tenant_id=${tenant_id}&filename=${iconFileName}`)
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { render, screen } from '@testing-library/react'
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
// Import mocks
|
||||
import { useGlobalPublicStore } from '@/context/global-public-context'
|
||||
import { useSystemFeatures } from '@/hooks/use-global-public'
|
||||
|
||||
import { PluginPageContext, PluginPageContextProvider, usePluginPageContext } from './context'
|
||||
|
||||
@ -11,7 +11,7 @@ vi.mock('nuqs', () => ({
|
||||
}))
|
||||
|
||||
vi.mock('@/context/global-public-context', () => ({
|
||||
useGlobalPublicStore: vi.fn(),
|
||||
useSystemFeatures: vi.fn(),
|
||||
}))
|
||||
|
||||
vi.mock('../hooks', () => ({
|
||||
@ -25,12 +25,11 @@ vi.mock('../hooks', () => ({
|
||||
],
|
||||
}))
|
||||
|
||||
// Helper function to mock useGlobalPublicStore with marketplace setting
|
||||
// Helper function to mock useSystemFeatures with marketplace setting
|
||||
const mockGlobalPublicStore = (enableMarketplace: boolean) => {
|
||||
vi.mocked(useGlobalPublicStore).mockImplementation((selector) => {
|
||||
const state = { systemFeatures: { enable_marketplace: enableMarketplace } }
|
||||
return selector(state as Parameters<typeof selector>[0])
|
||||
})
|
||||
vi.mocked(useSystemFeatures).mockReturnValue({
|
||||
enable_marketplace: enableMarketplace,
|
||||
} as ReturnType<typeof useSystemFeatures>)
|
||||
}
|
||||
|
||||
// Test component that uses the context
|
||||
|
||||
@ -13,7 +13,7 @@ import {
|
||||
createContext,
|
||||
useContextSelector,
|
||||
} from 'use-context-selector'
|
||||
import { useGlobalPublicStore } from '@/context/global-public-context'
|
||||
import { useSystemFeatures } from '@/hooks/use-global-public'
|
||||
import { PLUGIN_PAGE_TABS_MAP, usePluginPageTabs } from '../hooks'
|
||||
|
||||
export type PluginPageContextValue = {
|
||||
@ -63,7 +63,7 @@ export const PluginPageContextProvider = ({
|
||||
})
|
||||
const [currentPluginID, setCurrentPluginID] = useState<string | undefined>()
|
||||
|
||||
const { enable_marketplace } = useGlobalPublicStore(s => s.systemFeatures)
|
||||
const { enable_marketplace } = useSystemFeatures()
|
||||
const tabs = usePluginPageTabs()
|
||||
const options = useMemo(() => {
|
||||
return enable_marketplace ? tabs : tabs.filter(tab => tab.value !== PLUGIN_PAGE_TABS_MAP.marketplace)
|
||||
|
||||
@ -56,14 +56,10 @@ vi.mock('../context', () => ({
|
||||
|
||||
// Mock global public store (Zustand store)
|
||||
vi.mock('@/context/global-public-context', () => ({
|
||||
useGlobalPublicStore: (selector: (state: any) => any) => {
|
||||
return selector({
|
||||
systemFeatures: {
|
||||
...defaultSystemFeatures,
|
||||
...mockState.systemFeatures,
|
||||
},
|
||||
})
|
||||
},
|
||||
useSystemFeatures: () => ({
|
||||
...defaultSystemFeatures,
|
||||
...mockState.systemFeatures,
|
||||
}),
|
||||
}))
|
||||
|
||||
// Mock useInstalledPluginList hook
|
||||
|
||||
@ -11,7 +11,7 @@ import { MagicBox } from '@/app/components/base/icons/src/vender/solid/mediaAndD
|
||||
import InstallFromGitHub from '@/app/components/plugins/install-plugin/install-from-github'
|
||||
import InstallFromLocalPackage from '@/app/components/plugins/install-plugin/install-from-local-package'
|
||||
import { SUPPORT_INSTALL_LOCAL_FILE_EXTENSIONS } from '@/config'
|
||||
import { useGlobalPublicStore } from '@/context/global-public-context'
|
||||
import { useSystemFeatures } from '@/hooks/use-global-public'
|
||||
import { useInstalledPluginList } from '@/service/use-plugins'
|
||||
import Line from '../../marketplace/empty/line'
|
||||
import { usePluginPageContext } from '../context'
|
||||
@ -27,7 +27,7 @@ const Empty = () => {
|
||||
const fileInputRef = useRef<HTMLInputElement>(null)
|
||||
const [selectedAction, setSelectedAction] = useState<string | null>(null)
|
||||
const [selectedFile, setSelectedFile] = useState<File | null>(null)
|
||||
const { enable_marketplace, plugin_installation_permission } = useGlobalPublicStore(s => s.systemFeatures)
|
||||
const { enable_marketplace, plugin_installation_permission } = useSystemFeatures()
|
||||
const setActiveTab = usePluginPageContext(v => v.setActiveTab)
|
||||
|
||||
const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
|
||||
@ -28,14 +28,9 @@ vi.mock('@/context/i18n', () => ({
|
||||
}))
|
||||
|
||||
vi.mock('@/context/global-public-context', () => ({
|
||||
useGlobalPublicStore: vi.fn((selector) => {
|
||||
const state = {
|
||||
systemFeatures: {
|
||||
enable_marketplace: true,
|
||||
},
|
||||
}
|
||||
return selector(state)
|
||||
}),
|
||||
useSystemFeatures: vi.fn(() => ({
|
||||
enable_marketplace: true,
|
||||
})),
|
||||
}))
|
||||
|
||||
vi.mock('@/context/app-context', () => ({
|
||||
@ -629,14 +624,9 @@ describe('PluginPage Component', () => {
|
||||
it('should handle marketplace disabled', () => {
|
||||
// Mock marketplace disabled
|
||||
vi.mock('@/context/global-public-context', async () => ({
|
||||
useGlobalPublicStore: vi.fn((selector) => {
|
||||
const state = {
|
||||
systemFeatures: {
|
||||
enable_marketplace: false,
|
||||
},
|
||||
}
|
||||
return selector(state)
|
||||
}),
|
||||
useSystemFeatures: vi.fn(() => ({
|
||||
enable_marketplace: false,
|
||||
})),
|
||||
}))
|
||||
|
||||
vi.mocked(useQueryState).mockReturnValue(['discover', vi.fn()])
|
||||
|
||||
@ -16,9 +16,9 @@ import TabSlider from '@/app/components/base/tab-slider'
|
||||
import Tooltip from '@/app/components/base/tooltip'
|
||||
import ReferenceSettingModal from '@/app/components/plugins/reference-setting-modal'
|
||||
import { MARKETPLACE_API_PREFIX, SUPPORT_INSTALL_LOCAL_FILE_EXTENSIONS } from '@/config'
|
||||
import { useGlobalPublicStore } from '@/context/global-public-context'
|
||||
import { useDocLink } from '@/context/i18n'
|
||||
import useDocumentTitle from '@/hooks/use-document-title'
|
||||
import { useSystemFeatures } from '@/hooks/use-global-public'
|
||||
import { usePluginInstallation } from '@/hooks/use-query-params'
|
||||
import { fetchBundleInfoFromMarketPlace, fetchManifestFromMarketPlace } from '@/service/plugins'
|
||||
import { sleep } from '@/utils'
|
||||
@ -112,7 +112,7 @@ const PluginPage = ({
|
||||
const options = usePluginPageContext(v => v.options)
|
||||
const activeTab = usePluginPageContext(v => v.activeTab)
|
||||
const setActiveTab = usePluginPageContext(v => v.setActiveTab)
|
||||
const { enable_marketplace } = useGlobalPublicStore(s => s.systemFeatures)
|
||||
const { enable_marketplace } = useSystemFeatures()
|
||||
|
||||
const isPluginsTab = useMemo(() => activeTab === PLUGIN_PAGE_TABS_MAP.plugins, [activeTab])
|
||||
const isExploringMarketplace = useMemo(() => {
|
||||
|
||||
@ -16,7 +16,7 @@ import {
|
||||
import InstallFromGitHub from '@/app/components/plugins/install-plugin/install-from-github'
|
||||
import InstallFromLocalPackage from '@/app/components/plugins/install-plugin/install-from-local-package'
|
||||
import { SUPPORT_INSTALL_LOCAL_FILE_EXTENSIONS } from '@/config'
|
||||
import { useGlobalPublicStore } from '@/context/global-public-context'
|
||||
import { useSystemFeatures } from '@/hooks/use-global-public'
|
||||
import { cn } from '@/utils/classnames'
|
||||
|
||||
type Props = {
|
||||
@ -37,7 +37,7 @@ const InstallPluginDropdown = ({
|
||||
const [isMenuOpen, setIsMenuOpen] = useState(false)
|
||||
const [selectedAction, setSelectedAction] = useState<string | null>(null)
|
||||
const [selectedFile, setSelectedFile] = useState<File | null>(null)
|
||||
const { enable_marketplace, plugin_installation_permission } = useGlobalPublicStore(s => s.systemFeatures)
|
||||
const { enable_marketplace, plugin_installation_permission } = useSystemFeatures()
|
||||
|
||||
const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const file = event.target.files?.[0]
|
||||
|
||||
@ -2,7 +2,7 @@ import { renderHook, waitFor } from '@testing-library/react'
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
// Import mocks for assertions
|
||||
import { useAppContext } from '@/context/app-context'
|
||||
import { useGlobalPublicStore } from '@/context/global-public-context'
|
||||
import { useSystemFeatures } from '@/hooks/use-global-public'
|
||||
|
||||
import { useInvalidateReferenceSettings, useMutationReferenceSettings, useReferenceSettings } from '@/service/use-plugins'
|
||||
import Toast from '../../base/toast'
|
||||
@ -21,7 +21,7 @@ vi.mock('@/context/app-context', () => ({
|
||||
}))
|
||||
|
||||
vi.mock('@/context/global-public-context', () => ({
|
||||
useGlobalPublicStore: vi.fn(),
|
||||
useSystemFeatures: vi.fn(),
|
||||
}))
|
||||
|
||||
vi.mock('@/service/use-plugins', () => ({
|
||||
@ -309,14 +309,9 @@ describe('useCanInstallPluginFromMarketplace Hook', () => {
|
||||
})
|
||||
|
||||
it('should return true when marketplace is enabled and canManagement is true', () => {
|
||||
vi.mocked(useGlobalPublicStore).mockImplementation((selector) => {
|
||||
const state = {
|
||||
systemFeatures: {
|
||||
enable_marketplace: true,
|
||||
},
|
||||
}
|
||||
return selector(state as Parameters<typeof selector>[0])
|
||||
})
|
||||
vi.mocked(useSystemFeatures).mockReturnValue({
|
||||
enable_marketplace: true,
|
||||
} as ReturnType<typeof useSystemFeatures>)
|
||||
|
||||
const { result } = renderHook(() => useCanInstallPluginFromMarketplace())
|
||||
|
||||
@ -324,14 +319,9 @@ describe('useCanInstallPluginFromMarketplace Hook', () => {
|
||||
})
|
||||
|
||||
it('should return false when marketplace is disabled', () => {
|
||||
vi.mocked(useGlobalPublicStore).mockImplementation((selector) => {
|
||||
const state = {
|
||||
systemFeatures: {
|
||||
enable_marketplace: false,
|
||||
},
|
||||
}
|
||||
return selector(state as Parameters<typeof selector>[0])
|
||||
})
|
||||
vi.mocked(useSystemFeatures).mockReturnValue({
|
||||
enable_marketplace: false,
|
||||
} as ReturnType<typeof useSystemFeatures>)
|
||||
|
||||
const { result } = renderHook(() => useCanInstallPluginFromMarketplace())
|
||||
|
||||
@ -339,14 +329,9 @@ describe('useCanInstallPluginFromMarketplace Hook', () => {
|
||||
})
|
||||
|
||||
it('should return false when canManagement is false', () => {
|
||||
vi.mocked(useGlobalPublicStore).mockImplementation((selector) => {
|
||||
const state = {
|
||||
systemFeatures: {
|
||||
enable_marketplace: true,
|
||||
},
|
||||
}
|
||||
return selector(state as Parameters<typeof selector>[0])
|
||||
})
|
||||
vi.mocked(useSystemFeatures).mockReturnValue({
|
||||
enable_marketplace: true,
|
||||
} as ReturnType<typeof useSystemFeatures>)
|
||||
|
||||
vi.mocked(useReferenceSettings).mockReturnValue({
|
||||
data: {
|
||||
@ -363,14 +348,9 @@ describe('useCanInstallPluginFromMarketplace Hook', () => {
|
||||
})
|
||||
|
||||
it('should return false when both marketplace is disabled and canManagement is false', () => {
|
||||
vi.mocked(useGlobalPublicStore).mockImplementation((selector) => {
|
||||
const state = {
|
||||
systemFeatures: {
|
||||
enable_marketplace: false,
|
||||
},
|
||||
}
|
||||
return selector(state as Parameters<typeof selector>[0])
|
||||
})
|
||||
vi.mocked(useSystemFeatures).mockReturnValue({
|
||||
enable_marketplace: false,
|
||||
} as ReturnType<typeof useSystemFeatures>)
|
||||
|
||||
vi.mocked(useReferenceSettings).mockReturnValue({
|
||||
data: {
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { useMemo } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useAppContext } from '@/context/app-context'
|
||||
import { useGlobalPublicStore } from '@/context/global-public-context'
|
||||
import { useSystemFeatures } from '@/hooks/use-global-public'
|
||||
import { useInvalidateReferenceSettings, useMutationReferenceSettings, useReferenceSettings } from '@/service/use-plugins'
|
||||
import Toast from '../../base/toast'
|
||||
import { PermissionType } from '../types'
|
||||
@ -48,7 +48,7 @@ const useReferenceSetting = () => {
|
||||
}
|
||||
|
||||
export const useCanInstallPluginFromMarketplace = () => {
|
||||
const { enable_marketplace } = useGlobalPublicStore(s => s.systemFeatures)
|
||||
const { enable_marketplace } = useSystemFeatures()
|
||||
const { canManagement } = useReferenceSetting()
|
||||
|
||||
const canInstallPluginFromMarketplace = useMemo(() => {
|
||||
|
||||
@ -36,9 +36,7 @@ vi.mock('react-i18next', () => ({
|
||||
// Mock global public store
|
||||
const mockSystemFeatures = { enable_marketplace: true }
|
||||
vi.mock('@/context/global-public-context', () => ({
|
||||
useGlobalPublicStore: (selector: (s: { systemFeatures: typeof mockSystemFeatures }) => typeof mockSystemFeatures) => {
|
||||
return selector({ systemFeatures: mockSystemFeatures })
|
||||
},
|
||||
useSystemFeatures: () => mockSystemFeatures,
|
||||
}))
|
||||
|
||||
// Mock Modal component
|
||||
|
||||
@ -9,7 +9,7 @@ import Button from '@/app/components/base/button'
|
||||
import Modal from '@/app/components/base/modal'
|
||||
import { PermissionType } from '@/app/components/plugins/types'
|
||||
import OptionCard from '@/app/components/workflow/nodes/_base/components/option-card'
|
||||
import { useGlobalPublicStore } from '@/context/global-public-context'
|
||||
import { useSystemFeatures } from '@/hooks/use-global-public'
|
||||
import AutoUpdateSetting from './auto-update-setting'
|
||||
import { defaultValue as autoUpdateDefaultValue } from './auto-update-setting/config'
|
||||
import Label from './label'
|
||||
@ -30,7 +30,7 @@ const PluginSettingModal: FC<Props> = ({
|
||||
const { auto_upgrade: autoUpdateConfig, permission: privilege } = payload || {}
|
||||
const [tempPrivilege, setTempPrivilege] = useState<Permissions>(privilege)
|
||||
const [tempAutoUpdateConfig, setTempAutoUpdateConfig] = useState<AutoUpdateConfig>(autoUpdateConfig || autoUpdateDefaultValue)
|
||||
const { enable_marketplace } = useGlobalPublicStore(s => s.systemFeatures)
|
||||
const { enable_marketplace } = useSystemFeatures()
|
||||
const handlePrivilegeChange = useCallback((key: string) => {
|
||||
return (value: PermissionType) => {
|
||||
setTempPrivilege({
|
||||
|
||||
@ -27,11 +27,11 @@ import Toast from '@/app/components/base/toast'
|
||||
import Res from '@/app/components/share/text-generation/result'
|
||||
import RunOnce from '@/app/components/share/text-generation/run-once'
|
||||
import { appDefaultIconBackground, BATCH_CONCURRENCY } from '@/config'
|
||||
import { useGlobalPublicStore } from '@/context/global-public-context'
|
||||
import { useWebAppStore } from '@/context/web-app-context'
|
||||
import { useAppFavicon } from '@/hooks/use-app-favicon'
|
||||
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
|
||||
import useDocumentTitle from '@/hooks/use-document-title'
|
||||
import { useSystemFeatures } from '@/hooks/use-global-public'
|
||||
import { changeLanguage } from '@/i18n-config/client'
|
||||
import { AccessMode } from '@/models/access-control'
|
||||
import { AppSourceType, fetchSavedMessage as doFetchSavedMessage, removeMessage, saveMessage } from '@/service/share'
|
||||
@ -91,7 +91,7 @@ const TextGeneration: FC<IMainProps> = ({
|
||||
doSetInputs(newInputs)
|
||||
inputsRef.current = newInputs
|
||||
}, [])
|
||||
const systemFeatures = useGlobalPublicStore(s => s.systemFeatures)
|
||||
const systemFeatures = useSystemFeatures()
|
||||
const [appId, setAppId] = useState<string>('')
|
||||
const [siteInfo, setSiteInfo] = useState<SiteInfo | null>(null)
|
||||
const [customConfig, setCustomConfig] = useState<Record<string, any> | null>(null)
|
||||
|
||||
@ -1,21 +0,0 @@
|
||||
'use client'
|
||||
import type { FC, PropsWithChildren } from 'react'
|
||||
import * as React from 'react'
|
||||
import { useIsLogin } from '@/service/use-common'
|
||||
import Loading from './base/loading'
|
||||
|
||||
const Splash: FC<PropsWithChildren> = () => {
|
||||
// would auto redirect to signin page if not logged in
|
||||
const { isLoading, data: loginData } = useIsLogin()
|
||||
const isLoggedIn = loginData?.logged_in
|
||||
|
||||
if (isLoading || !isLoggedIn) {
|
||||
return (
|
||||
<div className="fixed inset-0 z-[9999999] flex h-full items-center justify-center bg-background-body">
|
||||
<Loading />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
return null
|
||||
}
|
||||
export default React.memo(Splash)
|
||||
@ -14,7 +14,7 @@ import LabelFilter from '@/app/components/tools/labels/filter'
|
||||
import CustomCreateCard from '@/app/components/tools/provider/custom-create-card'
|
||||
import ProviderDetail from '@/app/components/tools/provider/detail'
|
||||
import WorkflowToolEmpty from '@/app/components/tools/provider/empty'
|
||||
import { useGlobalPublicStore } from '@/context/global-public-context'
|
||||
import { useSystemFeatures } from '@/hooks/use-global-public'
|
||||
import { useCheckInstalled, useInvalidateInstalledPluginList } from '@/service/use-plugins'
|
||||
import { useAllToolProviders } from '@/service/use-tools'
|
||||
import { cn } from '@/utils/classnames'
|
||||
@ -42,7 +42,7 @@ const ProviderList = () => {
|
||||
// searchParams.get('category') === 'workflow'
|
||||
const { t } = useTranslation()
|
||||
const { getTagLabel } = useTags()
|
||||
const { enable_marketplace } = useGlobalPublicStore(s => s.systemFeatures)
|
||||
const { enable_marketplace } = useSystemFeatures()
|
||||
const containerRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
const [activeTab, setActiveTab] = useQueryState('category', {
|
||||
|
||||
@ -18,7 +18,7 @@ import { useTranslation } from 'react-i18next'
|
||||
import Button from '@/app/components/base/button'
|
||||
import Divider from '@/app/components/base/divider'
|
||||
import { SearchMenu } from '@/app/components/base/icons/src/vender/line/general'
|
||||
import { useGlobalPublicStore } from '@/context/global-public-context'
|
||||
import { useSystemFeatures } from '@/hooks/use-global-public'
|
||||
import { useFeaturedTriggersRecommendations } from '@/service/use-plugins'
|
||||
import { useAllTriggerPlugins, useInvalidateAllTriggerPlugins } from '@/service/use-triggers'
|
||||
import { cn } from '@/utils/classnames'
|
||||
@ -54,7 +54,7 @@ const AllStartBlocks = ({
|
||||
const { t } = useTranslation()
|
||||
const [hasStartBlocksContent, setHasStartBlocksContent] = useState(false)
|
||||
const [hasPluginContent, setHasPluginContent] = useState(false)
|
||||
const { enable_marketplace } = useGlobalPublicStore(s => s.systemFeatures)
|
||||
const { enable_marketplace } = useSystemFeatures()
|
||||
const pluginRef = useRef<ListRef>(null)
|
||||
const wrapElemRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
|
||||
@ -19,8 +19,8 @@ import Button from '@/app/components/base/button'
|
||||
import Divider from '@/app/components/base/divider'
|
||||
import { SearchMenu } from '@/app/components/base/icons/src/vender/line/general'
|
||||
import PluginList from '@/app/components/workflow/block-selector/market-place-plugin/list'
|
||||
import { useGlobalPublicStore } from '@/context/global-public-context'
|
||||
import { useGetLanguage } from '@/context/i18n'
|
||||
import { useSystemFeatures } from '@/hooks/use-global-public'
|
||||
import { cn } from '@/utils/classnames'
|
||||
import { getMarketplaceUrl } from '@/utils/var'
|
||||
import { useMarketplacePlugins } from '../../plugins/marketplace/hooks'
|
||||
@ -167,7 +167,7 @@ const AllTools = ({
|
||||
plugins: notInstalledPlugins = [],
|
||||
} = useMarketplacePlugins()
|
||||
|
||||
const { enable_marketplace } = useGlobalPublicStore(s => s.systemFeatures)
|
||||
const { enable_marketplace } = useSystemFeatures()
|
||||
|
||||
useEffect(() => {
|
||||
if (!enable_marketplace)
|
||||
|
||||
@ -11,8 +11,8 @@ import {
|
||||
useRef,
|
||||
} from 'react'
|
||||
import PluginList from '@/app/components/workflow/block-selector/market-place-plugin/list'
|
||||
import { useGlobalPublicStore } from '@/context/global-public-context'
|
||||
import { useGetLanguage } from '@/context/i18n'
|
||||
import { useSystemFeatures } from '@/hooks/use-global-public'
|
||||
import { cn } from '@/utils/classnames'
|
||||
import { useMarketplacePlugins } from '../../plugins/marketplace/hooks'
|
||||
import { PluginCategoryEnum } from '../../plugins/types'
|
||||
@ -76,7 +76,7 @@ const DataSources = ({
|
||||
onSelect(BlockEnum.DataSource, toolDefaultValue && defaultValue)
|
||||
}, [onSelect])
|
||||
|
||||
const { enable_marketplace } = useGlobalPublicStore(s => s.systemFeatures)
|
||||
const { enable_marketplace } = useSystemFeatures()
|
||||
|
||||
const {
|
||||
queryPluginsWithDebounced: fetchPlugins,
|
||||
|
||||
@ -8,7 +8,7 @@ import type {
|
||||
import { memo, useEffect, useMemo } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Tooltip from '@/app/components/base/tooltip'
|
||||
import { useGlobalPublicStore } from '@/context/global-public-context'
|
||||
import { useSystemFeatures } from '@/hooks/use-global-public'
|
||||
import { useFeaturedToolsRecommendations } from '@/service/use-plugins'
|
||||
import { useAllBuiltInTools, useAllCustomTools, useAllMCPTools, useAllWorkflowTools, useInvalidateAllBuiltInTools } from '@/service/use-tools'
|
||||
import { cn } from '@/utils/classnames'
|
||||
@ -64,7 +64,7 @@ const Tabs: FC<TabsProps> = ({
|
||||
const { data: workflowTools } = useAllWorkflowTools()
|
||||
const { data: mcpTools } = useAllMCPTools()
|
||||
const invalidateBuiltInTools = useInvalidateAllBuiltInTools()
|
||||
const { enable_marketplace } = useGlobalPublicStore(s => s.systemFeatures)
|
||||
const { enable_marketplace } = useSystemFeatures()
|
||||
const workflowStore = useWorkflowStore()
|
||||
const inRAGPipeline = dataSources.length > 0
|
||||
const {
|
||||
|
||||
@ -20,7 +20,7 @@ import Toast from '@/app/components/base/toast'
|
||||
import SearchBox from '@/app/components/plugins/marketplace/search-box'
|
||||
import EditCustomToolModal from '@/app/components/tools/edit-custom-collection-modal'
|
||||
import AllTools from '@/app/components/workflow/block-selector/all-tools'
|
||||
import { useGlobalPublicStore } from '@/context/global-public-context'
|
||||
import { useSystemFeatures } from '@/hooks/use-global-public'
|
||||
import {
|
||||
createCustomCollection,
|
||||
} from '@/service/tools'
|
||||
@ -70,7 +70,7 @@ const ToolPicker: FC<Props> = ({
|
||||
const [searchText, setSearchText] = useState('')
|
||||
const [tags, setTags] = useState<string[]>([])
|
||||
|
||||
const { enable_marketplace } = useGlobalPublicStore(s => s.systemFeatures)
|
||||
const { enable_marketplace } = useSystemFeatures()
|
||||
const { data: buildInTools } = useAllBuiltInTools()
|
||||
const { data: customTools } = useAllCustomTools()
|
||||
const invalidateCustomTools = useInvalidateAllCustomTools()
|
||||
|
||||
@ -16,7 +16,7 @@ import { useMarketplacePlugins } from '@/app/components/plugins/marketplace/hook
|
||||
import { PluginCategoryEnum } from '@/app/components/plugins/types'
|
||||
import { CollectionType } from '@/app/components/tools/types'
|
||||
import PluginList from '@/app/components/workflow/block-selector/market-place-plugin/list'
|
||||
import { useGlobalPublicStore } from '@/context/global-public-context'
|
||||
import { useSystemFeatures } from '@/hooks/use-global-public'
|
||||
import { useStrategyProviders } from '@/service/use-strategy'
|
||||
import { cn } from '@/utils/classnames'
|
||||
import Tools from '../../../block-selector/tools'
|
||||
@ -95,7 +95,7 @@ export type AgentStrategySelectorProps = {
|
||||
}
|
||||
|
||||
export const AgentStrategySelector = memo((props: AgentStrategySelectorProps) => {
|
||||
const { enable_marketplace } = useGlobalPublicStore(s => s.systemFeatures)
|
||||
const { enable_marketplace } = useSystemFeatures()
|
||||
|
||||
const { value, onChange } = props
|
||||
const [open, setOpen] = useState(false)
|
||||
|
||||
@ -2,8 +2,8 @@
|
||||
import { useSearchParams } from 'next/navigation'
|
||||
import * as React from 'react'
|
||||
import ChangePasswordForm from '@/app/forgot-password/ChangePasswordForm'
|
||||
import { useGlobalPublicStore } from '@/context/global-public-context'
|
||||
import useDocumentTitle from '@/hooks/use-document-title'
|
||||
import { useSystemFeatures } from '@/hooks/use-global-public'
|
||||
import { cn } from '@/utils/classnames'
|
||||
import Header from '../signin/_header'
|
||||
import ForgotPasswordForm from './ForgotPasswordForm'
|
||||
@ -12,7 +12,7 @@ const ForgotPassword = () => {
|
||||
useDocumentTitle('')
|
||||
const searchParams = useSearchParams()
|
||||
const token = searchParams.get('token')
|
||||
const { systemFeatures } = useGlobalPublicStore()
|
||||
const systemFeatures = useSystemFeatures()
|
||||
|
||||
return (
|
||||
<div className={cn('flex min-h-screen w-full justify-center bg-background-default-burn p-6')}>
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
'use client'
|
||||
import * as React from 'react'
|
||||
import { useGlobalPublicStore } from '@/context/global-public-context'
|
||||
import { useSystemFeatures } from '@/hooks/use-global-public'
|
||||
import { cn } from '@/utils/classnames'
|
||||
import Header from '../signin/_header'
|
||||
import InstallForm from './installForm'
|
||||
|
||||
const Install = () => {
|
||||
const { systemFeatures } = useGlobalPublicStore()
|
||||
const systemFeatures = useSystemFeatures()
|
||||
return (
|
||||
<div className={cn('flex min-h-screen w-full justify-center bg-background-default-burn p-6')}>
|
||||
<div className={cn('flex w-full shrink-0 flex-col rounded-2xl border border-effects-highlight bg-background-default-subtle')}>
|
||||
|
||||
@ -1,13 +1,16 @@
|
||||
import type { Viewport } from 'next'
|
||||
import { dehydrate, HydrationBoundary } from '@tanstack/react-query'
|
||||
import { Provider as JotaiProvider } from 'jotai'
|
||||
import { ThemeProvider } from 'next-themes'
|
||||
import { Instrument_Serif } from 'next/font/google'
|
||||
import { NuqsAdapter } from 'nuqs/adapters/next/app'
|
||||
import GlobalPublicStoreProvider from '@/context/global-public-context'
|
||||
import { TanstackQueryInitializer } from '@/context/query-client'
|
||||
import { getQueryClientServer } from '@/context/query-client-server'
|
||||
import { getLocaleOnServer } from '@/i18n-config/server'
|
||||
import { DatasetAttr } from '@/types/feature'
|
||||
import { cn } from '@/utils/classnames'
|
||||
import { serverFetch } from '@/utils/ssr-fetch'
|
||||
import { ToastProvider } from './components/base/toast'
|
||||
import BrowserInitializer from './components/browser-initializer'
|
||||
import { ReactScanLoader } from './components/devtools/react-scan/loader'
|
||||
@ -39,6 +42,18 @@ const LocaleLayout = async ({
|
||||
children: React.ReactNode
|
||||
}) => {
|
||||
const locale = await getLocaleOnServer()
|
||||
const queryClient = getQueryClientServer()
|
||||
|
||||
await Promise.all([
|
||||
queryClient.prefetchQuery({
|
||||
queryKey: ['systemFeatures'],
|
||||
queryFn: () => serverFetch('/system-features'),
|
||||
}),
|
||||
queryClient.prefetchQuery({
|
||||
queryKey: ['setupStatus'],
|
||||
queryFn: () => serverFetch('/setup'),
|
||||
}),
|
||||
])
|
||||
|
||||
const datasetMap: Record<DatasetAttr, string | undefined> = {
|
||||
[DatasetAttr.DATA_API_PREFIX]: process.env.NEXT_PUBLIC_API_PREFIX,
|
||||
@ -107,13 +122,15 @@ const LocaleLayout = async ({
|
||||
<BrowserInitializer>
|
||||
<SentryInitializer>
|
||||
<TanstackQueryInitializer>
|
||||
<I18nServerProvider>
|
||||
<ToastProvider>
|
||||
<GlobalPublicStoreProvider>
|
||||
{children}
|
||||
</GlobalPublicStoreProvider>
|
||||
</ToastProvider>
|
||||
</I18nServerProvider>
|
||||
<HydrationBoundary state={dehydrate(queryClient)}>
|
||||
<I18nServerProvider>
|
||||
<ToastProvider>
|
||||
<GlobalPublicStoreProvider>
|
||||
{children}
|
||||
</GlobalPublicStoreProvider>
|
||||
</ToastProvider>
|
||||
</I18nServerProvider>
|
||||
</HydrationBoundary>
|
||||
</TanstackQueryInitializer>
|
||||
</SentryInitializer>
|
||||
</BrowserInitializer>
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
'use client'
|
||||
import { useGlobalPublicStore } from '@/context/global-public-context'
|
||||
import { useSystemFeatures } from '@/hooks/use-global-public'
|
||||
|
||||
import { cn } from '@/utils/classnames'
|
||||
import Header from '../signin/_header'
|
||||
|
||||
export default function SignInLayout({ children }: any) {
|
||||
const { systemFeatures } = useGlobalPublicStore()
|
||||
const systemFeatures = useSystemFeatures()
|
||||
return (
|
||||
<>
|
||||
<div className={cn('flex min-h-screen w-full justify-center bg-background-default-burn p-6')}>
|
||||
|
||||
@ -3,8 +3,8 @@ import type { Locale } from '@/i18n-config'
|
||||
import dynamic from 'next/dynamic'
|
||||
import Divider from '@/app/components/base/divider'
|
||||
import LocaleSigninSelect from '@/app/components/base/select/locale-signin'
|
||||
import { useGlobalPublicStore } from '@/context/global-public-context'
|
||||
import { useLocale } from '@/context/i18n'
|
||||
import { useSystemFeatures } from '@/hooks/use-global-public'
|
||||
import { setLocaleOnClient } from '@/i18n-config'
|
||||
import { languages } from '@/i18n-config/language'
|
||||
|
||||
@ -20,7 +20,7 @@ const ThemeSelector = dynamic(() => import('@/app/components/base/theme-selector
|
||||
|
||||
const Header = () => {
|
||||
const locale = useLocale()
|
||||
const systemFeatures = useGlobalPublicStore(s => s.systemFeatures)
|
||||
const systemFeatures = useSystemFeatures()
|
||||
|
||||
return (
|
||||
<div className="flex w-full items-center justify-between p-6">
|
||||
|
||||
@ -12,7 +12,7 @@ import Loading from '@/app/components/base/loading'
|
||||
import { SimpleSelect } from '@/app/components/base/select'
|
||||
import Toast from '@/app/components/base/toast'
|
||||
import { LICENSE_LINK } from '@/constants/link'
|
||||
import { useGlobalPublicStore } from '@/context/global-public-context'
|
||||
import { useSystemFeatures } from '@/hooks/use-global-public'
|
||||
import { setLocaleOnClient } from '@/i18n-config'
|
||||
import { languages, LanguagesSupported } from '@/i18n-config/language'
|
||||
import { activateMember } from '@/service/common'
|
||||
@ -22,7 +22,7 @@ import { resolvePostLoginRedirect } from '../utils/post-login-redirect'
|
||||
|
||||
export default function InviteSettingsPage() {
|
||||
const { t } = useTranslation()
|
||||
const systemFeatures = useGlobalPublicStore(s => s.systemFeatures)
|
||||
const systemFeatures = useSystemFeatures()
|
||||
const router = useRouter()
|
||||
const searchParams = useSearchParams()
|
||||
const token = decodeURIComponent(searchParams.get('invite_token') as string)
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
'use client'
|
||||
import { useGlobalPublicStore } from '@/context/global-public-context'
|
||||
|
||||
import useDocumentTitle from '@/hooks/use-document-title'
|
||||
|
||||
import { useSystemFeatures } from '@/hooks/use-global-public'
|
||||
import { cn } from '@/utils/classnames'
|
||||
import Header from './_header'
|
||||
|
||||
export default function SignInLayout({ children }: any) {
|
||||
const { systemFeatures } = useGlobalPublicStore()
|
||||
const systemFeatures = useSystemFeatures()
|
||||
useDocumentTitle('')
|
||||
return (
|
||||
<>
|
||||
|
||||
@ -6,7 +6,7 @@ import { useCallback, useEffect, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Toast from '@/app/components/base/toast'
|
||||
import { IS_CE_EDITION } from '@/config'
|
||||
import { useGlobalPublicStore } from '@/context/global-public-context'
|
||||
import { useSystemFeatures } from '@/hooks/use-global-public'
|
||||
import { invitationCheck } from '@/service/common'
|
||||
import { useIsLogin } from '@/service/use-common'
|
||||
import { LicenseStatus } from '@/types/feature'
|
||||
@ -30,7 +30,7 @@ const NormalForm = () => {
|
||||
const [isInitCheckLoading, setInitCheckLoading] = useState(true)
|
||||
const [isRedirecting, setIsRedirecting] = useState(false)
|
||||
const isLoading = isCheckLoading || isInitCheckLoading || isRedirecting
|
||||
const { systemFeatures } = useGlobalPublicStore()
|
||||
const systemFeatures = useSystemFeatures()
|
||||
const [authType, updateAuthType] = useState<'code' | 'password'>('password')
|
||||
const [showORLine, setShowORLine] = useState(false)
|
||||
const [allMethodsAreDisabled, setAllMethodsAreDisabled] = useState(false)
|
||||
|
||||
@ -2,8 +2,8 @@ import type { MockedFunction } from 'vitest'
|
||||
import type { SystemFeatures } from '@/types/feature'
|
||||
import { fireEvent, render, screen, waitFor } from '@testing-library/react'
|
||||
import * as React from 'react'
|
||||
import { useGlobalPublicStore } from '@/context/global-public-context'
|
||||
import { useLocale } from '@/context/i18n'
|
||||
import { useSystemFeatures } from '@/hooks/use-global-public'
|
||||
import { useSendMail } from '@/service/use-common'
|
||||
import { defaultSystemFeatures } from '@/types/feature'
|
||||
import Form from './input-mail'
|
||||
@ -33,7 +33,7 @@ vi.mock('next/link', () => ({
|
||||
}))
|
||||
|
||||
vi.mock('@/context/global-public-context', () => ({
|
||||
useGlobalPublicStore: vi.fn(),
|
||||
useSystemFeatures: vi.fn(),
|
||||
}))
|
||||
|
||||
vi.mock('@/context/i18n', () => ({
|
||||
@ -46,7 +46,7 @@ vi.mock('@/service/use-common', () => ({
|
||||
|
||||
type UseSendMailResult = ReturnType<typeof useSendMail>
|
||||
|
||||
const mockUseGlobalPublicStore = useGlobalPublicStore as unknown as MockedFunction<typeof useGlobalPublicStore>
|
||||
const mockUseSystemFeatures = useSystemFeatures as unknown as MockedFunction<typeof useSystemFeatures>
|
||||
const mockUseLocale = useLocale as unknown as MockedFunction<typeof useLocale>
|
||||
const mockUseSendMail = useSendMail as unknown as MockedFunction<typeof useSendMail>
|
||||
|
||||
@ -57,11 +57,9 @@ const renderForm = ({
|
||||
brandingEnabled?: boolean
|
||||
isPending?: boolean
|
||||
} = {}) => {
|
||||
mockUseGlobalPublicStore.mockReturnValue({
|
||||
systemFeatures: buildSystemFeatures({
|
||||
branding: { enabled: brandingEnabled },
|
||||
}),
|
||||
})
|
||||
mockUseSystemFeatures.mockReturnValue(buildSystemFeatures({
|
||||
branding: { enabled: brandingEnabled },
|
||||
}))
|
||||
mockUseLocale.mockReturnValue('en-US')
|
||||
mockUseSendMail.mockReturnValue({
|
||||
mutateAsync: mockSubmitMail,
|
||||
|
||||
@ -8,8 +8,8 @@ import Input from '@/app/components/base/input'
|
||||
import Toast from '@/app/components/base/toast'
|
||||
import Split from '@/app/signin/split'
|
||||
import { emailRegex } from '@/config'
|
||||
import { useGlobalPublicStore } from '@/context/global-public-context'
|
||||
import { useLocale } from '@/context/i18n'
|
||||
import { useSystemFeatures } from '@/hooks/use-global-public'
|
||||
import { useSendMail } from '@/service/use-common'
|
||||
|
||||
type Props = {
|
||||
@ -21,7 +21,7 @@ export default function Form({
|
||||
const { t } = useTranslation()
|
||||
const [email, setEmail] = useState('')
|
||||
const locale = useLocale()
|
||||
const { systemFeatures } = useGlobalPublicStore()
|
||||
const systemFeatures = useSystemFeatures()
|
||||
|
||||
const { mutateAsync: submitMail, isPending } = useSendMail()
|
||||
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
'use client'
|
||||
import Header from '@/app/signin/_header'
|
||||
|
||||
import { useGlobalPublicStore } from '@/context/global-public-context'
|
||||
import useDocumentTitle from '@/hooks/use-document-title'
|
||||
import { useSystemFeatures } from '@/hooks/use-global-public'
|
||||
import { cn } from '@/utils/classnames'
|
||||
|
||||
export default function RegisterLayout({ children }: any) {
|
||||
const { systemFeatures } = useGlobalPublicStore()
|
||||
const systemFeatures = useSystemFeatures()
|
||||
useDocumentTitle('')
|
||||
return (
|
||||
<>
|
||||
|
||||
@ -10,12 +10,12 @@ import { setUserId, setUserProperties } from '@/app/components/base/amplitude'
|
||||
import { setZendeskConversationFields } from '@/app/components/base/zendesk/utils'
|
||||
import MaintenanceNotice from '@/app/components/header/maintenance-notice'
|
||||
import { ZENDESK_FIELD_IDS } from '@/config'
|
||||
import { useSystemFeatures } from '@/hooks/use-global-public'
|
||||
import {
|
||||
useCurrentWorkspace,
|
||||
useLangGeniusVersion,
|
||||
useUserProfile,
|
||||
} from '@/service/use-common'
|
||||
import { useGlobalPublicStore } from './global-public-context'
|
||||
|
||||
export type AppContextValue = {
|
||||
userProfile: UserProfileResponse
|
||||
@ -89,7 +89,7 @@ export type AppContextProviderProps = {
|
||||
|
||||
export const AppContextProvider: FC<AppContextProviderProps> = ({ children }) => {
|
||||
const queryClient = useQueryClient()
|
||||
const systemFeatures = useGlobalPublicStore(s => s.systemFeatures)
|
||||
const systemFeatures = useSystemFeatures()
|
||||
const { data: userProfileResp } = useUserProfile()
|
||||
const { data: currentWorkspaceResp, isPending: isLoadingCurrentWorkspace, isFetching: isValidatingCurrentWorkspace } = useCurrentWorkspace()
|
||||
const langGeniusVersionQuery = useLangGeniusVersion(
|
||||
|
||||
@ -1,61 +1,12 @@
|
||||
'use client'
|
||||
import type { FC, PropsWithChildren } from 'react'
|
||||
import type { SystemFeatures } from '@/types/feature'
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import { create } from 'zustand'
|
||||
import Loading from '@/app/components/base/loading'
|
||||
import { consoleClient } from '@/service/client'
|
||||
import { defaultSystemFeatures } from '@/types/feature'
|
||||
import { fetchSetupStatusWithCache } from '@/utils/setup-status'
|
||||
|
||||
type GlobalPublicStore = {
|
||||
systemFeatures: SystemFeatures
|
||||
setSystemFeatures: (systemFeatures: SystemFeatures) => void
|
||||
}
|
||||
|
||||
export const useGlobalPublicStore = create<GlobalPublicStore>(set => ({
|
||||
systemFeatures: defaultSystemFeatures,
|
||||
setSystemFeatures: (systemFeatures: SystemFeatures) => set(() => ({ systemFeatures })),
|
||||
}))
|
||||
|
||||
const systemFeaturesQueryKey = ['systemFeatures'] as const
|
||||
const setupStatusQueryKey = ['setupStatus'] as const
|
||||
|
||||
async function fetchSystemFeatures() {
|
||||
const data = await consoleClient.systemFeatures()
|
||||
const { setSystemFeatures } = useGlobalPublicStore.getState()
|
||||
setSystemFeatures({ ...defaultSystemFeatures, ...data })
|
||||
return data
|
||||
}
|
||||
|
||||
export function useSystemFeaturesQuery() {
|
||||
return useQuery({
|
||||
queryKey: systemFeaturesQueryKey,
|
||||
queryFn: fetchSystemFeatures,
|
||||
})
|
||||
}
|
||||
|
||||
export function useIsSystemFeaturesPending() {
|
||||
const { isPending } = useSystemFeaturesQuery()
|
||||
return isPending
|
||||
}
|
||||
|
||||
export function useSetupStatusQuery() {
|
||||
return useQuery({
|
||||
queryKey: setupStatusQueryKey,
|
||||
queryFn: fetchSetupStatusWithCache,
|
||||
staleTime: Infinity,
|
||||
})
|
||||
}
|
||||
import { useSetupStatusQuery, useSystemFeaturesQuery } from '@/hooks/use-global-public'
|
||||
|
||||
const GlobalPublicStoreProvider: FC<PropsWithChildren> = ({
|
||||
children,
|
||||
}) => {
|
||||
// Fetch systemFeatures and setupStatus in parallel to reduce waterfall.
|
||||
// setupStatus is prefetched here and cached in localStorage for AppInitializer.
|
||||
const { isPending } = useSystemFeaturesQuery()
|
||||
|
||||
// Prefetch setupStatus for AppInitializer (result not needed here)
|
||||
useSetupStatusQuery()
|
||||
|
||||
if (isPending)
|
||||
|
||||
@ -8,9 +8,9 @@ import { useEffect } from 'react'
|
||||
import { create } from 'zustand'
|
||||
import { getProcessedSystemVariablesFromUrlParams } from '@/app/components/base/chat/utils'
|
||||
import Loading from '@/app/components/base/loading'
|
||||
import { useIsSystemFeaturesPending } from '@/hooks/use-global-public'
|
||||
import { AccessMode } from '@/models/access-control'
|
||||
import { useGetWebAppAccessModeByCode } from '@/service/use-share'
|
||||
import { useIsSystemFeaturesPending } from './global-public-context'
|
||||
|
||||
type WebAppStore = {
|
||||
shareCode: string | null
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import type { SetupStatusResponse } from '@/models/common'
|
||||
import type { SystemFeatures } from '@/types/feature'
|
||||
import { type } from '@orpc/contract'
|
||||
import { base } from '../base'
|
||||
@ -9,3 +10,11 @@ export const systemFeaturesContract = base
|
||||
})
|
||||
.input(type<unknown>())
|
||||
.output(type<SystemFeatures>())
|
||||
|
||||
export const setupStatusContract = base
|
||||
.route({
|
||||
path: '/setup',
|
||||
method: 'GET',
|
||||
})
|
||||
.input(type<unknown>())
|
||||
.output(type<SetupStatusResponse>())
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import type { InferContractRouterInputs } from '@orpc/contract'
|
||||
import { bindPartnerStackContract, invoicesContract } from './console/billing'
|
||||
import { systemFeaturesContract } from './console/system'
|
||||
import { setupStatusContract, systemFeaturesContract } from './console/system'
|
||||
import { trialAppDatasetsContract, trialAppInfoContract, trialAppParametersContract, trialAppWorkflowsContract } from './console/try-app'
|
||||
import { collectionPluginsContract, collectionsContract, searchAdvancedContract } from './marketplace'
|
||||
|
||||
@ -14,6 +14,7 @@ export type MarketPlaceInputs = InferContractRouterInputs<typeof marketplaceRout
|
||||
|
||||
export const consoleRouterContract = {
|
||||
systemFeatures: systemFeaturesContract,
|
||||
setupStatus: setupStatusContract,
|
||||
trialApps: {
|
||||
info: trialAppInfoContract,
|
||||
datasets: trialAppDatasetsContract,
|
||||
|
||||
@ -24,7 +24,7 @@
|
||||
},
|
||||
"__tests__/embedded-user-id-store.test.tsx": {
|
||||
"ts/no-explicit-any": {
|
||||
"count": 3
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"__tests__/goto-anything/command-selector.test.tsx": {
|
||||
@ -104,11 +104,6 @@
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"app/(shareLayout)/webapp-reset-password/layout.tsx": {
|
||||
"ts/no-explicit-any": {
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"app/(shareLayout)/webapp-signin/components/mail-and-password-auth.tsx": {
|
||||
"ts/no-explicit-any": {
|
||||
"count": 2
|
||||
@ -656,7 +651,7 @@
|
||||
},
|
||||
"app/components/apps/app-card.spec.tsx": {
|
||||
"ts/no-explicit-any": {
|
||||
"count": 22
|
||||
"count": 20
|
||||
}
|
||||
},
|
||||
"app/components/apps/app-card.tsx": {
|
||||
@ -1674,7 +1669,7 @@
|
||||
},
|
||||
"app/components/custom/custom-web-app-brand/index.spec.tsx": {
|
||||
"ts/no-explicit-any": {
|
||||
"count": 7
|
||||
"count": 6
|
||||
}
|
||||
},
|
||||
"app/components/custom/custom-web-app-brand/index.tsx": {
|
||||
@ -2543,7 +2538,7 @@
|
||||
},
|
||||
"app/components/plugins/plugin-item/index.spec.tsx": {
|
||||
"ts/no-explicit-any": {
|
||||
"count": 10
|
||||
"count": 8
|
||||
}
|
||||
},
|
||||
"app/components/plugins/plugin-item/index.tsx": {
|
||||
@ -2566,7 +2561,7 @@
|
||||
},
|
||||
"app/components/plugins/plugin-page/empty/index.spec.tsx": {
|
||||
"ts/no-explicit-any": {
|
||||
"count": 7
|
||||
"count": 5
|
||||
}
|
||||
},
|
||||
"app/components/plugins/plugin-page/empty/index.tsx": {
|
||||
@ -4346,11 +4341,6 @@
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"context/global-public-context.tsx": {
|
||||
"react-refresh/only-export-components": {
|
||||
"count": 4
|
||||
}
|
||||
},
|
||||
"context/hooks/use-trigger-events-limit-modal.ts": {
|
||||
"react-hooks-extra/no-direct-set-state-in-use-effect": {
|
||||
"count": 3
|
||||
|
||||
377
web/gen/orpc.ts
377
web/gen/orpc.ts
@ -1,377 +0,0 @@
|
||||
// This file is auto-generated by @hey-api/openapi-ts
|
||||
|
||||
import { oc } from '@orpc/contract'
|
||||
import { z } from 'zod'
|
||||
|
||||
import { zAudioToTextData, zAudioToTextResponse2, zCreateAnnotationData, zCreateAnnotationResponse, zDeleteAnnotationData, zDeleteConversationData, zGetAnnotationListData, zGetAnnotationListResponse, zGetChatAppFeedbacksData, zGetChatAppFeedbacksResponse, zGetChatAppInfoResponse, zGetChatAppMetaResponse, zGetChatAppParametersData, zGetChatAppParametersResponse, zGetChatWebAppSettingsResponse, zGetConversationHistoryData, zGetConversationHistoryResponse, zGetConversationsListData, zGetConversationsListResponse, zGetConversationVariablesData, zGetConversationVariablesResponse, zGetInitialAnnotationReplySettingsStatusData, zGetInitialAnnotationReplySettingsStatusResponse, zGetSuggestedQuestionsData, zGetSuggestedQuestionsResponse, zInitialAnnotationReplySettingsData, zInitialAnnotationReplySettingsResponse2, zPostChatMessageFeedbackData, zPostChatMessageFeedbackResponse, zPreviewChatFileData, zPreviewChatFileResponse, zRenameConversationData, zRenameConversationResponse, zSendChatMessageData, zSendChatMessageResponse, zStopChatMessageGenerationData, zStopChatMessageGenerationResponse, zTextToAudioChatData, zTextToAudioChatResponse, zUpdateAnnotationData, zUpdateAnnotationResponse, zUploadChatFileData, zUploadChatFileResponse } from './zod'
|
||||
|
||||
export const base = oc.$route({ inputStructure: 'detailed', outputStructure: 'detailed' })
|
||||
|
||||
/**
|
||||
* Send Chat Message
|
||||
*
|
||||
* Send a request to the chat application.
|
||||
*/
|
||||
export const sendChatMessageContract = base.route({
|
||||
method: 'POST',
|
||||
path: '/chat-messages',
|
||||
operationId: 'sendChatMessage',
|
||||
summary: 'Send Chat Message',
|
||||
description: 'Send a request to the chat application.',
|
||||
tags: ['Chat'],
|
||||
}).input(zSendChatMessageData).output(z.object({ body: zSendChatMessageResponse, status: z.literal(200) }))
|
||||
|
||||
/**
|
||||
* File Upload
|
||||
*
|
||||
* Upload a file (currently only images are supported) for use when sending messages, enabling multimodal understanding of images and text. Supports png, jpg, jpeg, webp, gif formats. Uploaded files are for use by the current end-user only.
|
||||
*/
|
||||
export const uploadChatFileContract = base.route({
|
||||
method: 'POST',
|
||||
path: '/files/upload',
|
||||
operationId: 'uploadChatFile',
|
||||
summary: 'File Upload',
|
||||
description: 'Upload a file (currently only images are supported) for use when sending messages, enabling multimodal understanding of images and text. Supports png, jpg, jpeg, webp, gif formats. Uploaded files are for use by the current end-user only.',
|
||||
tags: ['Files'],
|
||||
}).input(zUploadChatFileData).output(z.object({ body: zUploadChatFileResponse, status: z.literal(200) }))
|
||||
|
||||
/**
|
||||
* File Preview
|
||||
*
|
||||
* Preview or download uploaded files. This endpoint allows you to access files that have been previously uploaded via the File Upload API. Files can only be accessed if they belong to messages within the requesting application.
|
||||
*/
|
||||
export const previewChatFileContract = base.route({
|
||||
method: 'GET',
|
||||
path: '/files/{file_id}/preview',
|
||||
operationId: 'previewChatFile',
|
||||
summary: 'File Preview',
|
||||
description: 'Preview or download uploaded files. This endpoint allows you to access files that have been previously uploaded via the File Upload API. Files can only be accessed if they belong to messages within the requesting application.',
|
||||
tags: ['Files'],
|
||||
}).input(zPreviewChatFileData).output(z.object({ body: zPreviewChatFileResponse, status: z.literal(200) }))
|
||||
|
||||
/**
|
||||
* Stop Chat Message Generation
|
||||
*
|
||||
* Stops a chat message generation task. Only supported in streaming mode.
|
||||
*/
|
||||
export const stopChatMessageGenerationContract = base.route({
|
||||
method: 'POST',
|
||||
path: '/chat-messages/{task_id}/stop',
|
||||
operationId: 'stopChatMessageGeneration',
|
||||
summary: 'Stop Chat Message Generation',
|
||||
description: 'Stops a chat message generation task. Only supported in streaming mode.',
|
||||
tags: ['Chat'],
|
||||
}).input(zStopChatMessageGenerationData).output(z.object({ body: zStopChatMessageGenerationResponse, status: z.literal(200) }))
|
||||
|
||||
/**
|
||||
* Message Feedback
|
||||
*
|
||||
* End-users can provide feedback messages, facilitating application developers to optimize expected outputs.
|
||||
*/
|
||||
export const postChatMessageFeedbackContract = base.route({
|
||||
method: 'POST',
|
||||
path: '/messages/{message_id}/feedbacks',
|
||||
operationId: 'postChatMessageFeedback',
|
||||
summary: 'Message Feedback',
|
||||
description: 'End-users can provide feedback messages, facilitating application developers to optimize expected outputs.',
|
||||
tags: ['Feedback'],
|
||||
}).input(zPostChatMessageFeedbackData).output(z.object({ body: zPostChatMessageFeedbackResponse, status: z.literal(200) }))
|
||||
|
||||
/**
|
||||
* Get feedbacks of application
|
||||
*
|
||||
* Get application's feedbacks.
|
||||
*/
|
||||
export const getChatAppFeedbacksContract = base.route({
|
||||
method: 'GET',
|
||||
path: '/app/feedbacks',
|
||||
operationId: 'getChatAppFeedbacks',
|
||||
summary: 'Get feedbacks of application',
|
||||
description: 'Get application\'s feedbacks.',
|
||||
tags: ['Feedback'],
|
||||
}).input(zGetChatAppFeedbacksData).output(z.object({ body: zGetChatAppFeedbacksResponse, status: z.literal(200) }))
|
||||
|
||||
/**
|
||||
* Next Suggested Questions
|
||||
*
|
||||
* Get next questions suggestions for the current message.
|
||||
*/
|
||||
export const getSuggestedQuestionsContract = base.route({
|
||||
method: 'GET',
|
||||
path: '/messages/{message_id}/suggested',
|
||||
operationId: 'getSuggestedQuestions',
|
||||
summary: 'Next Suggested Questions',
|
||||
description: 'Get next questions suggestions for the current message.',
|
||||
tags: ['Chat'],
|
||||
}).input(zGetSuggestedQuestionsData).output(z.object({ body: zGetSuggestedQuestionsResponse, status: z.literal(200) }))
|
||||
|
||||
/**
|
||||
* Get Conversation History Messages
|
||||
*
|
||||
* Returns historical chat records in a scrolling load format, with the first page returning the latest `{limit}` messages, i.e., in reverse order.
|
||||
*/
|
||||
export const getConversationHistoryContract = base.route({
|
||||
method: 'GET',
|
||||
path: '/messages',
|
||||
operationId: 'getConversationHistory',
|
||||
summary: 'Get Conversation History Messages',
|
||||
description: 'Returns historical chat records in a scrolling load format, with the first page returning the latest `{limit}` messages, i.e., in reverse order.',
|
||||
tags: ['Conversations'],
|
||||
}).input(zGetConversationHistoryData).output(z.object({ body: zGetConversationHistoryResponse, status: z.literal(200) }))
|
||||
|
||||
/**
|
||||
* Get Conversations
|
||||
*
|
||||
* Retrieve the conversation list for the current user, defaulting to the most recent 20 entries.
|
||||
*/
|
||||
export const getConversationsListContract = base.route({
|
||||
method: 'GET',
|
||||
path: '/conversations',
|
||||
operationId: 'getConversationsList',
|
||||
summary: 'Get Conversations',
|
||||
description: 'Retrieve the conversation list for the current user, defaulting to the most recent 20 entries.',
|
||||
tags: ['Conversations'],
|
||||
}).input(zGetConversationsListData).output(z.object({ body: zGetConversationsListResponse, status: z.literal(200) }))
|
||||
|
||||
/**
|
||||
* Delete Conversation
|
||||
*
|
||||
* Delete a conversation.
|
||||
*/
|
||||
export const deleteConversationContract = base.route({
|
||||
method: 'DELETE',
|
||||
path: '/conversations/{conversation_id}',
|
||||
operationId: 'deleteConversation',
|
||||
summary: 'Delete Conversation',
|
||||
description: 'Delete a conversation.',
|
||||
tags: ['Conversations'],
|
||||
}).input(zDeleteConversationData)
|
||||
|
||||
/**
|
||||
* Conversation Rename
|
||||
*
|
||||
* Rename the session. The session name is used for display on clients that support multiple sessions.
|
||||
*/
|
||||
export const renameConversationContract = base.route({
|
||||
method: 'POST',
|
||||
path: '/conversations/{conversation_id}/name',
|
||||
operationId: 'renameConversation',
|
||||
summary: 'Conversation Rename',
|
||||
description: 'Rename the session. The session name is used for display on clients that support multiple sessions.',
|
||||
tags: ['Conversations'],
|
||||
}).input(zRenameConversationData).output(z.object({ body: zRenameConversationResponse, status: z.literal(200) }))
|
||||
|
||||
/**
|
||||
* Get Conversation Variables
|
||||
*
|
||||
* Retrieve variables from a specific conversation.
|
||||
*/
|
||||
export const getConversationVariablesContract = base.route({
|
||||
method: 'GET',
|
||||
path: '/conversations/{conversation_id}/variables',
|
||||
operationId: 'getConversationVariables',
|
||||
summary: 'Get Conversation Variables',
|
||||
description: 'Retrieve variables from a specific conversation.',
|
||||
tags: ['Conversations'],
|
||||
}).input(zGetConversationVariablesData).output(z.object({ body: zGetConversationVariablesResponse, status: z.literal(200) }))
|
||||
|
||||
/**
|
||||
* Speech to Text
|
||||
*
|
||||
* Convert audio file to text. Supported formats: mp3, mp4, mpeg, mpga, m4a, wav, webm. File size limit: 15MB.
|
||||
*/
|
||||
export const audioToTextContract = base.route({
|
||||
method: 'POST',
|
||||
path: '/audio-to-text',
|
||||
operationId: 'audioToText',
|
||||
summary: 'Speech to Text',
|
||||
description: 'Convert audio file to text. Supported formats: mp3, mp4, mpeg, mpga, m4a, wav, webm. File size limit: 15MB.',
|
||||
tags: ['TTS'],
|
||||
}).input(zAudioToTextData).output(z.object({ body: zAudioToTextResponse2, status: z.literal(200) }))
|
||||
|
||||
/**
|
||||
* Text to Audio
|
||||
*
|
||||
* Convert text to speech.
|
||||
*/
|
||||
export const textToAudioChatContract = base.route({
|
||||
method: 'POST',
|
||||
path: '/text-to-audio',
|
||||
operationId: 'textToAudioChat',
|
||||
summary: 'Text to Audio',
|
||||
description: 'Convert text to speech.',
|
||||
tags: ['TTS'],
|
||||
}).input(zTextToAudioChatData).output(z.object({ body: zTextToAudioChatResponse, status: z.literal(200) }))
|
||||
|
||||
/**
|
||||
* Get Application Basic Information
|
||||
*
|
||||
* Used to get basic information about this application.
|
||||
*/
|
||||
export const getChatAppInfoContract = base.route({
|
||||
method: 'GET',
|
||||
path: '/info',
|
||||
operationId: 'getChatAppInfo',
|
||||
summary: 'Get Application Basic Information',
|
||||
description: 'Used to get basic information about this application.',
|
||||
tags: ['Application'],
|
||||
}).output(z.object({ body: zGetChatAppInfoResponse, status: z.literal(200) }))
|
||||
|
||||
/**
|
||||
* Get Application Parameters Information
|
||||
*
|
||||
* Used at the start of entering the page to obtain information such as features, input parameter names, types, and default values.
|
||||
*/
|
||||
export const getChatAppParametersContract = base.route({
|
||||
method: 'GET',
|
||||
path: '/parameters',
|
||||
operationId: 'getChatAppParameters',
|
||||
summary: 'Get Application Parameters Information',
|
||||
description: 'Used at the start of entering the page to obtain information such as features, input parameter names, types, and default values.',
|
||||
tags: ['Application'],
|
||||
}).input(zGetChatAppParametersData).output(z.object({ body: zGetChatAppParametersResponse, status: z.literal(200) }))
|
||||
|
||||
/**
|
||||
* Get Application Meta Information
|
||||
*
|
||||
* Used to get icons of tools in this application.
|
||||
*/
|
||||
export const getChatAppMetaContract = base.route({
|
||||
method: 'GET',
|
||||
path: '/meta',
|
||||
operationId: 'getChatAppMeta',
|
||||
summary: 'Get Application Meta Information',
|
||||
description: 'Used to get icons of tools in this application.',
|
||||
tags: ['Application'],
|
||||
}).output(z.object({ body: zGetChatAppMetaResponse, status: z.literal(200) }))
|
||||
|
||||
/**
|
||||
* Get Application WebApp Settings
|
||||
*
|
||||
* Used to get the WebApp settings of the application.
|
||||
*/
|
||||
export const getChatWebAppSettingsContract = base.route({
|
||||
method: 'GET',
|
||||
path: '/site',
|
||||
operationId: 'getChatWebAppSettings',
|
||||
summary: 'Get Application WebApp Settings',
|
||||
description: 'Used to get the WebApp settings of the application.',
|
||||
tags: ['Application'],
|
||||
}).output(z.object({ body: zGetChatWebAppSettingsResponse, status: z.literal(200) }))
|
||||
|
||||
/**
|
||||
* Get Annotation List
|
||||
*
|
||||
* Retrieves a list of annotations for the application.
|
||||
*/
|
||||
export const getAnnotationListContract = base.route({
|
||||
method: 'GET',
|
||||
path: '/apps/annotations',
|
||||
operationId: 'getAnnotationList',
|
||||
summary: 'Get Annotation List',
|
||||
description: 'Retrieves a list of annotations for the application.',
|
||||
tags: ['Annotations'],
|
||||
}).input(zGetAnnotationListData).output(z.object({ body: zGetAnnotationListResponse, status: z.literal(200) }))
|
||||
|
||||
/**
|
||||
* Create Annotation
|
||||
*
|
||||
* Creates a new annotation.
|
||||
*/
|
||||
export const createAnnotationContract = base.route({
|
||||
method: 'POST',
|
||||
path: '/apps/annotations',
|
||||
operationId: 'createAnnotation',
|
||||
summary: 'Create Annotation',
|
||||
description: 'Creates a new annotation.',
|
||||
tags: ['Annotations'],
|
||||
}).input(zCreateAnnotationData).output(z.object({ body: zCreateAnnotationResponse, status: z.literal(200) }))
|
||||
|
||||
/**
|
||||
* Delete Annotation
|
||||
*
|
||||
* Deletes an annotation.
|
||||
*/
|
||||
export const deleteAnnotationContract = base.route({
|
||||
method: 'DELETE',
|
||||
path: '/apps/annotations/{annotation_id}',
|
||||
operationId: 'deleteAnnotation',
|
||||
summary: 'Delete Annotation',
|
||||
description: 'Deletes an annotation.',
|
||||
tags: ['Annotations'],
|
||||
}).input(zDeleteAnnotationData)
|
||||
|
||||
/**
|
||||
* Update Annotation
|
||||
*
|
||||
* Updates an existing annotation.
|
||||
*/
|
||||
export const updateAnnotationContract = base.route({
|
||||
method: 'PUT',
|
||||
path: '/apps/annotations/{annotation_id}',
|
||||
operationId: 'updateAnnotation',
|
||||
summary: 'Update Annotation',
|
||||
description: 'Updates an existing annotation.',
|
||||
tags: ['Annotations'],
|
||||
}).input(zUpdateAnnotationData).output(z.object({ body: zUpdateAnnotationResponse, status: z.literal(200) }))
|
||||
|
||||
/**
|
||||
* Initial Annotation Reply Settings
|
||||
*
|
||||
* Enable or disable annotation reply settings and configure embedding models. This interface is executed asynchronously.
|
||||
*/
|
||||
export const initialAnnotationReplySettingsContract = base.route({
|
||||
method: 'POST',
|
||||
path: '/apps/annotation-reply/{action}',
|
||||
operationId: 'initialAnnotationReplySettings',
|
||||
summary: 'Initial Annotation Reply Settings',
|
||||
description: 'Enable or disable annotation reply settings and configure embedding models. This interface is executed asynchronously.',
|
||||
tags: ['Annotations'],
|
||||
}).input(zInitialAnnotationReplySettingsData).output(z.object({ body: zInitialAnnotationReplySettingsResponse2, status: z.literal(200) }))
|
||||
|
||||
/**
|
||||
* Query Initial Annotation Reply Settings Task Status
|
||||
*
|
||||
* Queries the status of an asynchronously executed annotation reply settings task.
|
||||
*/
|
||||
export const getInitialAnnotationReplySettingsStatusContract = base.route({
|
||||
method: 'GET',
|
||||
path: '/apps/annotation-reply/{action}/status/{job_id}',
|
||||
operationId: 'getInitialAnnotationReplySettingsStatus',
|
||||
summary: 'Query Initial Annotation Reply Settings Task Status',
|
||||
description: 'Queries the status of an asynchronously executed annotation reply settings task.',
|
||||
tags: ['Annotations'],
|
||||
}).input(zGetInitialAnnotationReplySettingsStatusData).output(z.object({ body: zGetInitialAnnotationReplySettingsStatusResponse, status: z.literal(200) }))
|
||||
|
||||
export const router = {
|
||||
chatMessages: { send: sendChatMessageContract, stopGeneration: stopChatMessageGenerationContract },
|
||||
files: { uploadChat: uploadChatFileContract, previewChat: previewChatFileContract },
|
||||
messages: {
|
||||
postChatFeedback: postChatMessageFeedbackContract,
|
||||
getSuggestedQuestions: getSuggestedQuestionsContract,
|
||||
getConversationHistory: getConversationHistoryContract,
|
||||
},
|
||||
app: { getChatFeedbacks: getChatAppFeedbacksContract },
|
||||
conversations: {
|
||||
getList: getConversationsListContract,
|
||||
delete: deleteConversationContract,
|
||||
rename: renameConversationContract,
|
||||
getVariables: getConversationVariablesContract,
|
||||
},
|
||||
audioToText: { audioToText: audioToTextContract },
|
||||
textToAudio: { textToAudioChat: textToAudioChatContract },
|
||||
info: { getChatApp: getChatAppInfoContract },
|
||||
parameters: { getChatApp: getChatAppParametersContract },
|
||||
meta: { getChatApp: getChatAppMetaContract },
|
||||
site: { getChatWebAppSettings: getChatWebAppSettingsContract },
|
||||
apps: {
|
||||
getAnnotationList: getAnnotationListContract,
|
||||
createAnnotation: createAnnotationContract,
|
||||
deleteAnnotation: deleteAnnotationContract,
|
||||
updateAnnotation: updateAnnotationContract,
|
||||
initialAnnotationReplySettings: initialAnnotationReplySettingsContract,
|
||||
getInitialAnnotationReplySettingsStatus: getInitialAnnotationReplySettingsStatusContract,
|
||||
},
|
||||
}
|
||||
|
||||
export type Router = typeof router
|
||||
@ -1,20 +0,0 @@
|
||||
// This file is auto-generated by @hey-api/openapi-ts
|
||||
|
||||
import { z } from 'zod'
|
||||
|
||||
import { zGetChatAppFeedbacksData, zGetChatAppFeedbacksResponse } from '../../zod/api/app'
|
||||
import { base } from '../common'
|
||||
|
||||
/**
|
||||
* Get feedbacks of application
|
||||
*
|
||||
* Get application's feedbacks.
|
||||
*/
|
||||
export const getChatAppFeedbacksContract = base.route({
|
||||
method: 'GET',
|
||||
path: '/app/feedbacks',
|
||||
operationId: 'getChatAppFeedbacks',
|
||||
summary: 'Get feedbacks of application',
|
||||
description: 'Get application\'s feedbacks.',
|
||||
tags: ['Feedback'],
|
||||
}).input(zGetChatAppFeedbacksData).output(z.object({ body: zGetChatAppFeedbacksResponse, status: z.literal(200) }))
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user