This commit is contained in:
Your Name committed 2026-06-12 23:21:14 -04:00
1 parent 36f8ff0759
commit 182d0349fa
5 files changed
+70 -3

No files matched your search

+2 -1
View File
@@ -12,11 +12,12 @@
"author": "", "author": "",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"@homaroma/types": "*",
"@elysiajs/cors": "^1.2.2", "@elysiajs/cors": "^1.2.2",
"@elysiajs/jwt": "^1.4.1", "@elysiajs/jwt": "^1.4.1",
"@elysiajs/static": "^1.4.7", "@elysiajs/static": "^1.4.7",
"@elysiajs/swagger": "^1.2.1", "@elysiajs/swagger": "^1.2.1",
"@homaroma/types": "*",
"@line/bot-sdk": "^11.0.1",
"@prisma/adapter-pg": "^7.6.0", "@prisma/adapter-pg": "^7.6.0",
"@prisma/client": "^7.6.0", "@prisma/client": "^7.6.0",
"bcryptjs": "^3.0.3", "bcryptjs": "^3.0.3",
+2 -1
View File
@@ -23,6 +23,7 @@ import { promotionsRoute } from "./routes/promotions";
import { pagesRoute } from "./routes/pages"; import { pagesRoute } from "./routes/pages";
import { paymentsRoute } from "./routes/payments"; import { paymentsRoute } from "./routes/payments";
import { profileRoute } from "./routes/profile"; import { profileRoute } from "./routes/profile";
import { lineRoutes } from "./routes/line";
const PORT = process.env.PORT ? parseInt(process.env.PORT) : 3051; const PORT = process.env.PORT ? parseInt(process.env.PORT) : 3051;
const CORS_ORIGIN = process.env.CORS_ORIGIN || "http://localhost:5173"; 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";
@@ -88,7 +89,7 @@ const app = new Elysia()
return file; return file;
}) })
.group("/api", (app) => .group("/api", (app) =>
app.use(authRoute).use(categoriesRoute).use(productsRoute).use(uploadRoute).use(heroRoute).use(ordersRoute).use(settingsRoute).use(articlesRoute).use(tiktokRoute).use(rawMaterialsRoute).use(shopeeRoute).use(tiktokOrderRoute).use(lazadaOrderRoute).use(customersRoute).use(couponsRoute).use(reviewsRoute).use(promotionsRoute).use(pagesRoute).use(paymentsRoute).use(profileRoute) app.use(authRoute).use(categoriesRoute).use(productsRoute).use(uploadRoute).use(heroRoute).use(ordersRoute).use(settingsRoute).use(articlesRoute).use(tiktokRoute).use(rawMaterialsRoute).use(shopeeRoute).use(tiktokOrderRoute).use(lazadaOrderRoute).use(customersRoute).use(couponsRoute).use(reviewsRoute).use(promotionsRoute).use(pagesRoute).use(paymentsRoute).use(profileRoute).use(lineRoutes)
) )
.listen({ port: PORT, hostname: "0.0.0.0" }); .listen({ port: PORT, hostname: "0.0.0.0" });
+13
View File
@@ -0,0 +1,13 @@
import * as line from '@line/bot-sdk';
const channelAccessToken = process.env.LINE_CHANNEL_ACCESS_TOKEN || '';
const channelSecret = process.env.LINE_CHANNEL_SECRET || '';
export const lineConfig = {
channelAccessToken,
channelSecret,
};
export const lineClient = new line.messagingApi.MessagingApiClient({
channelAccessToken,
});
+49
View File
@@ -0,0 +1,49 @@
import { Elysia } from 'elysia';
import * as line from '@line/bot-sdk';
import { lineConfig, lineClient } from '../lib/lineClient';
import crypto from 'crypto';
export const lineRoutes = new Elysia({ prefix: '/line' })
.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' };
}
// get raw body string
const rawBody = await request.clone().text();
const hash = crypto
.createHmac('SHA256', lineConfig.channelSecret)
.update(rawBody)
.digest('base64');
if (hash !== signature) {
set.status = 401;
return { error: 'Invalid Signature' };
}
try {
const events: line.WebhookEvent[] = (body as any).events || [];
for (const event of events) {
if (event.type === 'message' && event.message.type === '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' };
}
});
+4 -1
View File
@@ -46,6 +46,7 @@
"@elysiajs/static": "^1.4.7", "@elysiajs/static": "^1.4.7",
"@elysiajs/swagger": "^1.2.1", "@elysiajs/swagger": "^1.2.1",
"@homaroma/types": "*", "@homaroma/types": "*",
"@line/bot-sdk": "^11.0.1",
"@prisma/adapter-pg": "^7.6.0", "@prisma/adapter-pg": "^7.6.0",
"@prisma/client": "^7.6.0", "@prisma/client": "^7.6.0",
"bcryptjs": "^3.0.3", "bcryptjs": "^3.0.3",
@@ -398,7 +399,7 @@
"@liff/window-postmessage": ["@liff/window-postmessage@2.29.0", "", { "dependencies": { "@liff/consts": "2.29.0", "@liff/logger": "2.29.0", "@liff/util": "2.29.0" }, "peerDependencies": { "tslib": "^2.3.0" } }, "sha512-rIsGf3xC/3JAb/1Nyoqrcss5UUUECz7v36MhruGeaVvTfzXqz1sviaUnU+rNHxij9CJEqMpA1C8+YDKenF6wMw=="], "@liff/window-postmessage": ["@liff/window-postmessage@2.29.0", "", { "dependencies": { "@liff/consts": "2.29.0", "@liff/logger": "2.29.0", "@liff/util": "2.29.0" }, "peerDependencies": { "tslib": "^2.3.0" } }, "sha512-rIsGf3xC/3JAb/1Nyoqrcss5UUUECz7v36MhruGeaVvTfzXqz1sviaUnU+rNHxij9CJEqMpA1C8+YDKenF6wMw=="],
"@line/bot-sdk": ["@line/bot-sdk@10.8.0", "", { "dependencies": { "@types/node": "^24.0.0" }, "optionalDependencies": { "axios": "^1.7.4" } }, "sha512-1Mdpw/WPS3QP6fGiwPRTL27k8MgmMe7rVKCKsItP/kPzogX7a/X1ipQqk/F6RkorP9DNyJIx590hnVUkfBQf3w=="], "@line/bot-sdk": ["@line/bot-sdk@11.0.1", "", { "dependencies": { "@types/node": "^24.0.0" } }, "sha512-De9gBX2JfZs78nDSyzfetHJw0R6hncPVVy3tzPMwYZzgwK06C/tcijGy7Vq28K8uYKGVTSthbtMIoYKNhXISzw=="],
"@line/liff": ["@line/liff@2.29.0", "", { "dependencies": { "@liff/activity": "2.29.0", "@liff/analytics": "2.29.0", "@liff/close-window": "2.29.0", "@liff/consts": "2.29.0", "@liff/core": "2.29.0", "@liff/create-shortcut-on-home-screen": "2.29.0", "@liff/extensions": "2.29.0", "@liff/get-app-language": "2.29.0", "@liff/get-friendship": "2.29.0", "@liff/get-language": "2.29.0", "@liff/get-line-version": "2.29.0", "@liff/get-origins": "2.29.0", "@liff/get-os": "2.29.0", "@liff/get-profile": "2.29.0", "@liff/get-version": "2.29.0", "@liff/hooks": "2.29.0", "@liff/i18n": "2.29.0", "@liff/iap": "2.29.0", "@liff/init": "2.29.0", "@liff/is-api-available": "2.29.0", "@liff/is-in-client": "2.29.0", "@liff/is-logged-in": "2.29.0", "@liff/is-sub-window": "2.29.0", "@liff/liff-types": "2.29.0", "@liff/login": "2.29.0", "@liff/logout": "2.29.0", "@liff/native-bridge": "2.29.0", "@liff/open-window": "2.29.0", "@liff/permanent-link": "2.29.0", "@liff/permission": "2.29.0", "@liff/ready": "2.29.0", "@liff/request-friendship": "2.29.0", "@liff/scan-code-v2": "2.29.0", "@liff/send-messages": "2.29.0", "@liff/server-api": "2.29.0", "@liff/share-target-picker": "2.29.0", "@liff/store": "2.29.0", "@liff/sub-window": "2.29.0", "@liff/use": "2.29.0", "@liff/util": "2.29.0", "tslib": "^2.3.0", "whatwg-fetch": "^3.0.0" } }, "sha512-50xZ7D5FiOM/42d00GEZh2TJ9bHDkbml+5yjUceC5xF19jYQVMrGn70Qbc0GYkVmnZtKp2q9hXQEzk25/arGrA=="], "@line/liff": ["@line/liff@2.29.0", "", { "dependencies": { "@liff/activity": "2.29.0", "@liff/analytics": "2.29.0", "@liff/close-window": "2.29.0", "@liff/consts": "2.29.0", "@liff/core": "2.29.0", "@liff/create-shortcut-on-home-screen": "2.29.0", "@liff/extensions": "2.29.0", "@liff/get-app-language": "2.29.0", "@liff/get-friendship": "2.29.0", "@liff/get-language": "2.29.0", "@liff/get-line-version": "2.29.0", "@liff/get-origins": "2.29.0", "@liff/get-os": "2.29.0", "@liff/get-profile": "2.29.0", "@liff/get-version": "2.29.0", "@liff/hooks": "2.29.0", "@liff/i18n": "2.29.0", "@liff/iap": "2.29.0", "@liff/init": "2.29.0", "@liff/is-api-available": "2.29.0", "@liff/is-in-client": "2.29.0", "@liff/is-logged-in": "2.29.0", "@liff/is-sub-window": "2.29.0", "@liff/liff-types": "2.29.0", "@liff/login": "2.29.0", "@liff/logout": "2.29.0", "@liff/native-bridge": "2.29.0", "@liff/open-window": "2.29.0", "@liff/permanent-link": "2.29.0", "@liff/permission": "2.29.0", "@liff/ready": "2.29.0", "@liff/request-friendship": "2.29.0", "@liff/scan-code-v2": "2.29.0", "@liff/send-messages": "2.29.0", "@liff/server-api": "2.29.0", "@liff/share-target-picker": "2.29.0", "@liff/store": "2.29.0", "@liff/sub-window": "2.29.0", "@liff/use": "2.29.0", "@liff/util": "2.29.0", "tslib": "^2.3.0", "whatwg-fetch": "^3.0.0" } }, "sha512-50xZ7D5FiOM/42d00GEZh2TJ9bHDkbml+5yjUceC5xF19jYQVMrGn70Qbc0GYkVmnZtKp2q9hXQEzk25/arGrA=="],
@@ -1656,6 +1657,8 @@
"@eslint/eslintrc/globals": ["globals@14.0.0", "", {}, "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ=="], "@eslint/eslintrc/globals": ["globals@14.0.0", "", {}, "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ=="],
"@liff/send-messages/@line/bot-sdk": ["@line/bot-sdk@10.8.0", "", { "dependencies": { "@types/node": "^24.0.0" }, "optionalDependencies": { "axios": "^1.7.4" } }, "sha512-1Mdpw/WPS3QP6fGiwPRTL27k8MgmMe7rVKCKsItP/kPzogX7a/X1ipQqk/F6RkorP9DNyJIx590hnVUkfBQf3w=="],
"@modelcontextprotocol/sdk/ajv": ["ajv@8.20.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA=="], "@modelcontextprotocol/sdk/ajv": ["ajv@8.20.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA=="],
"@modelcontextprotocol/sdk/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], "@modelcontextprotocol/sdk/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],