fix: move onParse to root and support trailing slash in webhook route

This commit is contained in:
Your Name committed 2026-06-12 23:37:18 -04:00
1 parent ee1c7820c3
commit 32e9fd5ac6
2 files changed
+56 -53

No files matched your search

+5
View File
@@ -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 JWT_SECRET = process.env.JWT_SECRET || "homaroma_secret_2026_scent";
const app = new Elysia() const app = new Elysia()
.onParse(async ({ request }) => {
if (request.url.includes('/api/line/webhook')) {
return await request.text();
}
})
.use( .use(
cors({ cors({
origin: [ origin: [
+51 -53
View File
@@ -3,66 +3,64 @@ import * as line from '@line/bot-sdk';
import { lineConfig, lineClient } from '../lib/lineClient'; import { lineConfig, lineClient } from '../lib/lineClient';
import crypto from 'crypto'; import crypto from 'crypto';
export const lineRoutes = new Elysia({ prefix: '/line' }) const webhookHandler = async ({ request, body, set }: { request: Request, body: any, set: any }) => {
.onParse(async ({ request, contentType }) => { // 1. Verify Signature
if (contentType?.includes('application/json')) { const signature = request.headers.get('x-line-signature');
return await request.text(); if (!signature) {
} set.status = 400;
}) return { error: 'Missing Signature' };
.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' };
}
// In our onParse hook, we intercepted the body and returned it as a raw text string. // 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); const rawBody = typeof body === 'string' ? body : JSON.stringify(body);
if (!rawBody) { if (!rawBody) {
set.status = 400; set.status = 400;
return { error: 'Empty Body' }; return { error: 'Empty Body' };
} }
const hash = crypto const hash = crypto
.createHmac('SHA256', lineConfig.channelSecret) .createHmac('SHA256', lineConfig.channelSecret)
.update(rawBody) .update(rawBody)
.digest('base64'); .digest('base64');
console.log('[LINE Webhook] Signature Check - Received:', signature, 'Calculated:', hash); console.log('[LINE Webhook] Signature Check - Received:', signature, 'Calculated:', hash);
if (hash !== signature) { if (hash !== signature) {
console.error('[LINE Webhook] Invalid Signature Blocked'); console.error('[LINE Webhook] Invalid Signature Blocked');
set.status = 401; set.status = 401;
return { error: 'Invalid Signature' }; return { error: 'Invalid Signature' };
} }
try { try {
const parsedBody = typeof body === 'string' ? JSON.parse(body) : body; const parsedBody = typeof body === 'string' ? JSON.parse(body) : body;
const events: line.WebhookEvent[] = parsedBody.events || []; const events: line.WebhookEvent[] = parsedBody.events || [];
for (const event of events) { for (const event of events) {
if (event.type === 'message' && event.message.type === 'text') { if (event.type === 'message' && event.message.type === 'text') {
// Ignore verification dummy tokens // Ignore verification dummy tokens
if (event.replyToken === '00000000000000000000000000000000' || event.replyToken === 'ffffffffffffffffffffffffffffffff') { if (event.replyToken === '00000000000000000000000000000000' || event.replyToken === 'ffffffffffffffffffffffffffffffff') {
continue; continue;
}
// Simple echo bot for now
await lineClient.replyMessage({
replyToken: event.replyToken,
messages: [{
type: 'text',
text: `คุณส่งข้อความว่า: ${event.message.text}`
}]
});
} }
// 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);