diff --git a/apps/backend/src/index.ts b/apps/backend/src/index.ts index bc6431d..864d6cf 100644 --- a/apps/backend/src/index.ts +++ b/apps/backend/src/index.ts @@ -29,6 +29,11 @@ const CORS_ORIGIN = process.env.CORS_ORIGIN || "http://localhost:5173"; const JWT_SECRET = process.env.JWT_SECRET || "homaroma_secret_2026_scent"; const app = new Elysia() + .onParse(async ({ request }) => { + if (request.url.includes('/api/line/webhook')) { + return await request.text(); + } + }) .use( cors({ origin: [ diff --git a/apps/backend/src/routes/line.ts b/apps/backend/src/routes/line.ts index af2781b..d22eb28 100644 --- a/apps/backend/src/routes/line.ts +++ b/apps/backend/src/routes/line.ts @@ -3,66 +3,64 @@ import * as line from '@line/bot-sdk'; import { lineConfig, lineClient } from '../lib/lineClient'; import crypto from 'crypto'; -export const lineRoutes = new Elysia({ prefix: '/line' }) - .onParse(async ({ request, contentType }) => { - if (contentType?.includes('application/json')) { - return await request.text(); - } - }) - .post('/webhook', async ({ request, body, set }) => { - // 1. Verify Signature - const signature = request.headers.get('x-line-signature'); - if (!signature) { - set.status = 400; - return { error: 'Missing Signature' }; - } +const webhookHandler = async ({ request, body, set }: { request: Request, body: any, set: any }) => { + // 1. Verify Signature + const signature = request.headers.get('x-line-signature'); + if (!signature) { + set.status = 400; + return { error: 'Missing Signature' }; + } + + // In our global onParse hook, we intercepted the body and returned it as a raw text string. + const rawBody = typeof body === 'string' ? body : JSON.stringify(body); + + if (!rawBody) { + set.status = 400; + return { error: 'Empty Body' }; + } + + const hash = crypto + .createHmac('SHA256', lineConfig.channelSecret) + .update(rawBody) + .digest('base64'); + + console.log('[LINE Webhook] Signature Check - Received:', signature, 'Calculated:', hash); + + if (hash !== signature) { + console.error('[LINE Webhook] Invalid Signature Blocked'); + set.status = 401; + return { error: 'Invalid Signature' }; + } + + try { + const parsedBody = typeof body === 'string' ? JSON.parse(body) : body; + const events: line.WebhookEvent[] = parsedBody.events || []; - // In our onParse hook, we intercepted the body and returned it as a raw text string. - const rawBody = typeof body === 'string' ? body : JSON.stringify(body); - - if (!rawBody) { - set.status = 400; - return { error: 'Empty Body' }; - } - - const hash = crypto - .createHmac('SHA256', lineConfig.channelSecret) - .update(rawBody) - .digest('base64'); - - console.log('[LINE Webhook] Signature Check - Received:', signature, 'Calculated:', hash); - - if (hash !== signature) { - console.error('[LINE Webhook] Invalid Signature Blocked'); - set.status = 401; - return { error: 'Invalid Signature' }; - } - - try { - const parsedBody = typeof body === 'string' ? JSON.parse(body) : body; - const events: line.WebhookEvent[] = parsedBody.events || []; - - for (const event of events) { - if (event.type === 'message' && event.message.type === 'text') { - // Ignore verification dummy tokens - if (event.replyToken === '00000000000000000000000000000000' || event.replyToken === 'ffffffffffffffffffffffffffffffff') { - continue; - } - - // Simple echo bot for now - await lineClient.replyMessage({ - replyToken: event.replyToken, - messages: [{ - type: 'text', - text: `คุณส่งข้อความว่า: ${event.message.text}` - }] - }); + for (const event of events) { + if (event.type === 'message' && event.message.type === 'text') { + // Ignore verification dummy tokens + if (event.replyToken === '00000000000000000000000000000000' || event.replyToken === 'ffffffffffffffffffffffffffffffff') { + continue; } + + // Simple echo bot for now + await lineClient.replyMessage({ + replyToken: event.replyToken, + messages: [{ + type: 'text', + text: `คุณส่งข้อความว่า: ${event.message.text}` + }] + }); } - return { success: true }; - } catch (err) { - console.error('[LINE Webhook] Error:', err); - set.status = 500; - return { error: 'Webhook processing failed' }; } - }); + return { success: true }; + } catch (err) { + console.error('[LINE Webhook] Error:', err); + set.status = 500; + return { error: 'Webhook processing failed' }; + } +}; + +export const lineRoutes = new Elysia({ prefix: '/line' }) + .post('/webhook', webhookHandler) + .post('/webhook/', webhookHandler);