Merge branch 'feat/trigger' of https://github.com/langgenius/dify into feat/trigger

This commit is contained in:
lyzno1 2025-10-30 15:35:39 +08:00
commit bed2ce69bb
No known key found for this signature in database
7 changed files with 111 additions and 125 deletions

View File

@ -2,7 +2,6 @@
import ActionButton from '@/app/components/base/action-button' import ActionButton from '@/app/components/base/action-button'
import Loading from '@/app/components/base/loading' import Loading from '@/app/components/base/loading'
import { Markdown } from '@/app/components/base/markdown' import { Markdown } from '@/app/components/base/markdown'
import Modal from '@/app/components/base/modal'
import { useLanguage } from '@/app/components/header/account-setting/model-provider-page/hooks' import { useLanguage } from '@/app/components/header/account-setting/model-provider-page/hooks'
import { usePluginReadme } from '@/service/use-plugins' import { usePluginReadme } from '@/service/use-plugins'
import cn from '@/utils/classnames' import cn from '@/utils/classnames'
@ -85,29 +84,36 @@ const ReadmePanel: FC = () => {
</div> </div>
) )
return showType === ReadmeShowType.drawer ? createPortal( const portalContent = showType === ReadmeShowType.drawer
<div className='pointer-events-none fixed inset-0 z-[9997] flex justify-start'> ? (
<div <div className='pointer-events-none fixed inset-0 z-[9997] flex justify-start'>
className={cn( <div
'pointer-events-auto mb-2 ml-2 mr-2 mt-16 w-[600px] max-w-[600px] justify-start rounded-2xl border-[0.5px] border-components-panel-border bg-components-panel-bg p-0 shadow-xl', className={cn(
)} 'pointer-events-auto mb-2 ml-2 mr-2 mt-16 w-[600px] max-w-[600px] justify-start rounded-2xl border-[0.5px] border-components-panel-border bg-components-panel-bg p-0 shadow-xl',
> )}
{children} >
{children}
</div>
</div> </div>
</div>, )
: (
<div className='pointer-events-none fixed inset-0 z-[9997] flex items-center justify-center p-2'>
<div
className={cn(
'pointer-events-auto relative h-[calc(100vh-16px)] w-full max-w-[800px] rounded-2xl bg-components-panel-bg p-0 shadow-xl',
)}
onClick={(event) => {
event.stopPropagation()
}}
>
{children}
</div>
</div>
)
return createPortal(
portalContent,
document.body, document.body,
) : (
<Modal
isShow={!!detail}
onClose={onClose}
overlayOpacity={true}
className='h-[calc(100vh-16px)] max-w-[800px] p-0'
wrapperClassName='!z-[102]'
containerClassName='p-2'
clickOutsideNotClose={true}
>
{children}
</Modal>
) )
} }

View File

@ -1,5 +1,17 @@
import { useStore as useAppStore } from '@/app/components/app/store' import { useStore as useAppStore } from '@/app/components/app/store'
import { Stop } from '@/app/components/base/icons/src/vender/line/mediaAndDevices'
import Tooltip from '@/app/components/base/tooltip' import Tooltip from '@/app/components/base/tooltip'
import { useLanguage } from '@/app/components/header/account-setting/model-provider-page/hooks'
import {
AuthCategory,
AuthorizedInDataSourceNode,
AuthorizedInNode,
PluginAuth,
PluginAuthInDataSourceNode,
} from '@/app/components/plugins/plugin-auth'
import { usePluginStore } from '@/app/components/plugins/plugin-detail-panel/store'
import type { SimpleSubscription } from '@/app/components/plugins/plugin-detail-panel/subscription-list'
import { ReadmeEntrance } from '@/app/components/plugins/readme-panel/entrance'
import BlockIcon from '@/app/components/workflow/block-icon' import BlockIcon from '@/app/components/workflow/block-icon'
import { import {
WorkflowHistoryEvent, WorkflowHistoryEvent,
@ -11,7 +23,14 @@ import {
useToolIcon, useToolIcon,
useWorkflowHistory, useWorkflowHistory,
} from '@/app/components/workflow/hooks' } from '@/app/components/workflow/hooks'
import { useHooksStore } from '@/app/components/workflow/hooks-store'
import useInspectVarsCrud from '@/app/components/workflow/hooks/use-inspect-vars-crud'
import Split from '@/app/components/workflow/nodes/_base/components/split' import Split from '@/app/components/workflow/nodes/_base/components/split'
import DataSourceBeforeRunForm from '@/app/components/workflow/nodes/data-source/before-run-form'
import type { CustomRunFormProps } from '@/app/components/workflow/nodes/data-source/types'
import { DataSourceClassification } from '@/app/components/workflow/nodes/data-source/types'
import { useLogs } from '@/app/components/workflow/run/hooks'
import SpecialResultPanel from '@/app/components/workflow/run/special-result-panel'
import { useStore } from '@/app/components/workflow/store' import { useStore } from '@/app/components/workflow/store'
import { BlockEnum, type Node, NodeRunningStatus } from '@/app/components/workflow/types' import { BlockEnum, type Node, NodeRunningStatus } from '@/app/components/workflow/types'
import { import {
@ -20,16 +39,18 @@ import {
hasRetryNode, hasRetryNode,
isSupportCustomRunForm, isSupportCustomRunForm,
} from '@/app/components/workflow/utils' } from '@/app/components/workflow/utils'
import { useModalContext } from '@/context/modal-context'
import { useAllBuiltInTools } from '@/service/use-tools'
import { useAllTriggerPlugins } from '@/service/use-triggers' import { useAllTriggerPlugins } from '@/service/use-triggers'
import { FlowType } from '@/types/common'
import { canFindTool } from '@/utils'
import cn from '@/utils/classnames' import cn from '@/utils/classnames'
import { import {
RiCloseLine, RiCloseLine,
RiPlayLargeLine, RiPlayLargeLine,
} from '@remixicon/react' } from '@remixicon/react'
import type { import { debounce } from 'lodash-es'
FC, import type { FC, ReactNode } from 'react'
ReactNode,
} from 'react'
import React, { import React, {
cloneElement, cloneElement,
memo, memo,
@ -42,44 +63,18 @@ import React, {
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { useShallow } from 'zustand/react/shallow' import { useShallow } from 'zustand/react/shallow'
import { useResizePanel } from '../../hooks/use-resize-panel' import { useResizePanel } from '../../hooks/use-resize-panel'
import BeforeRunForm from '../before-run-form'
import PanelWrap from '../before-run-form/panel-wrap'
import ErrorHandleOnPanel from '../error-handle/error-handle-on-panel' import ErrorHandleOnPanel from '../error-handle/error-handle-on-panel'
import HelpLink from '../help-link' import HelpLink from '../help-link'
import NextStep from '../next-step' import NextStep from '../next-step'
import PanelOperator from '../panel-operator' import PanelOperator from '../panel-operator'
import RetryOnPanel from '../retry/retry-on-panel' import RetryOnPanel from '../retry/retry-on-panel'
import { import { DescriptionInput, TitleInput } from '../title-description-input'
DescriptionInput,
TitleInput,
} from '../title-description-input'
import Tab, { TabType } from './tab'
// import AuthMethodSelector from '@/app/components/workflow/nodes/trigger-plugin/components/auth-method-selector'
import { Stop } from '@/app/components/base/icons/src/vender/line/mediaAndDevices'
import {
AuthCategory,
AuthorizedInDataSourceNode,
AuthorizedInNode,
PluginAuth,
PluginAuthInDataSourceNode,
} from '@/app/components/plugins/plugin-auth'
import type { SimpleSubscription } from '@/app/components/plugins/plugin-detail-panel/subscription-list'
import { useHooksStore } from '@/app/components/workflow/hooks-store'
import useInspectVarsCrud from '@/app/components/workflow/hooks/use-inspect-vars-crud'
import DataSourceBeforeRunForm from '@/app/components/workflow/nodes/data-source/before-run-form'
import type { CustomRunFormProps } from '@/app/components/workflow/nodes/data-source/types'
import { DataSourceClassification } from '@/app/components/workflow/nodes/data-source/types'
import { useLogs } from '@/app/components/workflow/run/hooks'
import SpecialResultPanel from '@/app/components/workflow/run/special-result-panel'
import { useModalContext } from '@/context/modal-context'
import { FlowType } from '@/types/common'
import { canFindTool } from '@/utils'
import { debounce } from 'lodash-es'
import BeforeRunForm from '../before-run-form'
import PanelWrap from '../before-run-form/panel-wrap'
import LastRun from './last-run' import LastRun from './last-run'
import useLastRun from './last-run/use-last-run' import useLastRun from './last-run/use-last-run'
import Tab, { TabType } from './tab'
import { TriggerSubscription } from './trigger-subscription' import { TriggerSubscription } from './trigger-subscription'
import { ReadmeEntrance } from '@/app/components/plugins/readme-panel/entrance'
import { useAllBuiltInTools } from '@/service/use-tools'
const getCustomRunForm = (params: CustomRunFormProps): React.JSX.Element => { const getCustomRunForm = (params: CustomRunFormProps): React.JSX.Element => {
const nodeType = params.payload.type const nodeType = params.payload.type
@ -103,6 +98,7 @@ const BasePanel: FC<BasePanelProps> = ({
children, children,
}) => { }) => {
const { t } = useTranslation() const { t } = useTranslation()
const language = useLanguage()
const { showMessageLogModal } = useAppStore(useShallow(state => ({ const { showMessageLogModal } = useAppStore(useShallow(state => ({
showMessageLogModal: state.showMessageLogModal, showMessageLogModal: state.showMessageLogModal,
}))) })))
@ -224,6 +220,7 @@ const BasePanel: FC<BasePanelProps> = ({
useEffect(() => { useEffect(() => {
hasClickRunning.current = false hasClickRunning.current = false
}, [id]) }, [id])
const { const {
nodesMap, nodesMap,
} = useNodesMetaData() } = useNodesMetaData()
@ -278,12 +275,7 @@ const BasePanel: FC<BasePanelProps> = ({
}, [pendingSingleRun, id, handleSingleRun, handleStop, setPendingSingleRun]) }, [pendingSingleRun, id, handleSingleRun, handleStop, setPendingSingleRun])
const logParams = useLogs() const logParams = useLogs()
const passedLogParams = (() => { const passedLogParams = useMemo(() => [BlockEnum.Tool, BlockEnum.Agent, BlockEnum.Iteration, BlockEnum.Loop].includes(data.type) ? logParams : {}, [data.type, logParams])
if ([BlockEnum.Tool, BlockEnum.Agent, BlockEnum.Iteration, BlockEnum.Loop].includes(data.type))
return logParams
return {}
})()
const storeBuildInTools = useStore(s => s.buildInTools) const storeBuildInTools = useStore(s => s.buildInTools)
const { data: buildInTools } = useAllBuiltInTools() const { data: buildInTools } = useAllBuiltInTools()
@ -295,16 +287,32 @@ const BasePanel: FC<BasePanelProps> = ({
return data.type === BlockEnum.Tool && currToolCollection?.allow_delete return data.type === BlockEnum.Tool && currToolCollection?.allow_delete
}, [data.type, currToolCollection?.allow_delete]) }, [data.type, currToolCollection?.allow_delete])
const { data: triggerProviders = [] } = useAllTriggerPlugins() // only fetch trigger plugins when the node is a trigger plugin
const currentTriggerProvider = useMemo(() => { const { data: triggerPlugins = [] } = useAllTriggerPlugins(data.type === BlockEnum.TriggerPlugin)
if (!data.provider_id || !data.provider_name) const currentTriggerPlugin = useMemo(() => {
if (data.type !== BlockEnum.TriggerPlugin || !data.plugin_id || !triggerPlugins?.length)
return undefined return undefined
return triggerProviders.find(p => p.name === data.provider_id) // todo: confirm return triggerPlugins?.find(p => p.plugin_id === data.plugin_id)
}, [data.type, data.provider_id, data.provider_name, triggerProviders]) }, [data.type, data.plugin_id, triggerPlugins])
const { setDetail } = usePluginStore()
const showTriggerConfig = useMemo(() => { useEffect(() => {
return data.type === BlockEnum.TriggerPlugin && currentTriggerProvider if (currentTriggerPlugin) {
}, [data.type, currentTriggerProvider]) setDetail({
name: currentTriggerPlugin.label[language],
plugin_id: currentTriggerPlugin.plugin_id || '',
provider: currentTriggerPlugin.name,
declaration: {
tool: undefined,
// @ts-expect-error just remain the necessary fields
trigger: {
subscription_schema: currentTriggerPlugin.subscription_schema || [],
subscription_constructor: currentTriggerPlugin.subscription_constructor,
},
},
})
}
}, [currentTriggerPlugin, setDetail])
const dataSourceList = useStore(s => s.dataSourceList) const dataSourceList = useStore(s => s.dataSourceList)
@ -352,14 +360,14 @@ const BasePanel: FC<BasePanelProps> = ({
pluginDetail = currentDataSource pluginDetail = currentDataSource
break break
case BlockEnum.TriggerPlugin: case BlockEnum.TriggerPlugin:
pluginDetail = currentTriggerProvider pluginDetail = currentTriggerPlugin
break break
default: default:
break break
} }
return !pluginDetail ? null : <ReadmeEntrance pluginDetail={pluginDetail as any} className='mt-auto' /> return !pluginDetail ? null : <ReadmeEntrance pluginDetail={pluginDetail as any} className='mt-auto' />
}, [data.type, currToolCollection, currentDataSource, currentTriggerProvider]) }, [data.type, currToolCollection, currentDataSource, currentTriggerPlugin])
if (logParams.showSpecialResultPanel) { if (logParams.showSpecialResultPanel) {
return ( return (
@ -558,9 +566,9 @@ const BasePanel: FC<BasePanelProps> = ({
) )
} }
{ {
showTriggerConfig && ( currentTriggerPlugin && (
<TriggerSubscription <TriggerSubscription
data={data} subscriptionIdSelected={data.subscription_id}
onSubscriptionChange={handleSubscriptionChange} onSubscriptionChange={handleSubscriptionChange}
> >
<Tab <Tab
@ -571,7 +579,7 @@ const BasePanel: FC<BasePanelProps> = ({
) )
} }
{ {
!needsToolAuth && !currentDataSource && !showTriggerConfig && ( !needsToolAuth && !currentDataSource && !currentTriggerPlugin && (
<div className='flex items-center justify-between pl-4 pr-3'> <div className='flex items-center justify-between pl-4 pr-3'>
<Tab <Tab
value={tabType} value={tabType}

View File

@ -1,52 +1,25 @@
import { useLanguage } from '@/app/components/header/account-setting/model-provider-page/hooks'
import type { SimpleSubscription } from '@/app/components/plugins/plugin-detail-panel/subscription-list' import type { SimpleSubscription } from '@/app/components/plugins/plugin-detail-panel/subscription-list'
import { CreateButtonType, CreateSubscriptionButton } from '@/app/components/plugins/plugin-detail-panel/subscription-list/create' import { CreateButtonType, CreateSubscriptionButton } from '@/app/components/plugins/plugin-detail-panel/subscription-list/create'
import { SubscriptionSelectorEntry } from '@/app/components/plugins/plugin-detail-panel/subscription-list/selector-entry' import { SubscriptionSelectorEntry } from '@/app/components/plugins/plugin-detail-panel/subscription-list/selector-entry'
import { usePluginStore } from '@/app/components/plugins/plugin-detail-panel/store'
import { useSubscriptionList } from '@/app/components/plugins/plugin-detail-panel/subscription-list/use-subscription-list' import { useSubscriptionList } from '@/app/components/plugins/plugin-detail-panel/subscription-list/use-subscription-list'
import useConfig from '@/app/components/workflow/nodes/trigger-plugin/use-config'
import type { Node } from '@/app/components/workflow/types'
import cn from '@/utils/classnames' import cn from '@/utils/classnames'
import type { FC } from 'react' import type { FC } from 'react'
import { useEffect } from 'react'
type TriggerSubscriptionProps = { type TriggerSubscriptionProps = {
data: Node['data'] subscriptionIdSelected?: string
onSubscriptionChange: (v: SimpleSubscription, callback?: () => void) => void onSubscriptionChange: (v: SimpleSubscription, callback?: () => void) => void
children: React.ReactNode children: React.ReactNode
} }
export const TriggerSubscription: FC<TriggerSubscriptionProps> = ({ data, onSubscriptionChange, children }) => { export const TriggerSubscription: FC<TriggerSubscriptionProps> = ({ subscriptionIdSelected, onSubscriptionChange, children }) => {
// @ts-expect-error TODO: fix this
const { currentProvider } = useConfig(data.id as string, data)
const { setDetail } = usePluginStore()
const language = useLanguage()
const { subscriptions } = useSubscriptionList() const { subscriptions } = useSubscriptionList()
const subscriptionCount = subscriptions?.length || 0 const subscriptionCount = subscriptions?.length || 0
useEffect(() => {
if (currentProvider) {
setDetail({
name: currentProvider.label[language],
plugin_id: currentProvider.plugin_id || '',
provider: currentProvider.name,
declaration: {
tool: undefined,
// @ts-expect-error just remain the necessary fields
trigger: {
subscription_schema: currentProvider.subscription_schema || [],
subscription_constructor: currentProvider.subscription_constructor,
},
},
})
}
}, [currentProvider, setDetail])
return <div className={cn('px-4', subscriptionCount > 0 && 'flex items-center justify-between pr-3')}> return <div className={cn('px-4', subscriptionCount > 0 && 'flex items-center justify-between pr-3')}>
{!subscriptionCount && <CreateSubscriptionButton buttonType={CreateButtonType.FULL_BUTTON} />} {!subscriptionCount && <CreateSubscriptionButton buttonType={CreateButtonType.FULL_BUTTON} />}
{children} {children}
{subscriptionCount > 0 && <SubscriptionSelectorEntry {subscriptionCount > 0 && <SubscriptionSelectorEntry
selectedId={data.subscription_id} selectedId={subscriptionIdSelected}
onSelect={onSubscriptionChange} onSelect={onSubscriptionChange}
/>} />}
</div> </div>

View File

@ -14,7 +14,7 @@ type Props = {
onChange: (value: PluginTriggerVarInputs) => void onChange: (value: PluginTriggerVarInputs) => void
onOpen?: (index: number) => void onOpen?: (index: number) => void
inPanel?: boolean inPanel?: boolean
currentTrigger?: Event currentEvent?: Event
currentProvider?: TriggerWithProvider currentProvider?: TriggerWithProvider
extraParams?: Record<string, any> extraParams?: Record<string, any>
disableVariableInsertion?: boolean disableVariableInsertion?: boolean
@ -27,7 +27,7 @@ const TriggerForm: FC<Props> = ({
value, value,
onChange, onChange,
inPanel, inPanel,
currentTrigger, currentEvent,
currentProvider, currentProvider,
extraParams, extraParams,
disableVariableInsertion = false, disableVariableInsertion = false,
@ -44,7 +44,7 @@ const TriggerForm: FC<Props> = ({
value={value} value={value}
onChange={onChange} onChange={onChange}
inPanel={inPanel} inPanel={inPanel}
currentTrigger={currentTrigger} currentEvent={currentEvent}
currentProvider={currentProvider} currentProvider={currentProvider}
extraParams={extraParams} extraParams={extraParams}
disableVariableInsertion={disableVariableInsertion} disableVariableInsertion={disableVariableInsertion}

View File

@ -22,7 +22,7 @@ type Props = {
value: PluginTriggerVarInputs value: PluginTriggerVarInputs
onChange: (value: PluginTriggerVarInputs) => void onChange: (value: PluginTriggerVarInputs) => void
inPanel?: boolean inPanel?: boolean
currentTrigger?: Event currentEvent?: Event
currentProvider?: TriggerWithProvider currentProvider?: TriggerWithProvider
extraParams?: Record<string, any> extraParams?: Record<string, any>
disableVariableInsertion?: boolean disableVariableInsertion?: boolean
@ -35,7 +35,7 @@ const TriggerFormItem: FC<Props> = ({
value, value,
onChange, onChange,
inPanel, inPanel,
currentTrigger, currentEvent,
currentProvider, currentProvider,
extraParams, extraParams,
disableVariableInsertion = false, disableVariableInsertion = false,
@ -91,7 +91,7 @@ const TriggerFormItem: FC<Props> = ({
value={value} value={value}
onChange={onChange} onChange={onChange}
inPanel={inPanel} inPanel={inPanel}
currentTool={currentTrigger} currentTool={currentEvent}
currentProvider={currentProvider} currentProvider={currentProvider}
providerType='trigger' providerType='trigger'
extraParams={extraParams} extraParams={extraParams}

View File

@ -22,7 +22,8 @@ const Panel: FC<NodePanelProps<PluginTriggerNodeType>> = ({
outputSchema, outputSchema,
hasObjectOutput, hasObjectOutput,
currentProvider, currentProvider,
currentTrigger, currentEvent,
subscriptionSelected,
} = useConfig(id, data) } = useConfig(id, data)
const disableVariableInsertion = data.type === BlockEnum.TriggerPlugin const disableVariableInsertion = data.type === BlockEnum.TriggerPlugin
@ -36,7 +37,7 @@ const Panel: FC<NodePanelProps<PluginTriggerNodeType>> = ({
return ( return (
<div className='mt-2'> <div className='mt-2'>
{/* Dynamic Parameters Form - Only show when authenticated */} {/* Dynamic Parameters Form - Only show when authenticated */}
{triggerParameterSchema.length > 0 && ( {triggerParameterSchema.length > 0 && subscriptionSelected && (
<> <>
<div className='px-4 pb-4'> <div className='px-4 pb-4'>
<TriggerForm <TriggerForm
@ -46,7 +47,7 @@ const Panel: FC<NodePanelProps<PluginTriggerNodeType>> = ({
value={triggerParameterValue} value={triggerParameterValue}
onChange={setTriggerParameterValue} onChange={setTriggerParameterValue}
currentProvider={currentProvider} currentProvider={currentProvider}
currentTrigger={currentTrigger} currentEvent={currentEvent}
disableVariableInsertion={disableVariableInsertion} disableVariableInsertion={disableVariableInsertion}
/> />
</div> </div>

View File

@ -86,6 +86,7 @@ const useConfig = (id: string, payload: PluginTriggerNodeType) => {
event_name: event_name, event_name: event_name,
config = {}, config = {},
event_parameters: rawEventParameters = {}, event_parameters: rawEventParameters = {},
subscription_id,
} = inputs } = inputs
const event_parameters = useMemo( const event_parameters = useMemo(
@ -97,16 +98,6 @@ const useConfig = (id: string, payload: PluginTriggerNodeType) => {
[config], [config],
) )
// Construct provider for authentication check
const authProvider = useMemo(() => {
return provider_name || ''
}, [provider_id, provider_name])
const { data: subscriptions = [] } = useTriggerSubscriptions(
authProvider,
!!authProvider,
)
const currentProvider = useMemo<TriggerWithProvider | undefined>(() => { const currentProvider = useMemo<TriggerWithProvider | undefined>(() => {
return triggerPlugins.find( return triggerPlugins.find(
provider => provider =>
@ -116,6 +107,12 @@ const useConfig = (id: string, payload: PluginTriggerNodeType) => {
) )
}, [triggerPlugins, provider_name, provider_id]) }, [triggerPlugins, provider_name, provider_id])
const { data: subscriptions = [] } = useTriggerSubscriptions(provider_id || '')
const subscriptionSelected = useMemo(() => {
return subscriptions?.find(s => s.id === subscription_id)
}, [subscriptions, subscription_id])
const currentEvent = useMemo<Event | undefined>(() => { const currentEvent = useMemo<Event | undefined>(() => {
return currentProvider?.events.find( return currentProvider?.events.find(
event => event.name === event_name, event => event.name === event_name,
@ -221,7 +218,7 @@ const useConfig = (id: string, payload: PluginTriggerNodeType) => {
readOnly, readOnly,
inputs, inputs,
currentProvider, currentProvider,
currentTrigger: currentEvent, currentEvent,
triggerParameterSchema, triggerParameterSchema,
triggerParameterValue, triggerParameterValue,
setTriggerParameterValue, setTriggerParameterValue,
@ -229,6 +226,7 @@ const useConfig = (id: string, payload: PluginTriggerNodeType) => {
outputSchema, outputSchema,
hasObjectOutput, hasObjectOutput,
subscriptions, subscriptions,
subscriptionSelected,
} }
} }