Skip to content
Merged
12 changes: 6 additions & 6 deletions apps/docs/components/ui/icon-mapping.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ import {
EyeIcon,
FirecrawlIcon,
FirefliesIcon,
GitLabIcon,
GithubIcon,
GitLabIcon,
GmailIcon,
GongIcon,
GoogleBooksIcon,
Expand Down Expand Up @@ -71,9 +71,9 @@ import {
LinearIcon,
LinkedInIcon,
LinkupIcon,
MailServerIcon,
MailchimpIcon,
MailgunIcon,
MailServerIcon,
Mem0Icon,
MicrosoftDataverseIcon,
MicrosoftExcelIcon,
Expand Down Expand Up @@ -106,8 +106,6 @@ import {
ResendIcon,
RevenueCatIcon,
S3Icon,
SQSIcon,
STTIcon,
SalesforceIcon,
SearchIcon,
SendgridIcon,
Expand All @@ -119,17 +117,19 @@ import {
SimilarwebIcon,
SlackIcon,
SmtpIcon,
SQSIcon,
SshIcon,
STTIcon,
StagehandIcon,
StripeIcon,
SupabaseIcon,
TTSIcon,
TavilyIcon,
TelegramIcon,
TextractIcon,
TinybirdIcon,
TranslateIcon,
TrelloIcon,
TTSIcon,
TwilioIcon,
TypeformIcon,
UpstashIcon,
Expand All @@ -140,11 +140,11 @@ import {
WhatsAppIcon,
WikipediaIcon,
WordpressIcon,
xIcon,
YouTubeIcon,
ZendeskIcon,
ZepIcon,
ZoomIcon,
xIcon,
} from '@/components/icons'

type IconComponent = ComponentType<SVGProps<SVGSVGElement>>
Expand Down
2 changes: 1 addition & 1 deletion apps/docs/content/docs/en/tools/meta.json
Original file line number Diff line number Diff line change
Expand Up @@ -145,4 +145,4 @@
"zep",
"zoom"
]
}
}
40 changes: 40 additions & 0 deletions apps/sim/blocks/blocks/confluence.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { BlockConfig } from '@/blocks/types'
import { AuthMode } from '@/blocks/types'
import { normalizeFileInput } from '@/blocks/utils'
import type { ConfluenceResponse } from '@/tools/confluence/types'
import { getTrigger } from '@/triggers'

export const ConfluenceBlock: BlockConfig<ConfluenceResponse> = {
type: 'confluence',
Expand Down Expand Up @@ -838,7 +839,46 @@ export const ConfluenceV2Block: BlockConfig<ConfluenceResponse> = {
],
},
},

// Trigger subBlocks
...getTrigger('confluence_page_created').subBlocks,
...getTrigger('confluence_page_updated').subBlocks,
...getTrigger('confluence_page_removed').subBlocks,
...getTrigger('confluence_page_moved').subBlocks,
...getTrigger('confluence_comment_created').subBlocks,
...getTrigger('confluence_comment_removed').subBlocks,
...getTrigger('confluence_blog_created').subBlocks,
...getTrigger('confluence_blog_updated').subBlocks,
...getTrigger('confluence_blog_removed').subBlocks,
...getTrigger('confluence_attachment_created').subBlocks,
...getTrigger('confluence_attachment_removed').subBlocks,
...getTrigger('confluence_space_created').subBlocks,
...getTrigger('confluence_space_updated').subBlocks,
...getTrigger('confluence_label_added').subBlocks,
...getTrigger('confluence_label_removed').subBlocks,
...getTrigger('confluence_webhook').subBlocks,
],
triggers: {
enabled: true,
available: [
'confluence_page_created',
'confluence_page_updated',
'confluence_page_removed',
'confluence_page_moved',
'confluence_comment_created',
'confluence_comment_removed',
'confluence_blog_created',
'confluence_blog_updated',
'confluence_blog_removed',
'confluence_attachment_created',
'confluence_attachment_removed',
'confluence_space_created',
'confluence_space_updated',
'confluence_label_added',
'confluence_label_removed',
'confluence_webhook',
],
},
tools: {
access: [
// Page Tools
Expand Down
53 changes: 50 additions & 3 deletions apps/sim/lib/webhooks/processor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import { getWorkspaceBilledAccountUserId } from '@/lib/workspaces/utils'
import { resolveOAuthAccountId } from '@/app/api/auth/oauth/utils'
import { executeWebhookJob } from '@/background/webhook-execution'
import { resolveEnvVarReferences } from '@/executor/utils/reference-validation'
import { isConfluencePayloadMatch } from '@/triggers/confluence/utils'
import { isGitHubEventMatch } from '@/triggers/github/utils'
import { isHubSpotContactEventMatch } from '@/triggers/hubspot/utils'
import { isJiraEventMatch } from '@/triggers/jira/utils'
Expand Down Expand Up @@ -608,7 +609,7 @@ export async function verifyProviderAuth(
}

if (foundWebhook.provider === 'linear') {
const secret = providerConfig.secret as string | undefined
const secret = providerConfig.webhookSecret as string | undefined
Comment thread
waleedlatif1 marked this conversation as resolved.

if (secret) {
const signature = request.headers.get('Linear-Signature')
Expand Down Expand Up @@ -683,7 +684,7 @@ export async function verifyProviderAuth(
}

if (foundWebhook.provider === 'jira') {
const secret = providerConfig.secret as string | undefined
const secret = providerConfig.webhookSecret as string | undefined

if (secret) {
const signature = request.headers.get('X-Hub-Signature')
Expand All @@ -707,8 +708,33 @@ export async function verifyProviderAuth(
}
}

if (foundWebhook.provider === 'confluence') {
const secret = providerConfig.webhookSecret as string | undefined

if (secret) {
const signature = request.headers.get('X-Hub-Signature')

if (!signature) {
logger.warn(`[${requestId}] Confluence webhook missing signature header`)
return new NextResponse('Unauthorized - Missing Confluence signature', { status: 401 })
}

const isValidSignature = validateJiraSignature(secret, signature, rawBody)

if (!isValidSignature) {
logger.warn(`[${requestId}] Confluence signature verification failed`, {
signatureLength: signature.length,
secretLength: secret.length,
})
return new NextResponse('Unauthorized - Invalid Confluence signature', { status: 401 })
}

logger.debug(`[${requestId}] Confluence signature verified successfully`)
}
}

if (foundWebhook.provider === 'github') {
const secret = providerConfig.secret as string | undefined
const secret = providerConfig.webhookSecret as string | undefined

if (secret) {
// GitHub supports both SHA-256 (preferred) and SHA-1 (legacy)
Expand Down Expand Up @@ -930,6 +956,27 @@ export async function queueWebhookExecution(
}
}

if (foundWebhook.provider === 'confluence') {
const providerConfig = (foundWebhook.providerConfig as Record<string, any>) || {}
const triggerId = providerConfig.triggerId as string | undefined

if (triggerId && !isConfluencePayloadMatch(triggerId, body)) {
logger.debug(
`[${options.requestId}] Confluence payload mismatch for trigger ${triggerId}. Skipping execution.`,
{
webhookId: foundWebhook.id,
workflowId: foundWorkflow.id,
triggerId,
bodyKeys: Object.keys(body),
}
)

return NextResponse.json({
message: 'Payload does not match trigger configuration. Ignoring.',
})
}
}

if (foundWebhook.provider === 'hubspot') {
const providerConfig = (foundWebhook.providerConfig as Record<string, any>) || {}
const triggerId = providerConfig.triggerId as string | undefined
Expand Down
47 changes: 47 additions & 0 deletions apps/sim/lib/webhooks/utils.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1197,6 +1197,53 @@ export async function formatWebhookInput(
return extractIssueData(body)
}

if (foundWebhook.provider === 'confluence') {
const {
extractPageData,
extractCommentData,
extractBlogData,
extractAttachmentData,
extractSpaceData,
extractLabelData,
} = await import('@/triggers/confluence/utils')

const providerConfig = (foundWebhook.providerConfig as Record<string, any>) || {}
const triggerId = providerConfig.triggerId as string | undefined

if (triggerId?.startsWith('confluence_comment_')) {
return extractCommentData(body)
}
if (triggerId?.startsWith('confluence_blog_')) {
return extractBlogData(body)
}
if (triggerId?.startsWith('confluence_attachment_')) {
return extractAttachmentData(body)
}
if (triggerId?.startsWith('confluence_space_')) {
return extractSpaceData(body)
}
if (triggerId?.startsWith('confluence_label_')) {
return extractLabelData(body)
}
// Generic webhook — preserve all entity fields since event type varies
if (triggerId === 'confluence_webhook') {
return {
timestamp: body.timestamp,
userAccountId: body.userAccountId,
accountType: body.accountType,
page: body.page || null,
comment: body.comment || null,
blog: body.blog || body.blogpost || null,
attachment: body.attachment || null,
space: body.space || null,
label: body.label || null,
content: body.content || null,
}
}
// Default: page events
return extractPageData(body)
}

if (foundWebhook.provider === 'stripe') {
return body
}
Expand Down
41 changes: 41 additions & 0 deletions apps/sim/triggers/confluence/attachment_created.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { ConfluenceIcon } from '@/components/icons'
import { buildTriggerSubBlocks } from '@/triggers'
import {
buildAttachmentOutputs,
buildConfluenceAttachmentExtraFields,
confluenceSetupInstructions,
confluenceTriggerOptions,
} from '@/triggers/confluence/utils'
import type { TriggerConfig } from '@/triggers/types'

/**
* Confluence Attachment Created Trigger
*
* Triggers when a new attachment is uploaded to a page or blog post in Confluence.
*/
export const confluenceAttachmentCreatedTrigger: TriggerConfig = {
id: 'confluence_attachment_created',
name: 'Confluence Attachment Created',
provider: 'confluence',
description: 'Trigger workflow when an attachment is uploaded in Confluence',
version: '1.0.0',
icon: ConfluenceIcon,

subBlocks: buildTriggerSubBlocks({
triggerId: 'confluence_attachment_created',
triggerOptions: confluenceTriggerOptions,
setupInstructions: confluenceSetupInstructions('attachment_created'),
extraFields: buildConfluenceAttachmentExtraFields('confluence_attachment_created'),
}),

outputs: buildAttachmentOutputs(),

webhook: {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Hub-Signature': 'sha256=...',
'X-Atlassian-Webhook-Identifier': 'unique-webhook-id',
},
},
}
41 changes: 41 additions & 0 deletions apps/sim/triggers/confluence/attachment_removed.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { ConfluenceIcon } from '@/components/icons'
import { buildTriggerSubBlocks } from '@/triggers'
import {
buildAttachmentOutputs,
buildConfluenceAttachmentExtraFields,
confluenceSetupInstructions,
confluenceTriggerOptions,
} from '@/triggers/confluence/utils'
import type { TriggerConfig } from '@/triggers/types'

/**
* Confluence Attachment Removed Trigger
*
* Triggers when an attachment is removed or trashed from a page or blog post in Confluence.
*/
export const confluenceAttachmentRemovedTrigger: TriggerConfig = {
id: 'confluence_attachment_removed',
name: 'Confluence Attachment Removed',
provider: 'confluence',
description: 'Trigger workflow when an attachment is removed in Confluence',
version: '1.0.0',
icon: ConfluenceIcon,

subBlocks: buildTriggerSubBlocks({
triggerId: 'confluence_attachment_removed',
triggerOptions: confluenceTriggerOptions,
setupInstructions: confluenceSetupInstructions('attachment_removed'),
extraFields: buildConfluenceAttachmentExtraFields('confluence_attachment_removed'),
}),

outputs: buildAttachmentOutputs(),

webhook: {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Hub-Signature': 'sha256=...',
'X-Atlassian-Webhook-Identifier': 'unique-webhook-id',
},
},
}
41 changes: 41 additions & 0 deletions apps/sim/triggers/confluence/blog_created.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { ConfluenceIcon } from '@/components/icons'
import { buildTriggerSubBlocks } from '@/triggers'
import {
buildBlogOutputs,
buildConfluenceExtraFields,
confluenceSetupInstructions,
confluenceTriggerOptions,
} from '@/triggers/confluence/utils'
import type { TriggerConfig } from '@/triggers/types'

/**
* Confluence Blog Post Created Trigger
*
* Triggers when a new blog post is created in Confluence.
*/
export const confluenceBlogCreatedTrigger: TriggerConfig = {
id: 'confluence_blog_created',
name: 'Confluence Blog Post Created',
provider: 'confluence',
description: 'Trigger workflow when a blog post is created in Confluence',
version: '1.0.0',
icon: ConfluenceIcon,

subBlocks: buildTriggerSubBlocks({
triggerId: 'confluence_blog_created',
triggerOptions: confluenceTriggerOptions,
setupInstructions: confluenceSetupInstructions('blog_created'),
extraFields: buildConfluenceExtraFields('confluence_blog_created'),
}),

outputs: buildBlogOutputs(),

webhook: {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Hub-Signature': 'sha256=...',
'X-Atlassian-Webhook-Identifier': 'unique-webhook-id',
},
},
}
Loading