Skip to content

Commit 46ca31e

Browse files
author
waleed
committed
fix(webhooks): use next public app url instead of request origin for webhook registration
1 parent 09186bd commit 46ca31e

3 files changed

Lines changed: 24 additions & 56 deletions

File tree

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

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -282,11 +282,13 @@ export async function DELETE(
282282

283283
if (!resolvedExternalId) {
284284
try {
285-
const requestOrigin = new URL(request.url).origin
286-
const effectiveOrigin = requestOrigin.includes('localhost')
287-
? env.NEXT_PUBLIC_APP_URL || requestOrigin
288-
: requestOrigin
289-
const expectedNotificationUrl = `${effectiveOrigin}/api/webhooks/trigger/${foundWebhook.path}`
285+
if (!env.NEXT_PUBLIC_APP_URL) {
286+
logger.error(
287+
`[${requestId}] NEXT_PUBLIC_APP_URL not configured, cannot match Airtable webhook`
288+
)
289+
throw new Error('NEXT_PUBLIC_APP_URL must be configured')
290+
}
291+
const expectedNotificationUrl = `${env.NEXT_PUBLIC_APP_URL}/api/webhooks/trigger/${foundWebhook.path}`
290292

291293
const listUrl = `https://api.airtable.com/v0/bases/${baseId}/webhooks`
292294
const listResp = await fetch(listUrl, {

apps/sim/app/api/webhooks/route.ts

Lines changed: 12 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -432,25 +432,20 @@ async function createAirtableWebhookSubscription(
432432
logger.warn(
433433
`[${requestId}] Could not retrieve Airtable access token for user ${userId}. Cannot create webhook in Airtable.`
434434
)
435-
// Instead of silently returning, throw an error with clear user guidance
436435
throw new Error(
437436
'Airtable account connection required. Please connect your Airtable account in the trigger configuration and try again.'
438437
)
439438
}
440439

441-
const requestOrigin = new URL(request.url).origin
442-
// Ensure origin does not point to localhost for external API calls
443-
const effectiveOrigin = requestOrigin.includes('localhost')
444-
? env.NEXT_PUBLIC_APP_URL || requestOrigin // Use env var if available, fallback to original
445-
: requestOrigin
446-
447-
const notificationUrl = `${effectiveOrigin}/api/webhooks/trigger/${path}`
448-
if (effectiveOrigin !== requestOrigin) {
449-
logger.debug(
450-
`[${requestId}] Remapped localhost origin to ${effectiveOrigin} for notificationUrl`
440+
if (!env.NEXT_PUBLIC_APP_URL) {
441+
logger.error(
442+
`[${requestId}] NEXT_PUBLIC_APP_URL not configured, cannot register Airtable webhook`
451443
)
444+
throw new Error('NEXT_PUBLIC_APP_URL must be configured for Airtable webhook registration')
452445
}
453446

447+
const notificationUrl = `${env.NEXT_PUBLIC_APP_URL}/api/webhooks/trigger/${path}`
448+
454449
const airtableApiUrl = `https://api.airtable.com/v0/bases/${baseId}/webhooks`
455450

456451
const specification: any = {
@@ -549,19 +544,15 @@ async function createTelegramWebhookSubscription(
549544
return // Cannot proceed without botToken
550545
}
551546

552-
const requestOrigin = new URL(request.url).origin
553-
// Ensure origin does not point to localhost for external API calls
554-
const effectiveOrigin = requestOrigin.includes('localhost')
555-
? env.NEXT_PUBLIC_APP_URL || requestOrigin // Use env var if available, fallback to original
556-
: requestOrigin
557-
558-
const notificationUrl = `${effectiveOrigin}/api/webhooks/trigger/${path}`
559-
if (effectiveOrigin !== requestOrigin) {
560-
logger.debug(
561-
`[${requestId}] Remapped localhost origin to ${effectiveOrigin} for notificationUrl`
547+
if (!env.NEXT_PUBLIC_APP_URL) {
548+
logger.error(
549+
`[${requestId}] NEXT_PUBLIC_APP_URL not configured, cannot register Telegram webhook`
562550
)
551+
throw new Error('NEXT_PUBLIC_APP_URL must be configured for Telegram webhook registration')
563552
}
564553

554+
const notificationUrl = `${env.NEXT_PUBLIC_APP_URL}/api/webhooks/trigger/${path}`
555+
565556
const telegramApiUrl = `https://api.telegram.org/bot${botToken}/setWebhook`
566557

567558
const requestBody: any = {

apps/sim/app/api/webhooks/test/route.ts

Lines changed: 5 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { db } from '@sim/db'
22
import { webhook } from '@sim/db/schema'
33
import { eq } from 'drizzle-orm'
44
import { type NextRequest, NextResponse } from 'next/server'
5+
import { env } from '@/lib/env'
56
import { createLogger } from '@/lib/logs/console/logger'
67
import { generateRequestId } from '@/lib/utils'
78

@@ -13,7 +14,6 @@ export async function GET(request: NextRequest) {
1314
const requestId = generateRequestId()
1415

1516
try {
16-
// Get the webhook ID and provider from the query parameters
1717
const { searchParams } = new URL(request.url)
1818
const webhookId = searchParams.get('id')
1919

@@ -24,7 +24,6 @@ export async function GET(request: NextRequest) {
2424

2525
logger.debug(`[${requestId}] Testing webhook with ID: ${webhookId}`)
2626

27-
// Find the webhook in the database
2827
const webhooks = await db.select().from(webhook).where(eq(webhook.id, webhookId)).limit(1)
2928

3029
if (webhooks.length === 0) {
@@ -36,8 +35,7 @@ export async function GET(request: NextRequest) {
3635
const provider = foundWebhook.provider || 'generic'
3736
const providerConfig = (foundWebhook.providerConfig as Record<string, any>) || {}
3837

39-
// Construct the webhook URL
40-
const baseUrl = new URL(request.url).origin
38+
const baseUrl = env.NEXT_PUBLIC_APP_URL || new URL(request.url).origin
4139
const webhookUrl = `${baseUrl}/api/webhooks/trigger/${foundWebhook.path}`
4240

4341
logger.info(`[${requestId}] Testing webhook for provider: ${provider}`, {
@@ -46,7 +44,6 @@ export async function GET(request: NextRequest) {
4644
isActive: foundWebhook.isActive,
4745
})
4846

49-
// Provider-specific test logic
5047
switch (provider) {
5148
case 'whatsapp': {
5249
const verificationToken = providerConfig.verificationToken
@@ -59,30 +56,25 @@ export async function GET(request: NextRequest) {
5956
)
6057
}
6158

62-
// Generate a test challenge
6359
const challenge = `test_${Date.now()}`
6460

65-
// Construct the WhatsApp verification URL
6661
const whatsappUrl = `${webhookUrl}?hub.mode=subscribe&hub.verify_token=${verificationToken}&hub.challenge=${challenge}`
6762

6863
logger.debug(`[${requestId}] Testing WhatsApp webhook verification`, {
6964
webhookId,
7065
challenge,
7166
})
7267

73-
// Make a request to the webhook endpoint
7468
const response = await fetch(whatsappUrl, {
7569
headers: {
7670
'User-Agent': 'facebookplatform/1.0',
7771
},
7872
})
7973

80-
// Get the response details
8174
const status = response.status
8275
const contentType = response.headers.get('content-type')
8376
const responseText = await response.text()
8477

85-
// Check if the test was successful
8678
const success = status === 200 && responseText === challenge
8779

8880
if (success) {
@@ -139,7 +131,6 @@ export async function GET(request: NextRequest) {
139131
)
140132
}
141133

142-
// Test the webhook endpoint with a simple message to check if it's reachable
143134
const testMessage = {
144135
update_id: 12345,
145136
message: {
@@ -165,7 +156,6 @@ export async function GET(request: NextRequest) {
165156
url: webhookUrl,
166157
})
167158

168-
// Make a test request to the webhook endpoint
169159
const response = await fetch(webhookUrl, {
170160
method: 'POST',
171161
headers: {
@@ -175,16 +165,12 @@ export async function GET(request: NextRequest) {
175165
body: JSON.stringify(testMessage),
176166
})
177167

178-
// Get the response details
179168
const status = response.status
180169
let responseText = ''
181170
try {
182171
responseText = await response.text()
183-
} catch (_e) {
184-
// Ignore if we can't get response text
185-
}
172+
} catch (_e) {}
186173

187-
// Consider success if we get a 2xx response
188174
const success = status >= 200 && status < 300
189175

190176
if (success) {
@@ -196,7 +182,6 @@ export async function GET(request: NextRequest) {
196182
})
197183
}
198184

199-
// Get webhook info from Telegram API
200185
let webhookInfo = null
201186
try {
202187
const webhookInfoUrl = `https://api.telegram.org/bot${botToken}/getWebhookInfo`
@@ -215,7 +200,6 @@ export async function GET(request: NextRequest) {
215200
logger.warn(`[${requestId}] Failed to get Telegram webhook info`, e)
216201
}
217202

218-
// Format the curl command for testing
219203
const curlCommand = [
220204
`curl -X POST "${webhookUrl}"`,
221205
`-H "Content-Type: application/json"`,
@@ -288,16 +272,13 @@ export async function GET(request: NextRequest) {
288272
}
289273

290274
case 'generic': {
291-
// Get the general webhook configuration
292275
const token = providerConfig.token
293276
const secretHeaderName = providerConfig.secretHeaderName
294277
const requireAuth = providerConfig.requireAuth
295278
const allowedIps = providerConfig.allowedIps
296279

297-
// Generate sample curl command for testing
298280
let curlCommand = `curl -X POST "${webhookUrl}" -H "Content-Type: application/json"`
299281

300-
// Add auth headers to the curl command if required
301282
if (requireAuth && token) {
302283
if (secretHeaderName) {
303284
curlCommand += ` -H "${secretHeaderName}: ${token}"`
@@ -306,7 +287,6 @@ export async function GET(request: NextRequest) {
306287
}
307288
}
308289

309-
// Add a sample payload
310290
curlCommand += ` -d '{"event":"test_event","timestamp":"${new Date().toISOString()}"}'`
311291

312292
logger.info(`[${requestId}] General webhook test successful: ${webhookId}`)
@@ -391,7 +371,6 @@ export async function GET(request: NextRequest) {
391371
})
392372
}
393373

394-
// Add the Airtable test case
395374
case 'airtable': {
396375
const baseId = providerConfig.baseId
397376
const tableId = providerConfig.tableId
@@ -408,7 +387,6 @@ export async function GET(request: NextRequest) {
408387
)
409388
}
410389

411-
// Define a sample payload structure
412390
const samplePayload = {
413391
webhook: {
414392
id: 'whiYOUR_WEBHOOK_ID',
@@ -418,16 +396,15 @@ export async function GET(request: NextRequest) {
418396
},
419397
payloadFormat: 'v0',
420398
actionMetadata: {
421-
source: 'tableOrViewChange', // Example source
399+
source: 'tableOrViewChange',
422400
sourceMetadata: {},
423401
},
424402
payloads: [
425403
{
426404
timestamp: new Date().toISOString(),
427-
baseTransactionNumber: Date.now(), // Example transaction number
405+
baseTransactionNumber: Date.now(),
428406
changedTablesById: {
429407
[tableId]: {
430-
// Example changes - structure may vary based on actual event
431408
changedRecordsById: {
432409
recSAMPLEID1: {
433410
current: { cellValuesByFieldId: { fldSAMPLEID: 'New Value' } },
@@ -442,7 +419,6 @@ export async function GET(request: NextRequest) {
442419
],
443420
}
444421

445-
// Generate sample curl command
446422
let curlCommand = `curl -X POST "${webhookUrl}" -H "Content-Type: application/json"`
447423
curlCommand += ` -d '${JSON.stringify(samplePayload, null, 2)}'`
448424

@@ -519,7 +495,6 @@ export async function GET(request: NextRequest) {
519495
}
520496

521497
default: {
522-
// Generic webhook test
523498
logger.info(`[${requestId}] Generic webhook test successful: ${webhookId}`)
524499
return NextResponse.json({
525500
success: true,

0 commit comments

Comments
 (0)