Skip to content

Commit f850df7

Browse files
committed
on undeploy cleanup webhooks
1 parent 45b2eaf commit f850df7

File tree

3 files changed

+57
-3
lines changed

3 files changed

+57
-3
lines changed

apps/sim/app/api/v1/admin/workflows/[id]/deploy/route.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import { db, workflow } from '@sim/db'
22
import { createLogger } from '@sim/logger'
33
import { eq } from 'drizzle-orm'
4+
import { generateRequestId } from '@/lib/core/utils/request'
5+
import { cleanupWebhooksForWorkflow } from '@/lib/webhooks/deploy'
46
import {
57
deployWorkflow,
68
loadWorkflowFromNormalizedTables,
@@ -80,10 +82,11 @@ export const POST = withAdminAuthParams<RouteParams>(async (request, context) =>
8082

8183
export const DELETE = withAdminAuthParams<RouteParams>(async (request, context) => {
8284
const { id: workflowId } = await context.params
85+
const requestId = generateRequestId()
8386

8487
try {
8588
const [workflowRecord] = await db
86-
.select({ id: workflow.id })
89+
.select()
8790
.from(workflow)
8891
.where(eq(workflow.id, workflowId))
8992
.limit(1)
@@ -92,6 +95,13 @@ export const DELETE = withAdminAuthParams<RouteParams>(async (request, context)
9295
return notFoundResponse('Workflow')
9396
}
9497

98+
// Clean up external webhook subscriptions before undeploying
99+
await cleanupWebhooksForWorkflow(
100+
workflowId,
101+
workflowRecord as Record<string, unknown>,
102+
requestId
103+
)
104+
95105
const result = await undeployWorkflow({ workflowId })
96106
if (!result.success) {
97107
return internalErrorResponse(result.error || 'Failed to undeploy workflow')

apps/sim/app/api/workflows/[id]/deploy/route.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { and, desc, eq } from 'drizzle-orm'
44
import type { NextRequest } from 'next/server'
55
import { generateRequestId } from '@/lib/core/utils/request'
66
import { removeMcpToolsForWorkflow, syncMcpToolsForWorkflow } from '@/lib/mcp/workflow-mcp-sync'
7-
import { saveTriggerWebhooksForDeploy } from '@/lib/webhooks/deploy'
7+
import { cleanupWebhooksForWorkflow, saveTriggerWebhooksForDeploy } from '@/lib/webhooks/deploy'
88
import {
99
deployWorkflow,
1010
loadWorkflowFromNormalizedTables,
@@ -219,11 +219,18 @@ export async function DELETE(
219219
try {
220220
logger.debug(`[${requestId}] Undeploying workflow: ${id}`)
221221

222-
const { error } = await validateWorkflowPermissions(id, requestId, 'admin')
222+
const { error, workflow: workflowData } = await validateWorkflowPermissions(
223+
id,
224+
requestId,
225+
'admin'
226+
)
223227
if (error) {
224228
return createErrorResponse(error.message, error.status)
225229
}
226230

231+
// Clean up external webhook subscriptions before undeploying
232+
await cleanupWebhooksForWorkflow(id, workflowData as Record<string, unknown>, requestId)
233+
227234
const result = await undeployWorkflow({ workflowId: id })
228235
if (!result.success) {
229236
return createErrorResponse(result.error || 'Failed to undeploy workflow', 500)

apps/sim/lib/webhooks/deploy.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -539,3 +539,40 @@ export async function saveTriggerWebhooksForDeploy({
539539

540540
return { success: true }
541541
}
542+
543+
/**
544+
* Clean up all webhooks for a workflow during undeploy.
545+
* Removes external subscriptions and deletes webhook records from the database.
546+
*/
547+
export async function cleanupWebhooksForWorkflow(
548+
workflowId: string,
549+
workflow: Record<string, unknown>,
550+
requestId: string
551+
): Promise<void> {
552+
const existingWebhooks = await db.select().from(webhook).where(eq(webhook.workflowId, workflowId))
553+
554+
if (existingWebhooks.length === 0) {
555+
logger.debug(`[${requestId}] No webhooks to clean up for workflow ${workflowId}`)
556+
return
557+
}
558+
559+
logger.info(`[${requestId}] Cleaning up ${existingWebhooks.length} webhook(s) for undeploy`, {
560+
workflowId,
561+
webhookIds: existingWebhooks.map((wh) => wh.id),
562+
})
563+
564+
// Clean up external subscriptions
565+
for (const wh of existingWebhooks) {
566+
try {
567+
await cleanupExternalWebhook(wh, workflow, requestId)
568+
} catch (cleanupError) {
569+
logger.warn(`[${requestId}] Failed to cleanup external webhook ${wh.id}`, cleanupError)
570+
// Continue with other webhooks even if one fails
571+
}
572+
}
573+
574+
// Delete all webhook records
575+
await db.delete(webhook).where(eq(webhook.workflowId, workflowId))
576+
577+
logger.info(`[${requestId}] Cleaned up all webhooks for workflow ${workflowId}`)
578+
}

0 commit comments

Comments
 (0)