Skip to content

Commit 0175a3c

Browse files
committed
improvement(webhooks): remove dead code
1 parent 1560a39 commit 0175a3c

File tree

15 files changed

+479
-562
lines changed

15 files changed

+479
-562
lines changed

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

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,11 @@ import {
88
loadWorkflowFromNormalizedTables,
99
undeployWorkflow,
1010
} from '@/lib/workflows/persistence/utils'
11-
import { createSchedulesForDeploy, validateWorkflowSchedules } from '@/lib/workflows/schedules'
11+
import {
12+
cleanupDeploymentVersion,
13+
createSchedulesForDeploy,
14+
validateWorkflowSchedules,
15+
} from '@/lib/workflows/schedules'
1216
import { withAdminAuthParams } from '@/app/api/v1/admin/middleware'
1317
import {
1418
badRequestResponse,
@@ -72,7 +76,17 @@ export const POST = withAdminAuthParams<RouteParams>(async (request, context) =>
7276
deployResult.deploymentVersionId
7377
)
7478
if (!scheduleResult.success) {
75-
logger.warn(`Schedule creation failed for workflow ${workflowId}: ${scheduleResult.error}`)
79+
logger.error(
80+
`Admin API: Schedule creation failed for workflow ${workflowId}: ${scheduleResult.error}`
81+
)
82+
await cleanupDeploymentVersion({
83+
workflowId,
84+
workflow: normalizedData as unknown as Record<string, unknown>,
85+
requestId: generateRequestId(),
86+
deploymentVersionId: deployResult.deploymentVersionId,
87+
})
88+
await undeployWorkflow({ workflowId })
89+
return internalErrorResponse(scheduleResult.error || 'Failed to create schedule')
7690
}
7791

7892
logger.info(`Admin API: Deployed workflow ${workflowId} as v${deployResult.version}`)

apps/sim/app/api/v1/admin/workflows/[id]/versions/[versionId]/activate/route.ts

Lines changed: 134 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,23 @@
1-
import { db, workflow } from '@sim/db'
1+
import { db, workflow, workflowDeploymentVersion } from '@sim/db'
22
import { createLogger } from '@sim/logger'
3-
import { eq } from 'drizzle-orm'
3+
import { and, eq } from 'drizzle-orm'
4+
import { generateRequestId } from '@/lib/core/utils/request'
5+
import { syncMcpToolsForWorkflow } from '@/lib/mcp/workflow-mcp-sync'
6+
import { saveTriggerWebhooksForDeploy } from '@/lib/webhooks/deploy'
47
import { activateWorkflowVersion } from '@/lib/workflows/persistence/utils'
8+
import {
9+
cleanupDeploymentVersion,
10+
createSchedulesForDeploy,
11+
validateWorkflowSchedules,
12+
} from '@/lib/workflows/schedules'
513
import { withAdminAuthParams } from '@/app/api/v1/admin/middleware'
614
import {
715
badRequestResponse,
816
internalErrorResponse,
917
notFoundResponse,
1018
singleResponse,
1119
} from '@/app/api/v1/admin/responses'
20+
import type { BlockState } from '@/stores/workflows/workflow/types'
1221

1322
const logger = createLogger('AdminWorkflowActivateVersionAPI')
1423

@@ -18,11 +27,12 @@ interface RouteParams {
1827
}
1928

2029
export const POST = withAdminAuthParams<RouteParams>(async (request, context) => {
30+
const requestId = generateRequestId()
2131
const { id: workflowId, versionId } = await context.params
2232

2333
try {
2434
const [workflowRecord] = await db
25-
.select({ id: workflow.id })
35+
.select()
2636
.from(workflow)
2737
.where(eq(workflow.id, workflowId))
2838
.limit(1)
@@ -36,23 +46,142 @@ export const POST = withAdminAuthParams<RouteParams>(async (request, context) =>
3646
return badRequestResponse('Invalid version number')
3747
}
3848

49+
const [versionRow] = await db
50+
.select({
51+
id: workflowDeploymentVersion.id,
52+
state: workflowDeploymentVersion.state,
53+
})
54+
.from(workflowDeploymentVersion)
55+
.where(
56+
and(
57+
eq(workflowDeploymentVersion.workflowId, workflowId),
58+
eq(workflowDeploymentVersion.version, versionNum)
59+
)
60+
)
61+
.limit(1)
62+
63+
if (!versionRow?.state) {
64+
return notFoundResponse('Deployment version')
65+
}
66+
67+
const [currentActiveVersion] = await db
68+
.select({ id: workflowDeploymentVersion.id })
69+
.from(workflowDeploymentVersion)
70+
.where(
71+
and(
72+
eq(workflowDeploymentVersion.workflowId, workflowId),
73+
eq(workflowDeploymentVersion.isActive, true)
74+
)
75+
)
76+
.limit(1)
77+
78+
const previousVersionId = currentActiveVersion?.id
79+
80+
const deployedState = versionRow.state as { blocks?: Record<string, BlockState> }
81+
const blocks = deployedState.blocks
82+
if (!blocks || typeof blocks !== 'object') {
83+
return internalErrorResponse('Invalid deployed state structure')
84+
}
85+
86+
const workflowData = workflowRecord as Record<string, unknown>
87+
88+
const triggerSaveResult = await saveTriggerWebhooksForDeploy({
89+
request,
90+
workflowId,
91+
workflow: workflowData,
92+
userId: workflowRecord.userId,
93+
blocks,
94+
requestId,
95+
deploymentVersionId: versionRow.id,
96+
previousVersionId,
97+
forceRecreateSubscriptions: true,
98+
})
99+
100+
if (!triggerSaveResult.success) {
101+
logger.error(
102+
`[${requestId}] Admin API: Failed to sync triggers for workflow ${workflowId}`,
103+
triggerSaveResult.error
104+
)
105+
return internalErrorResponse(
106+
triggerSaveResult.error?.message || 'Failed to sync trigger configuration'
107+
)
108+
}
109+
110+
const scheduleValidation = validateWorkflowSchedules(blocks)
111+
if (!scheduleValidation.isValid) {
112+
return badRequestResponse(`Invalid schedule configuration: ${scheduleValidation.error}`)
113+
}
114+
115+
const scheduleResult = await createSchedulesForDeploy(workflowId, blocks, db, versionRow.id)
116+
117+
if (!scheduleResult.success) {
118+
await cleanupDeploymentVersion({
119+
workflowId,
120+
workflow: workflowData,
121+
requestId,
122+
deploymentVersionId: versionRow.id,
123+
})
124+
return internalErrorResponse(scheduleResult.error || 'Failed to sync schedules')
125+
}
126+
39127
const result = await activateWorkflowVersion({ workflowId, version: versionNum })
40128
if (!result.success) {
129+
await cleanupDeploymentVersion({
130+
workflowId,
131+
workflow: workflowData,
132+
requestId,
133+
deploymentVersionId: versionRow.id,
134+
})
41135
if (result.error === 'Deployment version not found') {
42136
return notFoundResponse('Deployment version')
43137
}
44138
return internalErrorResponse(result.error || 'Failed to activate version')
45139
}
46140

47-
logger.info(`Admin API: Activated version ${versionNum} for workflow ${workflowId}`)
141+
if (previousVersionId && previousVersionId !== versionRow.id) {
142+
try {
143+
logger.info(
144+
`[${requestId}] Admin API: Cleaning up previous version ${previousVersionId} webhooks/schedules`
145+
)
146+
await cleanupDeploymentVersion({
147+
workflowId,
148+
workflow: workflowData,
149+
requestId,
150+
deploymentVersionId: previousVersionId,
151+
skipExternalCleanup: true,
152+
})
153+
logger.info(`[${requestId}] Admin API: Previous version cleanup completed`)
154+
} catch (cleanupError) {
155+
logger.error(
156+
`[${requestId}] Admin API: Failed to clean up previous version ${previousVersionId}`,
157+
cleanupError
158+
)
159+
}
160+
}
161+
162+
await syncMcpToolsForWorkflow({
163+
workflowId,
164+
requestId,
165+
state: versionRow.state,
166+
context: 'activate',
167+
})
168+
169+
logger.info(
170+
`[${requestId}] Admin API: Activated version ${versionNum} for workflow ${workflowId}`
171+
)
48172

49173
return singleResponse({
50174
success: true,
51175
version: versionNum,
52176
deployedAt: result.deployedAt!.toISOString(),
53177
})
54178
} catch (error) {
55-
logger.error(`Admin API: Failed to activate version for workflow ${workflowId}`, { error })
179+
logger.error(
180+
`[${requestId}] Admin API: Failed to activate version for workflow ${workflowId}`,
181+
{
182+
error,
183+
}
184+
)
56185
return internalErrorResponse('Failed to activate deployment version')
57186
}
58187
})

apps/sim/app/api/webhooks/[id]/route.ts

Lines changed: 2 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,7 @@ import { getSession } from '@/lib/auth'
77
import { validateInteger } from '@/lib/core/security/input-validation'
88
import { PlatformEvents } from '@/lib/core/telemetry'
99
import { generateRequestId } from '@/lib/core/utils/request'
10-
import { resolveEnvVarsInObject } from '@/lib/webhooks/env-resolver'
11-
import {
12-
cleanupExternalWebhook,
13-
createExternalWebhookSubscription,
14-
shouldRecreateExternalWebhookSubscription,
15-
} from '@/lib/webhooks/provider-subscriptions'
16-
import { mergeNonUserFields } from '@/lib/webhooks/utils'
10+
import { cleanupExternalWebhook } from '@/lib/webhooks/provider-subscriptions'
1711
import { getUserEntityPermissions } from '@/lib/workspaces/permissions/utils'
1812

1913
const logger = createLogger('WebhookAPI')
@@ -89,7 +83,6 @@ export async function GET(request: NextRequest, { params }: { params: Promise<{
8983
}
9084
}
9185

92-
// Update a webhook
9386
export async function PATCH(request: NextRequest, { params }: { params: Promise<{ id: string }> }) {
9487
const requestId = generateRequestId()
9588

@@ -104,7 +97,7 @@ export async function PATCH(request: NextRequest, { params }: { params: Promise<
10497
}
10598

10699
const body = await request.json()
107-
const { path, provider, providerConfig, isActive, failedCount } = body
100+
const { isActive, failedCount } = body
108101

109102
if (failedCount !== undefined) {
110103
const validation = validateInteger(failedCount, 'failedCount', { min: 0 })
@@ -114,28 +107,6 @@ export async function PATCH(request: NextRequest, { params }: { params: Promise<
114107
}
115108
}
116109

117-
const originalProviderConfig = providerConfig
118-
let resolvedProviderConfig = providerConfig
119-
if (providerConfig) {
120-
const webhookDataForResolve = await db
121-
.select({
122-
workspaceId: workflow.workspaceId,
123-
})
124-
.from(webhook)
125-
.innerJoin(workflow, eq(webhook.workflowId, workflow.id))
126-
.where(eq(webhook.id, id))
127-
.limit(1)
128-
129-
if (webhookDataForResolve.length > 0) {
130-
resolvedProviderConfig = await resolveEnvVarsInObject(
131-
providerConfig,
132-
session.user.id,
133-
webhookDataForResolve[0].workspaceId || undefined
134-
)
135-
}
136-
}
137-
138-
// Find the webhook and check permissions
139110
const webhooks = await db
140111
.select({
141112
webhook: webhook,
@@ -156,16 +127,12 @@ export async function PATCH(request: NextRequest, { params }: { params: Promise<
156127
}
157128

158129
const webhookData = webhooks[0]
159-
160-
// Check if user has permission to modify this webhook
161130
let canModify = false
162131

163-
// Case 1: User owns the workflow
164132
if (webhookData.workflow.userId === session.user.id) {
165133
canModify = true
166134
}
167135

168-
// Case 2: Workflow belongs to a workspace and user has write or admin permission
169136
if (!canModify && webhookData.workflow.workspaceId) {
170137
const userPermission = await getUserEntityPermissions(
171138
session.user.id,
@@ -184,70 +151,14 @@ export async function PATCH(request: NextRequest, { params }: { params: Promise<
184151
return NextResponse.json({ error: 'Access denied' }, { status: 403 })
185152
}
186153

187-
const existingProviderConfig =
188-
(webhookData.webhook.providerConfig as Record<string, unknown>) || {}
189-
const nextProvider = (provider ?? webhookData.webhook.provider) as string
190-
191-
let nextProviderConfig =
192-
providerConfig !== undefined &&
193-
resolvedProviderConfig &&
194-
typeof resolvedProviderConfig === 'object'
195-
? (resolvedProviderConfig as Record<string, unknown>)
196-
: existingProviderConfig
197-
198-
if (
199-
providerConfig !== undefined &&
200-
shouldRecreateExternalWebhookSubscription({
201-
previousProvider: webhookData.webhook.provider as string,
202-
nextProvider,
203-
previousConfig: existingProviderConfig,
204-
nextConfig: originalProviderConfig as Record<string, unknown>,
205-
})
206-
) {
207-
await cleanupExternalWebhook(
208-
{ ...webhookData.webhook, providerConfig: existingProviderConfig },
209-
webhookData.workflow,
210-
requestId
211-
)
212-
213-
const result = await createExternalWebhookSubscription(
214-
request,
215-
{
216-
...webhookData.webhook,
217-
provider: nextProvider,
218-
providerConfig: nextProviderConfig,
219-
},
220-
webhookData.workflow,
221-
session.user.id,
222-
requestId
223-
)
224-
225-
nextProviderConfig = result.updatedProviderConfig as Record<string, unknown>
226-
}
227-
228154
logger.debug(`[${requestId}] Updating webhook properties`, {
229-
hasPathUpdate: path !== undefined,
230-
hasProviderUpdate: provider !== undefined,
231-
hasConfigUpdate: providerConfig !== undefined,
232155
hasActiveUpdate: isActive !== undefined,
233156
hasFailedCountUpdate: failedCount !== undefined,
234157
})
235158

236-
let finalProviderConfig: Record<string, unknown> =
237-
(webhooks[0].webhook.providerConfig as Record<string, unknown>) || {}
238-
if (providerConfig !== undefined && originalProviderConfig) {
239-
const userProvided = originalProviderConfig as Record<string, unknown>
240-
finalProviderConfig = { ...userProvided }
241-
mergeNonUserFields(finalProviderConfig, existingProviderConfig, userProvided)
242-
mergeNonUserFields(finalProviderConfig, nextProviderConfig, userProvided)
243-
}
244-
245159
const updatedWebhook = await db
246160
.update(webhook)
247161
.set({
248-
path: path !== undefined ? path : webhooks[0].webhook.path,
249-
provider: provider !== undefined ? provider : webhooks[0].webhook.provider,
250-
providerConfig: finalProviderConfig,
251162
isActive: isActive !== undefined ? isActive : webhooks[0].webhook.isActive,
252163
failedCount: failedCount !== undefined ? failedCount : webhooks[0].webhook.failedCount,
253164
updatedAt: new Date(),
@@ -330,8 +241,6 @@ export async function DELETE(
330241
}
331242

332243
const foundWebhook = webhookData.webhook
333-
const { cleanupExternalWebhook } = await import('@/lib/webhooks/provider-subscriptions')
334-
335244
const providerConfig = foundWebhook.providerConfig as Record<string, unknown> | null
336245
const credentialSetId = providerConfig?.credentialSetId as string | undefined
337246
const blockId = providerConfig?.blockId as string | undefined

0 commit comments

Comments
 (0)