on undeploy cleanup webhooks

This commit is contained in:
Vikhyath Mondreti
2026-01-15 12:37:44 -08:00
parent 45b2eaffa7
commit f850df7e9b
3 changed files with 57 additions and 3 deletions

View File

@@ -1,6 +1,8 @@
import { db, workflow } from '@sim/db'
import { createLogger } from '@sim/logger'
import { eq } from 'drizzle-orm'
import { generateRequestId } from '@/lib/core/utils/request'
import { cleanupWebhooksForWorkflow } from '@/lib/webhooks/deploy'
import {
deployWorkflow,
loadWorkflowFromNormalizedTables,
@@ -80,10 +82,11 @@ export const POST = withAdminAuthParams<RouteParams>(async (request, context) =>
export const DELETE = withAdminAuthParams<RouteParams>(async (request, context) => {
const { id: workflowId } = await context.params
const requestId = generateRequestId()
try {
const [workflowRecord] = await db
.select({ id: workflow.id })
.select()
.from(workflow)
.where(eq(workflow.id, workflowId))
.limit(1)
@@ -92,6 +95,13 @@ export const DELETE = withAdminAuthParams<RouteParams>(async (request, context)
return notFoundResponse('Workflow')
}
// Clean up external webhook subscriptions before undeploying
await cleanupWebhooksForWorkflow(
workflowId,
workflowRecord as Record<string, unknown>,
requestId
)
const result = await undeployWorkflow({ workflowId })
if (!result.success) {
return internalErrorResponse(result.error || 'Failed to undeploy workflow')

View File

@@ -4,7 +4,7 @@ import { and, desc, eq } from 'drizzle-orm'
import type { NextRequest } from 'next/server'
import { generateRequestId } from '@/lib/core/utils/request'
import { removeMcpToolsForWorkflow, syncMcpToolsForWorkflow } from '@/lib/mcp/workflow-mcp-sync'
import { saveTriggerWebhooksForDeploy } from '@/lib/webhooks/deploy'
import { cleanupWebhooksForWorkflow, saveTriggerWebhooksForDeploy } from '@/lib/webhooks/deploy'
import {
deployWorkflow,
loadWorkflowFromNormalizedTables,
@@ -219,11 +219,18 @@ export async function DELETE(
try {
logger.debug(`[${requestId}] Undeploying workflow: ${id}`)
const { error } = await validateWorkflowPermissions(id, requestId, 'admin')
const { error, workflow: workflowData } = await validateWorkflowPermissions(
id,
requestId,
'admin'
)
if (error) {
return createErrorResponse(error.message, error.status)
}
// Clean up external webhook subscriptions before undeploying
await cleanupWebhooksForWorkflow(id, workflowData as Record<string, unknown>, requestId)
const result = await undeployWorkflow({ workflowId: id })
if (!result.success) {
return createErrorResponse(result.error || 'Failed to undeploy workflow', 500)

View File

@@ -539,3 +539,40 @@ export async function saveTriggerWebhooksForDeploy({
return { success: true }
}
/**
* Clean up all webhooks for a workflow during undeploy.
* Removes external subscriptions and deletes webhook records from the database.
*/
export async function cleanupWebhooksForWorkflow(
workflowId: string,
workflow: Record<string, unknown>,
requestId: string
): Promise<void> {
const existingWebhooks = await db.select().from(webhook).where(eq(webhook.workflowId, workflowId))
if (existingWebhooks.length === 0) {
logger.debug(`[${requestId}] No webhooks to clean up for workflow ${workflowId}`)
return
}
logger.info(`[${requestId}] Cleaning up ${existingWebhooks.length} webhook(s) for undeploy`, {
workflowId,
webhookIds: existingWebhooks.map((wh) => wh.id),
})
// Clean up external subscriptions
for (const wh of existingWebhooks) {
try {
await cleanupExternalWebhook(wh, workflow, requestId)
} catch (cleanupError) {
logger.warn(`[${requestId}] Failed to cleanup external webhook ${wh.id}`, cleanupError)
// Continue with other webhooks even if one fails
}
}
// Delete all webhook records
await db.delete(webhook).where(eq(webhook.workflowId, workflowId))
logger.info(`[${requestId}] Cleaned up all webhooks for workflow ${workflowId}`)
}