perf(web): lazy load console contract shards (#38230)

This commit is contained in:
Stephen Zhou 2026-06-30 22:15:29 +08:00 committed by GitHub
parent baf2191ee3
commit 4b17f968a6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
24 changed files with 484 additions and 396 deletions

View File

@ -1,103 +1,54 @@
// This file is auto-generated by @hey-api/openapi-ts
import { account } from './account/orpc.gen'
import { activate } from './activate/orpc.gen'
import { agent } from './agent/orpc.gen'
import { allWorkspaces } from './all-workspaces/orpc.gen'
import { apiBasedExtension } from './api-based-extension/orpc.gen'
import { apiKeyAuth } from './api-key-auth/orpc.gen'
import { appDslVersion } from './app-dsl-version/orpc.gen'
import { app } from './app/orpc.gen'
import { apps } from './apps/orpc.gen'
import { auth } from './auth/orpc.gen'
import { billing } from './billing/orpc.gen'
import { codeBasedExtension } from './code-based-extension/orpc.gen'
import { compliance } from './compliance/orpc.gen'
import { dataSource } from './data-source/orpc.gen'
import { datasets } from './datasets/orpc.gen'
import { emailCodeLogin } from './email-code-login/orpc.gen'
import { emailRegister } from './email-register/orpc.gen'
import { explore } from './explore/orpc.gen'
import { features } from './features/orpc.gen'
import { files } from './files/orpc.gen'
import { forgotPassword } from './forgot-password/orpc.gen'
import { form } from './form/orpc.gen'
import { info } from './info/orpc.gen'
import { installedApps } from './installed-apps/orpc.gen'
import { instructionGenerate } from './instruction-generate/orpc.gen'
import { login } from './login/orpc.gen'
import { logout } from './logout/orpc.gen'
import { notification } from './notification/orpc.gen'
import { notion } from './notion/orpc.gen'
import { oauth } from './oauth/orpc.gen'
import { rag } from './rag/orpc.gen'
import { refreshToken } from './refresh-token/orpc.gen'
import { remoteFiles } from './remote-files/orpc.gen'
import { resetPassword } from './reset-password/orpc.gen'
import { ruleCodeGenerate } from './rule-code-generate/orpc.gen'
import { ruleGenerate } from './rule-generate/orpc.gen'
import { ruleStructuredOutputGenerate } from './rule-structured-output-generate/orpc.gen'
import { snippets } from './snippets/orpc.gen'
import { spec } from './spec/orpc.gen'
import { systemFeatures } from './system-features/orpc.gen'
import { tagBindings } from './tag-bindings/orpc.gen'
import { tags } from './tags/orpc.gen'
import { test } from './test/orpc.gen'
import { trialApps } from './trial-apps/orpc.gen'
import { trialModels } from './trial-models/orpc.gen'
import { website } from './website/orpc.gen'
import { workflowGenerate } from './workflow-generate/orpc.gen'
import { workflow } from './workflow/orpc.gen'
import { workspaces } from './workspaces/orpc.gen'
export const contract = {
account,
activate,
agent,
allWorkspaces,
apiBasedExtension,
apiKeyAuth,
app,
appDslVersion,
apps,
auth,
billing,
codeBasedExtension,
compliance,
dataSource,
datasets,
emailCodeLogin,
emailRegister,
explore,
features,
files,
forgotPassword,
form,
info,
installedApps,
instructionGenerate,
login,
logout,
notification,
notion,
oauth,
rag,
refreshToken,
remoteFiles,
resetPassword,
ruleCodeGenerate,
ruleGenerate,
ruleStructuredOutputGenerate,
snippets,
spec,
systemFeatures,
tagBindings,
tags,
test,
trialApps,
trialModels,
website,
workflow,
workflowGenerate,
workspaces,
export const contractLoaders = {
account: () => import('./account/orpc.gen').then(({ account }) => ({ account })),
activate: () => import('./activate/orpc.gen').then(({ activate }) => ({ activate })),
agent: () => import('./agent/orpc.gen').then(({ agent }) => ({ agent })),
allWorkspaces: () => import('./all-workspaces/orpc.gen').then(({ allWorkspaces }) => ({ allWorkspaces })),
apiBasedExtension: () => import('./api-based-extension/orpc.gen').then(({ apiBasedExtension }) => ({ apiBasedExtension })),
apiKeyAuth: () => import('./api-key-auth/orpc.gen').then(({ apiKeyAuth }) => ({ apiKeyAuth })),
app: () => import('./app/orpc.gen').then(({ app }) => ({ app })),
appDslVersion: () => import('./app-dsl-version/orpc.gen').then(({ appDslVersion }) => ({ appDslVersion })),
apps: () => import('./apps/orpc.gen').then(({ apps }) => ({ apps })),
auth: () => import('./auth/orpc.gen').then(({ auth }) => ({ auth })),
billing: () => import('./billing/orpc.gen').then(({ billing }) => ({ billing })),
codeBasedExtension: () => import('./code-based-extension/orpc.gen').then(({ codeBasedExtension }) => ({ codeBasedExtension })),
compliance: () => import('./compliance/orpc.gen').then(({ compliance }) => ({ compliance })),
dataSource: () => import('./data-source/orpc.gen').then(({ dataSource }) => ({ dataSource })),
datasets: () => import('./datasets/orpc.gen').then(({ datasets }) => ({ datasets })),
emailCodeLogin: () => import('./email-code-login/orpc.gen').then(({ emailCodeLogin }) => ({ emailCodeLogin })),
emailRegister: () => import('./email-register/orpc.gen').then(({ emailRegister }) => ({ emailRegister })),
explore: () => import('./explore/orpc.gen').then(({ explore }) => ({ explore })),
features: () => import('./features/orpc.gen').then(({ features }) => ({ features })),
files: () => import('./files/orpc.gen').then(({ files }) => ({ files })),
forgotPassword: () => import('./forgot-password/orpc.gen').then(({ forgotPassword }) => ({ forgotPassword })),
form: () => import('./form/orpc.gen').then(({ form }) => ({ form })),
info: () => import('./info/orpc.gen').then(({ info }) => ({ info })),
installedApps: () => import('./installed-apps/orpc.gen').then(({ installedApps }) => ({ installedApps })),
instructionGenerate: () => import('./instruction-generate/orpc.gen').then(({ instructionGenerate }) => ({ instructionGenerate })),
login: () => import('./login/orpc.gen').then(({ login }) => ({ login })),
logout: () => import('./logout/orpc.gen').then(({ logout }) => ({ logout })),
notification: () => import('./notification/orpc.gen').then(({ notification }) => ({ notification })),
notion: () => import('./notion/orpc.gen').then(({ notion }) => ({ notion })),
oauth: () => import('./oauth/orpc.gen').then(({ oauth }) => ({ oauth })),
rag: () => import('./rag/orpc.gen').then(({ rag }) => ({ rag })),
refreshToken: () => import('./refresh-token/orpc.gen').then(({ refreshToken }) => ({ refreshToken })),
remoteFiles: () => import('./remote-files/orpc.gen').then(({ remoteFiles }) => ({ remoteFiles })),
resetPassword: () => import('./reset-password/orpc.gen').then(({ resetPassword }) => ({ resetPassword })),
ruleCodeGenerate: () => import('./rule-code-generate/orpc.gen').then(({ ruleCodeGenerate }) => ({ ruleCodeGenerate })),
ruleGenerate: () => import('./rule-generate/orpc.gen').then(({ ruleGenerate }) => ({ ruleGenerate })),
ruleStructuredOutputGenerate: () =>
import('./rule-structured-output-generate/orpc.gen').then(({ ruleStructuredOutputGenerate }) => ({ ruleStructuredOutputGenerate })),
snippets: () => import('./snippets/orpc.gen').then(({ snippets }) => ({ snippets })),
spec: () => import('./spec/orpc.gen').then(({ spec }) => ({ spec })),
systemFeatures: () => import('./system-features/orpc.gen').then(({ systemFeatures }) => ({ systemFeatures })),
tagBindings: () => import('./tag-bindings/orpc.gen').then(({ tagBindings }) => ({ tagBindings })),
tags: () => import('./tags/orpc.gen').then(({ tags }) => ({ tags })),
test: () => import('./test/orpc.gen').then(({ test }) => ({ test })),
trialApps: () => import('./trial-apps/orpc.gen').then(({ trialApps }) => ({ trialApps })),
trialModels: () => import('./trial-models/orpc.gen').then(({ trialModels }) => ({ trialModels })),
website: () => import('./website/orpc.gen').then(({ website }) => ({ website })),
workflow: () => import('./workflow/orpc.gen').then(({ workflow }) => ({ workflow })),
workflowGenerate: () => import('./workflow-generate/orpc.gen').then(({ workflowGenerate }) => ({ workflowGenerate })),
workspaces: () => import('./workspaces/orpc.gen').then(({ workspaces }) => ({ workspaces })),
}

View File

@ -344,16 +344,13 @@ const consoleContractEntryContent = (segments: string[]) => {
}
})
const imports = contracts
.map(contract => `import { ${contract.name} } from './${contract.importPath}/orpc.gen'`)
const contractEntries = contracts
.map(contract => ` ${contract.name}: () => import('./${contract.importPath}/orpc.gen').then(({ ${contract.name} }) => ({ ${contract.name} })),`)
.join('\n')
const contractEntries = contracts.map(contract => ` ${contract.name},`).join('\n')
return `// This file is auto-generated by @hey-api/openapi-ts
${imports}
export const contract = {
export const contractLoaders = {
${contractEntries}
}
`

View File

@ -1,5 +1,5 @@
import type { PluginsSearchParams } from './types'
import type { MarketPlaceInputs } from '@/contract/router'
import type { MarketPlaceInputs } from '@/contract/marketplace'
import { useInfiniteQuery, useQuery } from '@tanstack/react-query'
import { marketplaceQuery } from '@/service/client'
import { getMarketplaceCollectionsAndPlugins, getMarketplacePlugins } from './utils'

View File

@ -0,0 +1,13 @@
import { agent } from '@dify/contracts/api/console/agent/orpc.gen'
import { agentDriveContracts } from './agent-drive'
export const agentRouterContract = {
...agent,
byAgentId: {
...agent.byAgentId,
drive: {
...agent.byAgentId.drive,
...agentDriveContracts.byAgentId.drive,
},
},
}

View File

@ -1,8 +1,10 @@
import type { AppListResponse, WorkflowOnlineUsersResponse } from '@/models/app'
import type { CommonResponse } from '@/models/common'
import type { AppModeEnum } from '@/types/app'
import { apps } from '@dify/contracts/api/console/apps/orpc.gen'
import { type } from '@orpc/contract'
import { base } from '../base'
import { agentDriveContracts } from './agent-drive'
export type AppListSortBy = 'last_modified' | 'recently_created' | 'earliest_created'
type AppListMode = AppModeEnum | 'agent' | 'channel' | 'all'
@ -85,3 +87,24 @@ export const workflowOnlineUsersContract = base
}
}>())
.output(type<WorkflowOnlineUsersResponse>())
export const appsRouterContract = {
...apps,
list: appListContract,
deleteApp: appDeleteContract,
starredList: appStarredListContract,
star: appStarContract,
unstar: appUnstarContract,
workflowOnlineUsers: workflowOnlineUsersContract,
byAppId: {
...apps.byAppId,
agent: {
...apps.byAppId.agent,
...agentDriveContracts.byAppId.agent,
drive: {
...apps.byAppId.agent.drive,
...agentDriveContracts.byAppId.agent.drive,
},
},
},
}

View File

@ -1,3 +1,4 @@
import { billing } from '@dify/contracts/api/console/billing/orpc.gen'
import { type } from '@orpc/contract'
import { base } from '../base'
@ -23,3 +24,9 @@ export const bindPartnerStackContract = base
}
}>())
.output(type<unknown>())
export const billingRouterContract = {
...billing,
invoices: invoicesContract,
bindPartnerStack: bindPartnerStackContract,
}

View File

@ -5,6 +5,7 @@ import type { Banner } from '@/models/app'
import type { App, AppCategory, InstalledApp } from '@/models/explore'
import type { AppMeta } from '@/models/share'
import type { AppModeEnum } from '@/types/app'
import { explore } from '@dify/contracts/api/console/explore/orpc.gen'
import { type } from '@orpc/contract'
import { base } from '../base'
@ -142,3 +143,18 @@ export const exploreBannersContract = base
})
.input(type<{ query?: { language?: string } }>())
.output(type<Banner[]>())
export const exploreRouterContract = {
...explore,
apps: exploreAppsContract,
learnDifyApps: learnDifyAppsContract,
appDetail: exploreAppDetailContract,
installedApps: exploreInstalledAppsContract,
uninstallInstalledApp: exploreInstalledAppUninstallContract,
updateInstalledApp: exploreInstalledAppPinContract,
appAccessMode: exploreInstalledAppAccessModeContract,
updateAppAccessMode: exploreInstalledAppAccessModeUpdateContract,
installedAppParameters: exploreInstalledAppParametersContract,
installedAppMeta: exploreInstalledAppMetaContract,
banners: exploreBannersContract,
}

View File

@ -1,4 +1,5 @@
import type { PostFilesUploadResponse } from '@dify/contracts/api/console/files/types.gen'
import { files } from '@dify/contracts/api/console/files/orpc.gen'
import { type } from '@orpc/contract'
import { base } from '../base'
@ -14,3 +15,11 @@ export const fileUploadContract = base
}
}>())
.output(type<PostFilesUploadResponse>())
export const filesRouterContract = {
...files,
upload: {
...files.upload,
post: fileUploadContract,
},
}

View File

@ -31,3 +31,8 @@ export const changePreferredProviderTypeContract = base
}
}>())
.output(type<CommonResponse>())
export const modelProvidersRouterContract = {
models: modelProvidersModelsContract,
changePreferredProviderType: changePreferredProviderTypeContract,
}

View File

@ -25,3 +25,8 @@ export const pluginLatestVersionsContract = base
}
}>())
.output(type<InstalledLatestVersionResponse>())
export const pluginsRouterContract = {
checkInstalled: pluginCheckInstalledContract,
latestVersions: pluginLatestVersionsContract,
}

View File

@ -342,3 +342,31 @@ export const stopSnippetWorkflowTaskContract = base
}
}>())
.output(type<unknown>())
export const snippetsRouterContract = {
list: listCustomizedSnippetsContract,
create: createCustomizedSnippetContract,
detail: getCustomizedSnippetContract,
update: updateCustomizedSnippetContract,
delete: deleteCustomizedSnippetContract,
export: exportCustomizedSnippetContract,
import: importCustomizedSnippetContract,
confirmImport: confirmSnippetImportContract,
checkDependencies: checkSnippetDependenciesContract,
incrementUseCount: incrementSnippetUseCountContract,
draftWorkflow: getSnippetDraftWorkflowContract,
syncDraftWorkflow: syncSnippetDraftWorkflowContract,
draftConfig: getSnippetDraftConfigContract,
publishedWorkflow: getSnippetPublishedWorkflowContract,
publishWorkflow: publishSnippetWorkflowContract,
defaultBlockConfigs: getSnippetDefaultBlockConfigsContract,
workflowRuns: listSnippetWorkflowRunsContract,
workflowRunDetail: getSnippetWorkflowRunDetailContract,
workflowRunNodeExecutions: listSnippetWorkflowRunNodeExecutionsContract,
runDraftNode: runSnippetDraftNodeContract,
lastDraftNodeRun: getSnippetDraftNodeLastRunContract,
runDraftIterationNode: runSnippetDraftIterationNodeContract,
runDraftLoopNode: runSnippetDraftLoopNodeContract,
runDraftWorkflow: runSnippetDraftWorkflowContract,
stopWorkflowTask: stopSnippetWorkflowTaskContract,
}

View File

@ -1,3 +1,4 @@
import { tags } from '@dify/contracts/api/console/tags/orpc.gen'
import { type } from '@orpc/contract'
import { base } from '../base'
@ -89,3 +90,13 @@ export const tagBindingRemoveContract = base
}
}>())
.output(type<unknown>())
export const tagsRouterContract = {
...tags,
list: tagListContract,
create: tagCreateContract,
update: tagUpdateContract,
delete: tagDeleteContract,
bind: tagBindingCreateContract,
unbind: tagBindingRemoveContract,
}

View File

@ -117,3 +117,21 @@ export const triggerOAuthInitiateContract = base
.route({ path: '/workspaces/current/trigger-provider/{provider}/subscriptions/oauth/authorize', method: 'GET' })
.input(type<{ params: { provider: string } }>())
.output(type<{ authorization_url: string, subscription_builder: TriggerSubscriptionBuilder }>())
export const triggersRouterContract = {
list: triggersContract,
providerInfo: triggerProviderInfoContract,
subscriptions: triggerSubscriptionsContract,
subscriptionBuilderCreate: triggerSubscriptionBuilderCreateContract,
subscriptionBuilderUpdate: triggerSubscriptionBuilderUpdateContract,
subscriptionBuilderVerifyUpdate: triggerSubscriptionBuilderVerifyUpdateContract,
subscriptionVerify: triggerSubscriptionVerifyContract,
subscriptionBuild: triggerSubscriptionBuildContract,
subscriptionDelete: triggerSubscriptionDeleteContract,
subscriptionUpdate: triggerSubscriptionUpdateContract,
subscriptionBuilderLogs: triggerSubscriptionBuilderLogsContract,
oauthConfig: triggerOAuthConfigContract,
oauthConfigure: triggerOAuthConfigureContract,
oauthDelete: triggerOAuthDeleteContract,
oauthInitiate: triggerOAuthInitiateContract,
}

View File

@ -1,6 +1,7 @@
import type { ChatConfig } from '@/app/components/base/chat/types'
import type { DataSetListResponse } from '@/models/datasets'
import type { TryAppFlowPreview, TryAppInfo } from '@/models/try-app'
import { trialApps } from '@dify/contracts/api/console/trial-apps/orpc.gen'
import { type } from '@orpc/contract'
import { base } from '../base'
@ -54,3 +55,11 @@ export const trialAppParametersContract = base
}
}>())
.output(type<ChatConfig>())
export const trialAppsRouterContract = {
...trialApps,
info: trialAppInfoContract,
datasets: trialAppDatasetsContract,
parameters: trialAppParametersContract,
workflows: trialAppWorkflowsContract,
}

View File

@ -78,3 +78,10 @@ export const workflowDraftUpdateFeaturesContract = base
}
}>())
.output(type<CommonResponse>())
export const workflowDraftRouterContract = {
environmentVariables: workflowDraftEnvironmentVariablesContract,
updateEnvironmentVariables: workflowDraftUpdateEnvironmentVariablesContract,
updateConversationVariables: workflowDraftUpdateConversationVariablesContract,
updateFeatures: workflowDraftUpdateFeaturesContract,
}

View File

@ -1,3 +1,4 @@
import { workspaces } from '@dify/contracts/api/console/workspaces/orpc.gen'
import { type } from '@orpc/contract'
import { base } from '../base'
@ -54,3 +55,11 @@ export const workspaceSwitchContract = base
body: SwitchWorkspaceRequest
}>())
.output(type<SwitchWorkspaceResponse>())
export const workspacesRouterContract = {
...workspaces,
get: workspacesGetContract,
switch: {
post: workspaceSwitchContract,
},
}

View File

@ -1,10 +1,11 @@
import type { InferContractRouterInputs } from '@orpc/contract'
import type { CollectionsAndPluginsSearchParams, MarketplaceCollection, PluginsSearchParams } from '@/app/components/plugins/marketplace/types'
import type { Plugin, PluginsFromMarketplaceResponse } from '@/app/components/plugins/types'
import type { MarketplaceTemplate } from '@/types/marketplace-template'
import { type } from '@orpc/contract'
import { base } from './base'
export const collectionsContract = base
const collectionsContract = base
.route({
path: '/collections',
method: 'GET',
@ -22,7 +23,7 @@ export const collectionsContract = base
}>(),
)
export const collectionPluginsContract = base
const collectionPluginsContract = base
.route({
path: '/collections/{collectionId}/plugins',
method: 'POST',
@ -43,7 +44,7 @@ export const collectionPluginsContract = base
}>(),
)
export const searchAdvancedContract = base
const searchAdvancedContract = base
.route({
path: '/{kind}/search/advanced',
method: 'POST',
@ -56,7 +57,7 @@ export const searchAdvancedContract = base
}>())
.output(type<{ data: PluginsFromMarketplaceResponse }>())
export const templateDetailContract = base
const templateDetailContract = base
.route({
path: '/templates/{templateId}',
method: 'GET',
@ -68,7 +69,7 @@ export const templateDetailContract = base
}>())
.output(type<{ data: MarketplaceTemplate }>())
export const downloadPluginContract = base
const downloadPluginContract = base
.route({
path: '/plugins/{organization}/{pluginName}/{version}/download',
method: 'GET',
@ -81,3 +82,13 @@ export const downloadPluginContract = base
}
}>())
.output(type<Blob>())
export const marketplaceRouterContract = {
collections: collectionsContract,
collectionPlugins: collectionPluginsContract,
searchAdvanced: searchAdvancedContract,
templateDetail: templateDetailContract,
downloadPlugin: downloadPluginContract,
}
export type MarketPlaceInputs = InferContractRouterInputs<typeof marketplaceRouterContract>

View File

@ -1,249 +1,120 @@
import type { InferContractRouterInputs } from '@orpc/contract'
import { contract as communityContract } from '@dify/contracts/api/console/orpc.gen'
import { account } from '@dify/contracts/api/console/account/orpc.gen'
import { activate } from '@dify/contracts/api/console/activate/orpc.gen'
import { allWorkspaces } from '@dify/contracts/api/console/all-workspaces/orpc.gen'
import { apiBasedExtension } from '@dify/contracts/api/console/api-based-extension/orpc.gen'
import { apiKeyAuth } from '@dify/contracts/api/console/api-key-auth/orpc.gen'
import { appDslVersion } from '@dify/contracts/api/console/app-dsl-version/orpc.gen'
import { app } from '@dify/contracts/api/console/app/orpc.gen'
import { auth } from '@dify/contracts/api/console/auth/orpc.gen'
import { codeBasedExtension } from '@dify/contracts/api/console/code-based-extension/orpc.gen'
import { compliance } from '@dify/contracts/api/console/compliance/orpc.gen'
import { dataSource } from '@dify/contracts/api/console/data-source/orpc.gen'
import { datasets } from '@dify/contracts/api/console/datasets/orpc.gen'
import { emailCodeLogin } from '@dify/contracts/api/console/email-code-login/orpc.gen'
import { emailRegister } from '@dify/contracts/api/console/email-register/orpc.gen'
import { features } from '@dify/contracts/api/console/features/orpc.gen'
import { forgotPassword } from '@dify/contracts/api/console/forgot-password/orpc.gen'
import { form } from '@dify/contracts/api/console/form/orpc.gen'
import { info } from '@dify/contracts/api/console/info/orpc.gen'
import { installedApps } from '@dify/contracts/api/console/installed-apps/orpc.gen'
import { instructionGenerate } from '@dify/contracts/api/console/instruction-generate/orpc.gen'
import { login } from '@dify/contracts/api/console/login/orpc.gen'
import { logout } from '@dify/contracts/api/console/logout/orpc.gen'
import { notion } from '@dify/contracts/api/console/notion/orpc.gen'
import { oauth } from '@dify/contracts/api/console/oauth/orpc.gen'
import { rag } from '@dify/contracts/api/console/rag/orpc.gen'
import { refreshToken } from '@dify/contracts/api/console/refresh-token/orpc.gen'
import { remoteFiles } from '@dify/contracts/api/console/remote-files/orpc.gen'
import { resetPassword } from '@dify/contracts/api/console/reset-password/orpc.gen'
import { ruleCodeGenerate } from '@dify/contracts/api/console/rule-code-generate/orpc.gen'
import { ruleGenerate } from '@dify/contracts/api/console/rule-generate/orpc.gen'
import { ruleStructuredOutputGenerate } from '@dify/contracts/api/console/rule-structured-output-generate/orpc.gen'
import { spec } from '@dify/contracts/api/console/spec/orpc.gen'
import { systemFeatures } from '@dify/contracts/api/console/system-features/orpc.gen'
import { tagBindings } from '@dify/contracts/api/console/tag-bindings/orpc.gen'
import { test } from '@dify/contracts/api/console/test/orpc.gen'
import { trialModels } from '@dify/contracts/api/console/trial-models/orpc.gen'
import { website } from '@dify/contracts/api/console/website/orpc.gen'
import { workflowGenerate } from '@dify/contracts/api/console/workflow-generate/orpc.gen'
import { workflow } from '@dify/contracts/api/console/workflow/orpc.gen'
import { contract as enterpriseContract } from '@dify/contracts/enterprise/orpc.gen'
import { rbacAccessConfigContract } from './console/access-control'
import { agentDriveContracts } from './console/agent-drive'
import {
appDeleteContract,
appListContract,
appStarContract,
appStarredListContract,
appUnstarContract,
workflowOnlineUsersContract,
} from './console/apps'
import { bindPartnerStackContract, invoicesContract } from './console/billing'
import {
exploreAppDetailContract,
exploreAppsContract,
exploreBannersContract,
exploreInstalledAppAccessModeContract,
exploreInstalledAppAccessModeUpdateContract,
exploreInstalledAppMetaContract,
exploreInstalledAppParametersContract,
exploreInstalledAppPinContract,
exploreInstalledAppsContract,
exploreInstalledAppUninstallContract,
learnDifyAppsContract,
} from './console/explore'
import { fileUploadContract } from './console/files'
import { changePreferredProviderTypeContract, modelProvidersModelsContract } from './console/model-providers'
import { agentRouterContract } from './console/agent'
import { appsRouterContract } from './console/apps'
import { billingRouterContract } from './console/billing'
import { exploreRouterContract } from './console/explore'
import { filesRouterContract } from './console/files'
import { modelProvidersRouterContract } from './console/model-providers'
import { notificationContract, notificationDismissContract } from './console/notification'
import { pluginCheckInstalledContract, pluginLatestVersionsContract } from './console/plugins'
import {
checkSnippetDependenciesContract,
confirmSnippetImportContract,
createCustomizedSnippetContract,
deleteCustomizedSnippetContract,
exportCustomizedSnippetContract,
getCustomizedSnippetContract,
getSnippetDefaultBlockConfigsContract,
getSnippetDraftConfigContract,
getSnippetDraftNodeLastRunContract,
getSnippetDraftWorkflowContract,
getSnippetPublishedWorkflowContract,
getSnippetWorkflowRunDetailContract,
importCustomizedSnippetContract,
incrementSnippetUseCountContract,
listCustomizedSnippetsContract,
listSnippetWorkflowRunNodeExecutionsContract,
listSnippetWorkflowRunsContract,
publishSnippetWorkflowContract,
runSnippetDraftIterationNodeContract,
runSnippetDraftLoopNodeContract,
runSnippetDraftNodeContract,
runSnippetDraftWorkflowContract,
stopSnippetWorkflowTaskContract,
syncSnippetDraftWorkflowContract,
updateCustomizedSnippetContract,
} from './console/snippets'
import {
tagBindingCreateContract,
tagBindingRemoveContract,
tagCreateContract,
tagDeleteContract,
tagListContract,
tagUpdateContract,
} from './console/tags'
import {
triggerOAuthConfigContract,
triggerOAuthConfigureContract,
triggerOAuthDeleteContract,
triggerOAuthInitiateContract,
triggerProviderInfoContract,
triggersContract,
triggerSubscriptionBuildContract,
triggerSubscriptionBuilderCreateContract,
triggerSubscriptionBuilderLogsContract,
triggerSubscriptionBuilderUpdateContract,
triggerSubscriptionBuilderVerifyUpdateContract,
triggerSubscriptionDeleteContract,
triggerSubscriptionsContract,
triggerSubscriptionUpdateContract,
triggerSubscriptionVerifyContract,
} from './console/trigger'
import { trialAppDatasetsContract, trialAppInfoContract, trialAppParametersContract, trialAppWorkflowsContract } from './console/try-app'
import {
workflowDraftEnvironmentVariablesContract,
workflowDraftUpdateConversationVariablesContract,
workflowDraftUpdateEnvironmentVariablesContract,
workflowDraftUpdateFeaturesContract,
} from './console/workflow'
import { pluginsRouterContract } from './console/plugins'
import { snippetsRouterContract } from './console/snippets'
import { tagsRouterContract } from './console/tags'
import { triggersRouterContract } from './console/trigger'
import { trialAppsRouterContract } from './console/try-app'
import { workflowDraftRouterContract } from './console/workflow'
import { workflowCommentContracts } from './console/workflow-comment'
import { workspacesGetContract, workspaceSwitchContract } from './console/workspaces'
import { collectionPluginsContract, collectionsContract, downloadPluginContract, searchAdvancedContract, templateDetailContract } from './marketplace'
import { workspacesRouterContract } from './console/workspaces'
export const marketplaceRouterContract = {
collections: collectionsContract,
collectionPlugins: collectionPluginsContract,
searchAdvanced: searchAdvancedContract,
templateDetail: templateDetailContract,
downloadPlugin: downloadPluginContract,
const communityContract = {
account,
activate,
allWorkspaces,
apiBasedExtension,
apiKeyAuth,
app,
appDslVersion,
auth,
codeBasedExtension,
compliance,
dataSource,
datasets,
emailCodeLogin,
emailRegister,
features,
forgotPassword,
form,
info,
installedApps,
instructionGenerate,
login,
logout,
notion,
oauth,
rag,
refreshToken,
remoteFiles,
resetPassword,
ruleCodeGenerate,
ruleGenerate,
ruleStructuredOutputGenerate,
spec,
systemFeatures,
tagBindings,
test,
trialModels,
website,
workflow,
workflowGenerate,
}
export type MarketPlaceInputs = InferContractRouterInputs<typeof marketplaceRouterContract>
export const consoleRouterContract = {
enterprise: enterpriseContract,
...communityContract,
apps: {
...communityContract.apps,
list: appListContract,
deleteApp: appDeleteContract,
starredList: appStarredListContract,
star: appStarContract,
unstar: appUnstarContract,
workflowOnlineUsers: workflowOnlineUsersContract,
byAppId: {
...communityContract.apps.byAppId,
agent: {
...communityContract.apps.byAppId.agent,
...agentDriveContracts.byAppId.agent,
drive: {
...communityContract.apps.byAppId.agent.drive,
...agentDriveContracts.byAppId.agent.drive,
},
},
},
},
agent: {
...communityContract.agent,
byAgentId: {
...communityContract.agent.byAgentId,
drive: {
...communityContract.agent.byAgentId.drive,
...agentDriveContracts.byAgentId.drive,
},
},
},
explore: {
...communityContract.explore,
apps: exploreAppsContract,
learnDifyApps: learnDifyAppsContract,
appDetail: exploreAppDetailContract,
installedApps: exploreInstalledAppsContract,
uninstallInstalledApp: exploreInstalledAppUninstallContract,
updateInstalledApp: exploreInstalledAppPinContract,
appAccessMode: exploreInstalledAppAccessModeContract,
updateAppAccessMode: exploreInstalledAppAccessModeUpdateContract,
installedAppParameters: exploreInstalledAppParametersContract,
installedAppMeta: exploreInstalledAppMetaContract,
banners: exploreBannersContract,
},
trialApps: {
...communityContract.trialApps,
info: trialAppInfoContract,
datasets: trialAppDatasetsContract,
parameters: trialAppParametersContract,
workflows: trialAppWorkflowsContract,
},
files: {
...communityContract.files,
upload: {
...communityContract.files.upload,
post: fileUploadContract,
},
},
modelProviders: {
models: modelProvidersModelsContract,
changePreferredProviderType: changePreferredProviderTypeContract,
},
plugins: {
checkInstalled: pluginCheckInstalledContract,
latestVersions: pluginLatestVersionsContract,
},
rbacAccessConfig: rbacAccessConfigContract,
snippets: {
list: listCustomizedSnippetsContract,
create: createCustomizedSnippetContract,
detail: getCustomizedSnippetContract,
update: updateCustomizedSnippetContract,
delete: deleteCustomizedSnippetContract,
export: exportCustomizedSnippetContract,
import: importCustomizedSnippetContract,
confirmImport: confirmSnippetImportContract,
checkDependencies: checkSnippetDependenciesContract,
incrementUseCount: incrementSnippetUseCountContract,
draftWorkflow: getSnippetDraftWorkflowContract,
syncDraftWorkflow: syncSnippetDraftWorkflowContract,
draftConfig: getSnippetDraftConfigContract,
publishedWorkflow: getSnippetPublishedWorkflowContract,
publishWorkflow: publishSnippetWorkflowContract,
defaultBlockConfigs: getSnippetDefaultBlockConfigsContract,
workflowRuns: listSnippetWorkflowRunsContract,
workflowRunDetail: getSnippetWorkflowRunDetailContract,
workflowRunNodeExecutions: listSnippetWorkflowRunNodeExecutionsContract,
runDraftNode: runSnippetDraftNodeContract,
lastDraftNodeRun: getSnippetDraftNodeLastRunContract,
runDraftIterationNode: runSnippetDraftIterationNodeContract,
runDraftLoopNode: runSnippetDraftLoopNodeContract,
runDraftWorkflow: runSnippetDraftWorkflowContract,
stopWorkflowTask: stopSnippetWorkflowTaskContract,
},
billing: {
...communityContract.billing,
invoices: invoicesContract,
bindPartnerStack: bindPartnerStackContract,
},
workflowDraft: {
environmentVariables: workflowDraftEnvironmentVariablesContract,
updateEnvironmentVariables: workflowDraftUpdateEnvironmentVariablesContract,
updateConversationVariables: workflowDraftUpdateConversationVariablesContract,
updateFeatures: workflowDraftUpdateFeaturesContract,
},
workflowComments: workflowCommentContracts,
agent: agentRouterContract,
apps: appsRouterContract,
billing: billingRouterContract,
explore: exploreRouterContract,
files: filesRouterContract,
modelProviders: modelProvidersRouterContract,
notification: notificationContract,
notificationDismiss: notificationDismissContract,
tags: {
...communityContract.tags,
list: tagListContract,
create: tagCreateContract,
update: tagUpdateContract,
delete: tagDeleteContract,
bind: tagBindingCreateContract,
unbind: tagBindingRemoveContract,
},
triggers: {
list: triggersContract,
providerInfo: triggerProviderInfoContract,
subscriptions: triggerSubscriptionsContract,
subscriptionBuilderCreate: triggerSubscriptionBuilderCreateContract,
subscriptionBuilderUpdate: triggerSubscriptionBuilderUpdateContract,
subscriptionBuilderVerifyUpdate: triggerSubscriptionBuilderVerifyUpdateContract,
subscriptionVerify: triggerSubscriptionVerifyContract,
subscriptionBuild: triggerSubscriptionBuildContract,
subscriptionDelete: triggerSubscriptionDeleteContract,
subscriptionUpdate: triggerSubscriptionUpdateContract,
subscriptionBuilderLogs: triggerSubscriptionBuilderLogsContract,
oauthConfig: triggerOAuthConfigContract,
oauthConfigure: triggerOAuthConfigureContract,
oauthDelete: triggerOAuthDeleteContract,
oauthInitiate: triggerOAuthInitiateContract,
},
workspaces: {
...communityContract.workspaces,
get: workspacesGetContract,
switch: {
post: workspaceSwitchContract,
},
},
plugins: pluginsRouterContract,
rbacAccessConfig: rbacAccessConfigContract,
snippets: snippetsRouterContract,
tags: tagsRouterContract,
triggers: triggersRouterContract,
trialApps: trialAppsRouterContract,
workflowComments: workflowCommentContracts,
workflowDraft: workflowDraftRouterContract,
workspaces: workspacesRouterContract,
}

View File

@ -2,20 +2,21 @@
import type { ContractRouterClient } from '@orpc/contract'
import type { JsonifiedClient } from '@orpc/openapi-client'
import type { ConsoleRouterContract } from '@/service/console-link'
import { createORPCClient } from '@orpc/client'
import { OpenAPILink } from '@orpc/openapi-client/fetch'
import { createTanstackQueryUtils } from '@orpc/tanstack-query'
import { API_PREFIX } from '@/config'
import { consoleRouterContract } from '@/contract/router'
// eslint-disable-next-line no-restricted-imports
import { request } from '@/service/base'
import { getBaseURL } from '@/service/client'
import { createConsoleDynamicLink } from '@/service/console-link'
type AgentConfigureConsoleClientContext = {
silent?: boolean
}
const agentConfigureConsoleLink = new OpenAPILink<AgentConfigureConsoleClientContext>(consoleRouterContract, {
const agentConfigureConsoleLink = createConsoleDynamicLink<AgentConfigureConsoleClientContext>(contract => new OpenAPILink<AgentConfigureConsoleClientContext>(contract, {
url: getBaseURL(API_PREFIX),
fetch: (input, init, options) => {
return request(
@ -28,9 +29,9 @@ const agentConfigureConsoleLink = new OpenAPILink<AgentConfigureConsoleClientCon
},
)
},
})
}))
const agentConfigureConsoleClient: JsonifiedClient<ContractRouterClient<typeof consoleRouterContract, AgentConfigureConsoleClientContext>>
const agentConfigureConsoleClient: JsonifiedClient<ContractRouterClient<ConsoleRouterContract, AgentConfigureConsoleClientContext>>
= createORPCClient(agentConfigureConsoleLink)
export const agentConfigureConsoleQuery = createTanstackQueryUtils(agentConfigureConsoleClient, {

View File

@ -38,7 +38,6 @@ const config: KnipConfig = {
'i18n-config/locale-resources/*.ts',
],
ignoreBinaries: [
'only-allow',
'pbcopy',
'which',
],

View File

@ -5,11 +5,13 @@ import type {
ListReleasesResponse,
PrecheckReleaseRequest,
} from '@dify/contracts/enterprise/types.gen'
import type { ContractRouterClient } from '@orpc/contract'
import type { ClientLink } from '@orpc/client'
import type { AnyContractRouter, ContractRouterClient } from '@orpc/contract'
import type { JsonifiedClient } from '@orpc/openapi-client'
import type { RouterUtils } from '@orpc/tanstack-query'
import type { InfiniteData, QueryClient, QueryKey } from '@tanstack/react-query'
import type { Tag } from '@/contract/console/tags'
import type { consoleRouterContract } from '@/contract/router'
import { createORPCClient, onError } from '@orpc/client'
import { OpenAPILink } from '@orpc/openapi-client/fetch'
import { createTanstackQueryUtils } from '@orpc/tanstack-query'
@ -19,13 +21,11 @@ import {
IS_MARKETPLACE,
MARKETPLACE_API_PREFIX,
} from '@/config'
import {
consoleRouterContract,
marketplaceRouterContract,
} from '@/contract/router'
import { marketplaceRouterContract } from '@/contract/marketplace'
import { isClient } from '@/utils/client'
// eslint-disable-next-line no-restricted-imports
import { request } from './base'
import { createConsoleDynamicLink } from './console-link'
function getMarketplaceHeaders() {
return new Headers({
@ -58,6 +58,30 @@ export function getBaseURL(path: string) {
return url
}
type ConsoleClientContext = Record<never, never>
type ConsoleClientLink = ClientLink<ConsoleClientContext>
function createConsoleOpenAPILink(contract: AnyContractRouter): ConsoleClientLink {
return new OpenAPILink<ConsoleClientContext>(contract, {
url: getBaseURL(API_PREFIX),
fetch: (input, init) => {
return request(
input.url,
init,
{
fetchCompat: true,
request: input,
},
)
},
interceptors: [
onError((error) => {
console.error(error)
}),
],
})
}
const marketplaceLink = new OpenAPILink(marketplaceRouterContract, {
url: MARKETPLACE_API_PREFIX,
headers: () => (getMarketplaceHeaders()),
@ -323,24 +347,7 @@ async function invalidateReleaseMutationQueries(
])
}
const consoleLink = new OpenAPILink(consoleRouterContract, {
url: getBaseURL(API_PREFIX),
fetch: (input, init) => {
return request(
input.url,
init,
{
fetchCompat: true,
request: input,
},
)
},
interceptors: [
onError((error) => {
console.error(error)
}),
],
})
const consoleLink = createConsoleDynamicLink<ConsoleClientContext>(createConsoleOpenAPILink)
export const consoleClient: JsonifiedClient<ContractRouterClient<typeof consoleRouterContract>> = createORPCClient(consoleLink)

View File

@ -0,0 +1,32 @@
import type { ClientContext, ClientLink } from '@orpc/client'
import type { AnyContractRouter } from '@orpc/contract'
import type { consoleRouterContract } from '@/contract/router'
import { DynamicLink } from '@orpc/client'
import { loadConsoleContractForSegment } from './console-router-loader'
export type ConsoleRouterContract = typeof consoleRouterContract
export function createConsoleDynamicLink<TContext extends ClientContext>(
createLink: (contract: AnyContractRouter) => ClientLink<TContext>,
) {
const routerLinkPromises = new Map<string, Promise<ClientLink<TContext>>>()
function getRouterLink(path: readonly string[]) {
const segment = path[0]
if (!segment)
throw new Error('Console contract path is empty.')
let routerLinkPromise = routerLinkPromises.get(segment)
if (!routerLinkPromise) {
routerLinkPromise = loadConsoleContractForSegment(segment).then(createLink).catch((error) => {
routerLinkPromises.delete(segment)
throw error
})
routerLinkPromises.set(segment, routerLinkPromise)
}
return routerLinkPromise
}
return new DynamicLink<TContext>((_options, path) => getRouterLink(path))
}

View File

@ -0,0 +1,51 @@
import type { AnyContractRouter } from '@orpc/contract'
import { contractLoaders } from '@dify/contracts/api/console/orpc.gen'
const wrapConsoleContract = (segment: string, contract: unknown) => ({ [segment]: contract }) as AnyContractRouter
async function loadGeneratedConsoleContract(segment: string) {
const loader = contractLoaders[segment as keyof typeof contractLoaders]
if (!loader)
return null
return loader() as Promise<AnyContractRouter>
}
const customConsoleContractLoaders: Record<string, () => Promise<AnyContractRouter>> = {
agent: () => import('@/contract/console/agent').then(({ agentRouterContract }) => wrapConsoleContract('agent', agentRouterContract)),
apps: () => import('@/contract/console/apps').then(({ appsRouterContract }) => wrapConsoleContract('apps', appsRouterContract)),
billing: () => import('@/contract/console/billing').then(({ billingRouterContract }) => wrapConsoleContract('billing', billingRouterContract)),
enterprise: () => import('@dify/contracts/enterprise/orpc.gen').then(({ contract }) => wrapConsoleContract('enterprise', contract)),
explore: () => import('@/contract/console/explore').then(({ exploreRouterContract }) => wrapConsoleContract('explore', exploreRouterContract)),
files: () => import('@/contract/console/files').then(({ filesRouterContract }) => wrapConsoleContract('files', filesRouterContract)),
modelProviders: () =>
import('@/contract/console/model-providers').then(({ modelProvidersRouterContract }) => wrapConsoleContract('modelProviders', modelProvidersRouterContract)),
notification: () =>
import('@/contract/console/notification').then(({ notificationContract }) => wrapConsoleContract('notification', notificationContract)),
notificationDismiss: () =>
import('@/contract/console/notification').then(({ notificationDismissContract }) => wrapConsoleContract('notificationDismiss', notificationDismissContract)),
plugins: () => import('@/contract/console/plugins').then(({ pluginsRouterContract }) => wrapConsoleContract('plugins', pluginsRouterContract)),
rbacAccessConfig: () =>
import('@/contract/console/access-control').then(({ rbacAccessConfigContract }) => wrapConsoleContract('rbacAccessConfig', rbacAccessConfigContract)),
snippets: () => import('@/contract/console/snippets').then(({ snippetsRouterContract }) => wrapConsoleContract('snippets', snippetsRouterContract)),
tags: () => import('@/contract/console/tags').then(({ tagsRouterContract }) => wrapConsoleContract('tags', tagsRouterContract)),
triggers: () => import('@/contract/console/trigger').then(({ triggersRouterContract }) => wrapConsoleContract('triggers', triggersRouterContract)),
trialApps: () => import('@/contract/console/try-app').then(({ trialAppsRouterContract }) => wrapConsoleContract('trialApps', trialAppsRouterContract)),
workflowComments: () =>
import('@/contract/console/workflow-comment').then(({ workflowCommentContracts }) => wrapConsoleContract('workflowComments', workflowCommentContracts)),
workflowDraft: () =>
import('@/contract/console/workflow').then(({ workflowDraftRouterContract }) => wrapConsoleContract('workflowDraft', workflowDraftRouterContract)),
workspaces: () => import('@/contract/console/workspaces').then(({ workspacesRouterContract }) => wrapConsoleContract('workspaces', workspacesRouterContract)),
}
export async function loadConsoleContractForSegment(segment: string) {
const customContractLoader = customConsoleContractLoaders[segment]
if (customContractLoader)
return customContractLoader()
const generatedContract = await loadGeneratedConsoleContract(segment)
if (generatedContract)
return generatedContract
throw new Error(`Console contract segment "${segment}" is not configured.`)
}

View File

@ -1,5 +1,7 @@
import type { ContractRouterClient } from '@orpc/contract'
import type { ClientLink } from '@orpc/client'
import type { AnyContractRouter, ContractRouterClient } from '@orpc/contract'
import type { JsonifiedClient } from '@orpc/openapi-client'
import type { consoleRouterContract } from '@/contract/router'
import { createORPCClient, onError } from '@orpc/client'
import { OpenAPILink } from '@orpc/openapi-client/fetch'
import { createTanstackQueryUtils } from '@orpc/tanstack-query'
@ -9,7 +11,7 @@ import {
CSRF_HEADER_NAME,
} from '@/config'
import { SERVER_CONSOLE_API_PREFIX } from '@/config/server'
import { consoleRouterContract } from '@/contract/router'
import { createConsoleDynamicLink } from './console-link'
import 'server-only'
@ -68,6 +70,29 @@ const createServerConsoleRequestHeaders = (context: ServerConsoleClientContext |
return requestHeaders
}
type ServerConsoleClientLink = ClientLink<ServerConsoleClientContext>
function createServerConsoleOpenAPILink(contract: AnyContractRouter): ServerConsoleClientLink {
return new OpenAPILink<ServerConsoleClientContext>(contract, {
url: getServerConsoleApiPrefix,
headers: ({ context }) => createServerConsoleRequestHeaders(context),
fetch: (request, init) => {
if (request.body && !request.headers.has('content-type'))
request.headers.set('Content-Type', 'application/json')
return globalThis.fetch(request, {
...init,
cache: 'no-store',
})
},
interceptors: [
onError((error) => {
console.error(error)
}),
],
})
}
export const getServerConsoleClientContext = async (): Promise<ServerConsoleClientContext> => {
const { cookies, headers } = await import('@/next/headers')
const requestHeaders = await headers()
@ -82,24 +107,7 @@ export const getServerConsoleClientContext = async (): Promise<ServerConsoleClie
export const getServerConsoleRequestHeaders = async () =>
createServerConsoleRequestHeaders(await getServerConsoleClientContext())
const serverConsoleLink = new OpenAPILink<ServerConsoleClientContext>(consoleRouterContract, {
url: getServerConsoleApiPrefix,
headers: ({ context }) => createServerConsoleRequestHeaders(context),
fetch: (request, init) => {
if (request.body && !request.headers.has('content-type'))
request.headers.set('Content-Type', 'application/json')
return globalThis.fetch(request, {
...init,
cache: 'no-store',
})
},
interceptors: [
onError((error) => {
console.error(error)
}),
],
})
const serverConsoleLink = createConsoleDynamicLink<ServerConsoleClientContext>(createServerConsoleOpenAPILink)
export const serverConsoleClient: JsonifiedClient<ContractRouterClient<typeof consoleRouterContract, ServerConsoleClientContext>> = createORPCClient(serverConsoleLink)