Skip to content

Commit e572fd2

Browse files
committed
refactor(triggers): consolidate v2 Linear triggers into same files as v1
Move v2 trigger exports from separate _v2.ts files into their corresponding v1 files, matching the block v2 convention where LinearV2Block lives alongside LinearBlock in the same file.
1 parent 8e11c32 commit e572fd2

35 files changed

+527
-825
lines changed

apps/sim/blocks/registry.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,6 @@ import { SftpBlock } from '@/blocks/blocks/sftp'
171171
import { SharepointBlock } from '@/blocks/blocks/sharepoint'
172172
import { ShopifyBlock } from '@/blocks/blocks/shopify'
173173
import { SimilarwebBlock } from '@/blocks/blocks/similarweb'
174-
import { SixtyfourBlock } from '@/blocks/blocks/sixtyfour'
175174
import { SlackBlock } from '@/blocks/blocks/slack'
176175
import { SmtpBlock } from '@/blocks/blocks/smtp'
177176
import { SpotifyBlock } from '@/blocks/blocks/spotify'
@@ -409,7 +408,6 @@ export const registry: Record<string, BlockConfig> = {
409408
sharepoint: SharepointBlock,
410409
shopify: ShopifyBlock,
411410
similarweb: SimilarwebBlock,
412-
sixtyfour: SixtyfourBlock,
413411
slack: SlackBlock,
414412
smtp: SmtpBlock,
415413
spotify: SpotifyBlock,

apps/sim/lib/webhooks/providers/linear.ts

Lines changed: 25 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
import crypto from 'crypto'
22
import { createLogger } from '@sim/logger'
3-
import { NextResponse } from 'next/server'
43
import { safeCompare } from '@/lib/core/security/encryption'
54
import { generateId } from '@/lib/core/utils/uuid'
65
import { getNotificationUrl, getProviderConfig } from '@/lib/webhooks/provider-subscription-utils'
76
import type {
8-
AuthContext,
97
DeleteSubscriptionContext,
108
EventMatchContext,
119
FormatInputContext,
@@ -14,6 +12,7 @@ import type {
1412
SubscriptionResult,
1513
WebhookProviderHandler,
1614
} from '@/lib/webhooks/providers/types'
15+
import { createHmacVerifier } from '@/lib/webhooks/providers/utils'
1716

1817
const logger = createLogger('WebhookProvider:Linear')
1918

@@ -42,73 +41,16 @@ function validateLinearSignature(secret: string, signature: string, body: string
4241
}
4342
}
4443

45-
const LINEAR_WEBHOOK_TIMESTAMP_SKEW_MS = 5 * 60 * 1000
46-
4744
export const linearHandler: WebhookProviderHandler = {
48-
async verifyAuth({
49-
request,
50-
rawBody,
51-
requestId,
52-
providerConfig,
53-
}: AuthContext): Promise<NextResponse | null> {
54-
const secret = providerConfig.webhookSecret as string | undefined
55-
if (!secret) {
56-
return null
57-
}
58-
59-
const signature = request.headers.get('Linear-Signature')
60-
if (!signature) {
61-
logger.warn(`[${requestId}] Linear webhook missing signature header`)
62-
return new NextResponse('Unauthorized - Missing Linear signature', { status: 401 })
63-
}
64-
65-
if (!validateLinearSignature(secret, signature, rawBody)) {
66-
logger.warn(`[${requestId}] Linear signature verification failed`)
67-
return new NextResponse('Unauthorized - Invalid Linear signature', { status: 401 })
68-
}
69-
70-
try {
71-
const parsed = JSON.parse(rawBody) as Record<string, unknown>
72-
const ts = parsed.webhookTimestamp
73-
if (typeof ts !== 'number' || !Number.isFinite(ts)) {
74-
logger.warn(`[${requestId}] Linear webhookTimestamp missing or invalid`)
75-
return new NextResponse('Unauthorized - Invalid webhook timestamp', {
76-
status: 401,
77-
})
78-
}
79-
80-
if (Math.abs(Date.now() - ts) > LINEAR_WEBHOOK_TIMESTAMP_SKEW_MS) {
81-
logger.warn(
82-
`[${requestId}] Linear webhookTimestamp outside allowed skew (${LINEAR_WEBHOOK_TIMESTAMP_SKEW_MS}ms)`
83-
)
84-
return new NextResponse('Unauthorized - Webhook timestamp skew too large', {
85-
status: 401,
86-
})
87-
}
88-
} catch (error) {
89-
logger.warn(
90-
`[${requestId}] Linear webhook body parse failed after signature verification`,
91-
error
92-
)
93-
return new NextResponse('Unauthorized - Invalid webhook body', { status: 401 })
94-
}
95-
96-
return null
97-
},
45+
verifyAuth: createHmacVerifier({
46+
configKey: 'webhookSecret',
47+
headerName: 'Linear-Signature',
48+
validateFn: validateLinearSignature,
49+
providerLabel: 'Linear',
50+
}),
9851

9952
async formatInput({ body }: FormatInputContext): Promise<FormatInputResult> {
10053
const b = body as Record<string, unknown>
101-
const rawActor = b.actor
102-
let actor: unknown = null
103-
if (rawActor && typeof rawActor === 'object' && !Array.isArray(rawActor)) {
104-
const a = rawActor as Record<string, unknown>
105-
const { type: linearActorType, ...rest } = a
106-
actor = {
107-
...rest,
108-
actorType: typeof linearActorType === 'string' ? linearActorType : null,
109-
}
110-
}
111-
11254
return {
11355
input: {
11456
action: b.action || '',
@@ -117,8 +59,7 @@ export const linearHandler: WebhookProviderHandler = {
11759
webhookTimestamp: b.webhookTimestamp || 0,
11860
organizationId: b.organizationId || '',
11961
createdAt: b.createdAt || '',
120-
url: typeof b.url === 'string' ? b.url : '',
121-
actor,
62+
actor: b.actor || null,
12263
data: b.data || null,
12364
updatedFrom: b.updatedFrom || null,
12465
},
@@ -167,20 +108,6 @@ export const linearHandler: WebhookProviderHandler = {
167108

168109
const notificationUrl = getNotificationUrl(ctx.webhook)
169110
const webhookSecret = generateId()
170-
const teamId = config.teamId as string | undefined
171-
172-
const input: Record<string, unknown> = {
173-
url: notificationUrl,
174-
resourceTypes,
175-
secret: webhookSecret,
176-
enabled: true,
177-
}
178-
179-
if (teamId) {
180-
input.teamId = teamId
181-
} else {
182-
input.allPublicTeams = true
183-
}
184111

185112
try {
186113
const response = await fetch('https://api.linear.app/graphql', {
@@ -196,7 +123,14 @@ export const linearHandler: WebhookProviderHandler = {
196123
webhook { id enabled }
197124
}
198125
}`,
199-
variables: { input },
126+
variables: {
127+
input: {
128+
url: notificationUrl,
129+
resourceTypes,
130+
secret: webhookSecret,
131+
enabled: true,
132+
},
133+
},
200134
}),
201135
})
202136

@@ -219,12 +153,6 @@ export const linearHandler: WebhookProviderHandler = {
219153
}
220154

221155
const externalId = result.webhook?.id
222-
if (typeof externalId !== 'string' || !externalId.trim()) {
223-
throw new Error(
224-
'Linear webhook was created but the API response did not include a webhook id.'
225-
)
226-
}
227-
228156
logger.info(
229157
`[${ctx.requestId}] Created Linear webhook ${externalId} for webhook ${ctx.webhook.id}`
230158
)
@@ -293,4 +221,13 @@ export const linearHandler: WebhookProviderHandler = {
293221
})
294222
}
295223
},
224+
225+
extractIdempotencyId(body: unknown) {
226+
const obj = body as Record<string, unknown>
227+
const data = obj.data as Record<string, unknown> | undefined
228+
if (obj.action && data?.id) {
229+
return `${obj.action}:${data.id}`
230+
}
231+
return null
232+
},
296233
}

apps/sim/triggers/linear/comment_created.ts

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
import { LinearIcon } from '@/components/icons'
2-
import { buildCommentOutputs, linearSetupInstructions } from '@/triggers/linear/utils'
2+
import {
3+
buildCommentOutputs,
4+
buildLinearV2SubBlocks,
5+
linearSetupInstructions,
6+
} from '@/triggers/linear/utils'
37
import type { TriggerConfig } from '@/triggers/types'
48

59
export const linearCommentCreatedTrigger: TriggerConfig = {
@@ -78,3 +82,27 @@ export const linearCommentCreatedTrigger: TriggerConfig = {
7882
},
7983
},
8084
}
85+
86+
export const linearCommentCreatedV2Trigger: TriggerConfig = {
87+
id: 'linear_comment_created_v2',
88+
name: 'Linear Comment Created',
89+
provider: 'linear',
90+
description: 'Trigger workflow when a new comment is created in Linear',
91+
version: '2.0.0',
92+
icon: LinearIcon,
93+
subBlocks: buildLinearV2SubBlocks({
94+
triggerId: 'linear_comment_created_v2',
95+
eventType: 'Comment (create)',
96+
}),
97+
outputs: buildCommentOutputs(),
98+
webhook: {
99+
method: 'POST',
100+
headers: {
101+
'Content-Type': 'application/json',
102+
'Linear-Event': 'Comment',
103+
'Linear-Delivery': 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',
104+
'Linear-Signature': 'sha256...',
105+
'User-Agent': 'Linear-Webhook',
106+
},
107+
},
108+
}

apps/sim/triggers/linear/comment_created_v2.ts

Lines changed: 0 additions & 30 deletions
This file was deleted.

apps/sim/triggers/linear/comment_updated.ts

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
import { LinearIcon } from '@/components/icons'
2-
import { buildCommentOutputs, linearSetupInstructions } from '@/triggers/linear/utils'
2+
import {
3+
buildCommentOutputs,
4+
buildLinearV2SubBlocks,
5+
linearSetupInstructions,
6+
} from '@/triggers/linear/utils'
37
import type { TriggerConfig } from '@/triggers/types'
48

59
export const linearCommentUpdatedTrigger: TriggerConfig = {
@@ -78,3 +82,27 @@ export const linearCommentUpdatedTrigger: TriggerConfig = {
7882
},
7983
},
8084
}
85+
86+
export const linearCommentUpdatedV2Trigger: TriggerConfig = {
87+
id: 'linear_comment_updated_v2',
88+
name: 'Linear Comment Updated',
89+
provider: 'linear',
90+
description: 'Trigger workflow when a comment is updated in Linear',
91+
version: '2.0.0',
92+
icon: LinearIcon,
93+
subBlocks: buildLinearV2SubBlocks({
94+
triggerId: 'linear_comment_updated_v2',
95+
eventType: 'Comment (update)',
96+
}),
97+
outputs: buildCommentOutputs(),
98+
webhook: {
99+
method: 'POST',
100+
headers: {
101+
'Content-Type': 'application/json',
102+
'Linear-Event': 'Comment',
103+
'Linear-Delivery': 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',
104+
'Linear-Signature': 'sha256...',
105+
'User-Agent': 'Linear-Webhook',
106+
},
107+
},
108+
}

apps/sim/triggers/linear/comment_updated_v2.ts

Lines changed: 0 additions & 30 deletions
This file was deleted.

apps/sim/triggers/linear/customer_request_created.ts

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
import { LinearIcon } from '@/components/icons'
2-
import { buildCustomerRequestOutputs, linearSetupInstructions } from '@/triggers/linear/utils'
2+
import {
3+
buildCustomerRequestOutputs,
4+
buildLinearV2SubBlocks,
5+
linearSetupInstructions,
6+
} from '@/triggers/linear/utils'
37
import type { TriggerConfig } from '@/triggers/types'
48

59
export const linearCustomerRequestCreatedTrigger: TriggerConfig = {
@@ -78,3 +82,27 @@ export const linearCustomerRequestCreatedTrigger: TriggerConfig = {
7882
},
7983
},
8084
}
85+
86+
export const linearCustomerRequestCreatedV2Trigger: TriggerConfig = {
87+
id: 'linear_customer_request_created_v2',
88+
name: 'Linear Customer Request Created',
89+
provider: 'linear',
90+
description: 'Trigger workflow when a new customer request is created in Linear',
91+
version: '2.0.0',
92+
icon: LinearIcon,
93+
subBlocks: buildLinearV2SubBlocks({
94+
triggerId: 'linear_customer_request_created_v2',
95+
eventType: 'Customer Requests',
96+
}),
97+
outputs: buildCustomerRequestOutputs(),
98+
webhook: {
99+
method: 'POST',
100+
headers: {
101+
'Content-Type': 'application/json',
102+
'Linear-Event': 'CustomerNeed',
103+
'Linear-Delivery': 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',
104+
'Linear-Signature': 'sha256...',
105+
'User-Agent': 'Linear-Webhook',
106+
},
107+
},
108+
}

apps/sim/triggers/linear/customer_request_created_v2.ts

Lines changed: 0 additions & 30 deletions
This file was deleted.

0 commit comments

Comments
 (0)