feat: when add/delete webhook trigger call the API (#24755)

This commit is contained in:
非法操作 2025-08-29 14:23:50 +08:00 committed by GitHub
parent a58df35ead
commit 19c0fc85e2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 39 additions and 23 deletions

View File

@ -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)

View File

@ -94,6 +94,7 @@ const Panel: FC<NodePanelProps<WebhookTriggerNodeType>> = ({
</div>
</div>
</Field>
<span>{inputs.webhook_debug_url || ''}</span>
<Split />

View File

@ -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[]

View File

@ -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])

View File

@ -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
}

View File

@ -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<CommonResponse, { appId: string; body:
}
// Webhook Trigger
export const fetchWebhookUrl: Fetcher<{ serverUrl: string }, { appId: string; nodeId: string }> = ({ appId, nodeId }) => {
return get<{ serverUrl: string }>(`apps/${appId}/webhook-url`, { params: { node: nodeId } })
export const fetchWebhookUrl: Fetcher<WebhookTriggerResponse, { appId: string; nodeId: string }> = ({ appId, nodeId }) => {
return post<WebhookTriggerResponse>(`apps/${appId}/workflows/triggers/webhook`, { params: { node_id: nodeId } })
}
export const deleteWebhookUrl: Fetcher<CommonResponse, { appId: string; nodeId: string }> = ({ appId, nodeId }) => {
return del<CommonResponse>(`apps/${appId}/workflows/triggers/webhook`, { params: { node_id: nodeId } })
}
export const fetchTracingConfig: Fetcher<TracingConfig & { has_not_configured: true }, { appId: string; provider: TracingProvider }> = ({ appId, provider }) => {