From 19c0fc85e242a87569d5ca5c968893bc7ff34d70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9D=9E=E6=B3=95=E6=93=8D=E4=BD=9C?= Date: Fri, 29 Aug 2025 14:23:50 +0800 Subject: [PATCH] feat: when add/delete webhook trigger call the API (#24755) --- .../workflow/hooks/use-nodes-interactions.ts | 15 +++++++++++ .../workflow/nodes/trigger-webhook/panel.tsx | 1 + .../workflow/nodes/trigger-webhook/types.ts | 1 + .../nodes/trigger-webhook/use-config.ts | 26 +++++-------------- web/models/app.ts | 9 +++++++ web/service/apps.ts | 10 ++++--- 6 files changed, 39 insertions(+), 23 deletions(-) diff --git a/web/app/components/workflow/hooks/use-nodes-interactions.ts b/web/app/components/workflow/hooks/use-nodes-interactions.ts index 72b5e434da..b45ce8831f 100644 --- a/web/app/components/workflow/hooks/use-nodes-interactions.ts +++ b/web/app/components/workflow/hooks/use-nodes-interactions.ts @@ -63,6 +63,8 @@ import { import { WorkflowHistoryEvent, useWorkflowHistory } from './use-workflow-history' import useInspectVarsCrud from './use-inspect-vars-crud' import { getNodeUsedVars } from '../nodes/_base/components/variable/utils' +import { deleteWebhookUrl } from '@/service/apps' +import { useStore as useAppStore } from '@/app/components/app/store' // Entry node deletion restriction has been removed to allow empty workflows @@ -74,6 +76,7 @@ export const useNodesInteractions = () => { const reactflow = useReactFlow() const { store: workflowHistoryStore } = useWorkflowHistoryStore() const { handleSyncWorkflowDraft } = useNodesSyncDraft() + const appId = useAppStore.getState().appDetail?.id const { checkNestedParallelLimit, getAfterNodesInSameBranch, @@ -588,6 +591,18 @@ export const useNodesInteractions = () => { return deleteNodeInspectorVars(nodeId) + + if (currentNode.data.type === BlockEnum.TriggerWebhook) { + if (appId) { + try { + deleteWebhookUrl({ appId, nodeId }) + } + catch (error) { + console.error('Failed to delete webhook URL:', error) + } + } + } + if (currentNode.data.type === BlockEnum.Iteration) { const iterationChildren = nodes.filter(node => node.parentId === currentNode.id) diff --git a/web/app/components/workflow/nodes/trigger-webhook/panel.tsx b/web/app/components/workflow/nodes/trigger-webhook/panel.tsx index 7fc12ef556..5c58a0000c 100644 --- a/web/app/components/workflow/nodes/trigger-webhook/panel.tsx +++ b/web/app/components/workflow/nodes/trigger-webhook/panel.tsx @@ -94,6 +94,7 @@ const Panel: FC> = ({ + {inputs.webhook_debug_url || ''} diff --git a/web/app/components/workflow/nodes/trigger-webhook/types.ts b/web/app/components/workflow/nodes/trigger-webhook/types.ts index 472aa1f97d..f065e670d8 100644 --- a/web/app/components/workflow/nodes/trigger-webhook/types.ts +++ b/web/app/components/workflow/nodes/trigger-webhook/types.ts @@ -19,6 +19,7 @@ export type WebhookHeader = { export type WebhookTriggerNodeType = CommonNodeType & { 'webhook_url'?: string + 'webhook_debug_url'?: string 'method': HttpMethod 'content-type': string 'headers': WebhookHeader[] diff --git a/web/app/components/workflow/nodes/trigger-webhook/use-config.ts b/web/app/components/workflow/nodes/trigger-webhook/use-config.ts index 3c1fb1347f..f14b7acde8 100644 --- a/web/app/components/workflow/nodes/trigger-webhook/use-config.ts +++ b/web/app/components/workflow/nodes/trigger-webhook/use-config.ts @@ -76,43 +76,29 @@ const useConfig = (id: string, payload: WebhookTriggerNodeType) => { const generateWebhookUrl = useCallback(async () => { // Idempotency: if we already have a URL, just return it. if (inputs.webhook_url && inputs.webhook_url.length > 0) - return inputs.webhook_url + return - // Helper to build a deterministic mock URL for local/dev usage. - const buildMockUrl = () => `https://mock.dify.local/webhook/${appId ?? 'app'}/${id}` - - if (!appId) { - // No appId available yet (e.g. during creation): use mock URL. - const mockUrl = buildMockUrl() - const newInputs = produce(inputs, (draft) => { - draft.webhook_url = mockUrl - }) - setInputs(newInputs) - return mockUrl - } + if (!appId) + return try { // Call backend to generate or fetch webhook url for this node const response = await fetchWebhookUrl({ appId, nodeId: id }) - const url = response.serverUrl const newInputs = produce(inputs, (draft) => { - draft.webhook_url = url + draft.webhook_url = response.webhook_url + draft.webhook_debug_url = response.webhook_debug_url }) setInputs(newInputs) - - return url } catch (error: unknown) { // Fallback to mock URL when API is not ready or request fails // Keep the UI unblocked and allow users to proceed in local/dev environments. console.error('Failed to generate webhook URL:', error) - const mockUrl = buildMockUrl() const newInputs = produce(inputs, (draft) => { - draft.webhook_url = mockUrl + draft.webhook_url = '' }) setInputs(newInputs) - return mockUrl } }, [appId, id, inputs, setInputs]) diff --git a/web/models/app.ts b/web/models/app.ts index 630dba9c19..b316e081b6 100644 --- a/web/models/app.ts +++ b/web/models/app.ts @@ -168,3 +168,12 @@ export type TracingConfig = { tracing_provider: TracingProvider tracing_config: ArizeConfig | PhoenixConfig | LangSmithConfig | LangFuseConfig | OpikConfig | WeaveConfig | AliyunConfig } + +export type WebhookTriggerResponse = { + id: string + webhook_id: string + webhook_url: string + webhook_debug_url: string + node_id: string + created_at: string +} diff --git a/web/service/apps.ts b/web/service/apps.ts index 9f1e00b497..3f4f05c264 100644 --- a/web/service/apps.ts +++ b/web/service/apps.ts @@ -1,6 +1,6 @@ import type { Fetcher } from 'swr' import { del, get, patch, post, put } from './base' -import type { ApiKeysListResponse, AppDailyConversationsResponse, AppDailyEndUsersResponse, AppDailyMessagesResponse, AppDetailResponse, AppListResponse, AppStatisticsResponse, AppTemplatesResponse, AppTokenCostsResponse, AppVoicesListResponse, CreateApiKeyResponse, DSLImportMode, DSLImportResponse, GenerationIntroductionResponse, TracingConfig, TracingStatus, UpdateAppModelConfigResponse, UpdateAppSiteCodeResponse, UpdateOpenAIKeyResponse, ValidateOpenAIKeyResponse, WorkflowDailyConversationsResponse } from '@/models/app' +import type { ApiKeysListResponse, AppDailyConversationsResponse, AppDailyEndUsersResponse, AppDailyMessagesResponse, AppDetailResponse, AppListResponse, AppStatisticsResponse, AppTemplatesResponse, AppTokenCostsResponse, AppVoicesListResponse, CreateApiKeyResponse, DSLImportMode, DSLImportResponse, GenerationIntroductionResponse, TracingConfig, TracingStatus, UpdateAppModelConfigResponse, UpdateAppSiteCodeResponse, UpdateOpenAIKeyResponse, ValidateOpenAIKeyResponse, WebhookTriggerResponse, WorkflowDailyConversationsResponse } from '@/models/app' import type { CommonResponse } from '@/models/common' import type { AppIconType, AppMode, ModelConfig } from '@/types/app' import type { TracingProvider } from '@/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/type' @@ -158,8 +158,12 @@ export const updateTracingStatus: Fetcher = ({ appId, nodeId }) => { - return get<{ serverUrl: string }>(`apps/${appId}/webhook-url`, { params: { node: nodeId } }) +export const fetchWebhookUrl: Fetcher = ({ appId, nodeId }) => { + return post(`apps/${appId}/workflows/triggers/webhook`, { params: { node_id: nodeId } }) +} + +export const deleteWebhookUrl: Fetcher = ({ appId, nodeId }) => { + return del(`apps/${appId}/workflows/triggers/webhook`, { params: { node_id: nodeId } }) } export const fetchTracingConfig: Fetcher = ({ appId, provider }) => {