Fix wws runtime compatibility - use module.exports pattern

- Update api/index.js to use handler export format
- Update chat/index.js to use handler export format
- Update webhook/index.js to use handler export format
- Remove addEventListener('fetch') pattern incompatible with wws
- Remove URL API usage (not available in wws JS runtime)
- Remove spread operator for better compatibility

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2025-12-15 05:56:18 +01:00
parent 9d83a9d2f0
commit 02d32536a0
3 changed files with 72 additions and 68 deletions

View File

@@ -1,22 +1,17 @@
// Health check and API info endpoint // Health check and API info endpoint
// Path: /api // Path: /api
addEventListener('fetch', event => { async function handler(request) {
event.respondWith(handleRequest(event.request))
})
async function handleRequest(request) {
const url = new URL(request.url)
// CORS headers for browser requests // CORS headers for browser requests
const corsHeaders = { const corsHeaders = {
'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS', 'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization', 'Access-Control-Allow-Headers': 'Content-Type, Authorization',
'Content-Type': 'application/json'
} }
if (request.method === 'OPTIONS') { if (request.method === 'OPTIONS') {
return new Response(null, { headers: corsHeaders }) return new Response('', { headers: corsHeaders })
} }
const response = { const response = {
@@ -38,9 +33,9 @@ async function handleRequest(request) {
} }
return new Response(JSON.stringify(response, null, 2), { return new Response(JSON.stringify(response, null, 2), {
headers: { headers: corsHeaders
'Content-Type': 'application/json',
...corsHeaders
}
}) })
} }
// Export for wws
module.exports = { handler }

View File

@@ -1,36 +1,34 @@
// Chat proxy - routes requests to n8n workflow // Chat proxy - routes requests to n8n workflow
// Path: /chat // Path: /chat
addEventListener('fetch', event => { async function handler(request) {
event.respondWith(handleRequest(event.request))
})
async function handleRequest(request) {
const corsHeaders = { const corsHeaders = {
'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS', 'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization', 'Access-Control-Allow-Headers': 'Content-Type, Authorization',
'Content-Type': 'application/json'
} }
if (request.method === 'OPTIONS') { if (request.method === 'OPTIONS') {
return new Response(null, { headers: corsHeaders }) return new Response('', { headers: corsHeaders })
} }
if (request.method !== 'POST') { if (request.method !== 'POST') {
return new Response(JSON.stringify({ error: 'Method not allowed' }), { return new Response(JSON.stringify({ error: 'Method not allowed' }), {
status: 405, status: 405,
headers: { 'Content-Type': 'application/json', ...corsHeaders } headers: corsHeaders
}) })
} }
try { try {
const body = await request.json() const body = await request.json()
const { message, project_id, user_id, provider = 'zai' } = body const { message, project_id, user_id, provider } = body
const selectedProvider = provider || 'zai'
if (!message) { if (!message) {
return new Response(JSON.stringify({ error: 'Message is required' }), { return new Response(JSON.stringify({ error: 'Message is required' }), {
status: 400, status: 400,
headers: { 'Content-Type': 'application/json', ...corsHeaders } headers: corsHeaders
}) })
} }
@@ -39,10 +37,10 @@ async function handleRequest(request) {
method: 'POST', method: 'POST',
headers: { 'Content-Type': 'application/json' }, headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ body: JSON.stringify({
message, message: message,
project_id: project_id || null, project_id: project_id || null,
user_id: user_id || null, user_id: user_id || null,
provider, provider: selectedProvider,
source: 'wws-proxy', source: 'wws-proxy',
timestamp: new Date().toISOString() timestamp: new Date().toISOString()
}) })
@@ -53,10 +51,10 @@ async function handleRequest(request) {
return new Response(JSON.stringify({ return new Response(JSON.stringify({
success: true, success: true,
response: result.response || result, response: result.response || result,
provider, provider: selectedProvider,
timestamp: new Date().toISOString() timestamp: new Date().toISOString()
}), { }), {
headers: { 'Content-Type': 'application/json', ...corsHeaders } headers: corsHeaders
}) })
} catch (error) { } catch (error) {
@@ -65,7 +63,9 @@ async function handleRequest(request) {
error: error.message || 'Internal server error' error: error.message || 'Internal server error'
}), { }), {
status: 500, status: 500,
headers: { 'Content-Type': 'application/json', ...corsHeaders } headers: corsHeaders
}) })
} }
} }
module.exports = { handler }

View File

@@ -1,50 +1,60 @@
// Webhook receiver for external integrations // Webhook receiver for external integrations
// Path: /webhook // Path: /webhook
addEventListener('fetch', event => { async function handler(request) {
event.respondWith(handleRequest(event.request))
})
async function handleRequest(request) {
const corsHeaders = { const corsHeaders = {
'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS', 'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization, X-Webhook-Secret', 'Access-Control-Allow-Headers': 'Content-Type, Authorization, X-Webhook-Secret',
'Content-Type': 'application/json'
} }
if (request.method === 'OPTIONS') { if (request.method === 'OPTIONS') {
return new Response(null, { headers: corsHeaders }) return new Response('', { headers: corsHeaders })
} }
const url = new URL(request.url) // GET request - return webhook info
const path = url.pathname if (request.method === 'GET') {
return new Response(JSON.stringify({
// Log incoming webhook endpoint: '/webhook',
const logEntry = { method: 'POST',
timestamp: new Date().toISOString(), supported_sources: ['gitea', 'stripe', 'supabase', 'default'],
method: request.method, usage: 'POST /webhook?source=gitea with JSON body'
path: path, }, null, 2), {
headers: Object.fromEntries(request.headers.entries()), headers: corsHeaders
query: Object.fromEntries(url.searchParams.entries()) })
} }
// POST request - receive and forward webhook
if (request.method === 'POST') { if (request.method === 'POST') {
try { try {
const timestamp = new Date().toISOString()
// Parse request body
let payload
const contentType = request.headers.get('content-type') || '' const contentType = request.headers.get('content-type') || ''
let payload
if (contentType.includes('application/json')) { if (contentType.includes('application/json')) {
payload = await request.json() payload = await request.json()
} else if (contentType.includes('form')) {
payload = Object.fromEntries(await request.formData())
} else { } else {
payload = await request.text() payload = await request.text()
} }
logEntry.payload = payload // Get source from query params (wws provides request.url as string)
let source = 'default'
// Route based on webhook type/source if (request.url && request.url.includes('?')) {
const source = url.searchParams.get('source') || 'unknown' const queryString = request.url.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 // Forward to appropriate n8n workflow based on source
const webhookMap = { const webhookMap = {
@@ -56,44 +66,43 @@ async function handleRequest(request) {
const targetUrl = webhookMap[source] || webhookMap['default'] const targetUrl = webhookMap[source] || webhookMap['default']
// Async forward (fire and forget for speed) // Forward to n8n (fire and forget)
fetch(targetUrl, { fetch(targetUrl, {
method: 'POST', method: 'POST',
headers: { 'Content-Type': 'application/json' }, headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ body: JSON.stringify({
source, source: source,
payload, payload: payload,
received_at: logEntry.timestamp, received_at: timestamp
headers: logEntry.headers
}) })
}).catch(err => console.error('Forward failed:', err)) }).catch(function(err) {
console.error('Forward failed:', err)
})
return new Response(JSON.stringify({ return new Response(JSON.stringify({
received: true, received: true,
source, source: source,
timestamp: logEntry.timestamp timestamp: timestamp
}), { }), {
headers: { 'Content-Type': 'application/json', ...corsHeaders } headers: corsHeaders
}) })
} catch (error) { } catch (error) {
return new Response(JSON.stringify({ return new Response(JSON.stringify({
received: false, received: false,
error: error.message error: error.message || 'Parse error'
}), { }), {
status: 400, status: 400,
headers: { 'Content-Type': 'application/json', ...corsHeaders } headers: corsHeaders
}) })
} }
} }
// GET request - return webhook info // Method not allowed
return new Response(JSON.stringify({ return new Response(JSON.stringify({ error: 'Method not allowed' }), {
endpoint: '/webhook', status: 405,
method: 'POST', headers: corsHeaders
supported_sources: ['gitea', 'stripe', 'supabase', 'default'],
usage: 'POST /webhook?source=gitea with JSON body'
}), {
headers: { 'Content-Type': 'application/json', ...corsHeaders }
}) })
} }
module.exports = { handler }