- Switch to addEventListener('fetch') pattern
- Use response.headers.set() instead of headers object
- wws uses Fetch API event handler pattern
- Avoid URL constructor (not available in wws)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
109 lines
3.4 KiB
JavaScript
109 lines
3.4 KiB
JavaScript
// Webhook receiver for external integrations
|
|
// Path: /webhook
|
|
|
|
const handler = async (request) => {
|
|
if (request.method === 'OPTIONS') {
|
|
let response = new Response('')
|
|
response.headers.set('Access-Control-Allow-Origin', '*')
|
|
response.headers.set('Access-Control-Allow-Methods', 'GET, POST, OPTIONS')
|
|
response.headers.set('Access-Control-Allow-Headers', 'Content-Type, Authorization, X-Webhook-Secret')
|
|
return response
|
|
}
|
|
|
|
// GET request - return webhook info
|
|
if (request.method === 'GET') {
|
|
let response = new Response(JSON.stringify({
|
|
endpoint: '/webhook',
|
|
method: 'POST',
|
|
supported_sources: ['gitea', 'stripe', 'supabase', 'default'],
|
|
usage: 'POST /webhook?source=gitea with JSON body'
|
|
}, null, 2))
|
|
response.headers.set('Content-Type', 'application/json')
|
|
response.headers.set('Access-Control-Allow-Origin', '*')
|
|
return response
|
|
}
|
|
|
|
// POST request - receive and forward webhook
|
|
if (request.method === 'POST') {
|
|
try {
|
|
const timestamp = new Date().toISOString()
|
|
|
|
// Parse request body
|
|
let payload
|
|
const contentType = request.headers.get('content-type') || ''
|
|
|
|
if (contentType.indexOf('application/json') !== -1) {
|
|
payload = await request.json()
|
|
} else {
|
|
payload = await request.text()
|
|
}
|
|
|
|
// Get source from query params
|
|
let source = 'default'
|
|
const reqUrl = request.url || ''
|
|
if (reqUrl.indexOf('?') !== -1) {
|
|
const queryString = reqUrl.split('?')[1]
|
|
if (queryString) {
|
|
const params = queryString.split('&')
|
|
for (let i = 0; i < params.length; i++) {
|
|
const pair = params[i].split('=')
|
|
if (pair[0] === 'source' && pair[1]) {
|
|
source = decodeURIComponent(pair[1])
|
|
break
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Forward to appropriate n8n workflow based on source
|
|
const webhookMap = {
|
|
'gitea': 'https://n8n.mylder.io/webhook/gitea-event',
|
|
'stripe': 'https://n8n.mylder.io/webhook/stripe-event',
|
|
'supabase': 'https://n8n.mylder.io/webhook/supabase-event',
|
|
'default': 'https://n8n.mylder.io/webhook/generic-event'
|
|
}
|
|
|
|
const targetUrl = webhookMap[source] || webhookMap['default']
|
|
|
|
// Forward to n8n (fire and forget)
|
|
fetch(targetUrl, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({
|
|
source: source,
|
|
payload: payload,
|
|
received_at: timestamp
|
|
})
|
|
})
|
|
|
|
let response = new Response(JSON.stringify({
|
|
received: true,
|
|
source: source,
|
|
timestamp: timestamp
|
|
}))
|
|
response.headers.set('Content-Type', 'application/json')
|
|
response.headers.set('Access-Control-Allow-Origin', '*')
|
|
return response
|
|
|
|
} catch (error) {
|
|
let response = new Response(JSON.stringify({
|
|
received: false,
|
|
error: error.message || 'Parse error'
|
|
}))
|
|
response.headers.set('Content-Type', 'application/json')
|
|
response.headers.set('Access-Control-Allow-Origin', '*')
|
|
return response
|
|
}
|
|
}
|
|
|
|
// Method not allowed
|
|
let response = new Response(JSON.stringify({ error: 'Method not allowed' }))
|
|
response.headers.set('Content-Type', 'application/json')
|
|
response.headers.set('Access-Control-Allow-Origin', '*')
|
|
return response
|
|
}
|
|
|
|
addEventListener('fetch', event => {
|
|
return event.respondWith(handler(event.request))
|
|
})
|