154 lines
5.8 KiB
TypeScript
154 lines
5.8 KiB
TypeScript
import { Elysia } from "elysia";
|
|
import { cors } from "@elysiajs/cors";
|
|
import { swagger } from "@elysiajs/swagger";
|
|
import { jwt } from "@elysiajs/jwt";
|
|
import { staticPlugin } from "@elysiajs/static";
|
|
import { categoriesRoute } from "./routes/categories";
|
|
import { productsRoute } from "./routes/products";
|
|
import { authRoute } from "./routes/auth";
|
|
import { uploadRoute } from "./routes/upload";
|
|
import { ordersRoute } from "./routes/orders";
|
|
import { settingsRoute } from "./routes/settings";
|
|
import { articlesRoute } from "./routes/articles";
|
|
import { tiktokRoute } from "./routes/tiktok";
|
|
import { rawMaterialsRoute } from "./routes/raw-materials";
|
|
import { shopeeRoute } from "./routes/shopee";
|
|
import { tiktokOrderRoute } from "./routes/tiktokOrder";
|
|
import { lazadaOrderRoute } from "./routes/lazadaOrder";
|
|
import { customersRoute } from "./routes/customers";
|
|
import { couponsRoute } from "./routes/coupons";
|
|
import { reviewsRoute } from "./routes/reviews";
|
|
import { promotionsRoute } from "./routes/promotions";
|
|
import { pagesRoute } from "./routes/pages";
|
|
import { paymentsRoute } from "./routes/payments";
|
|
import { profileRoute } from "./routes/profile";
|
|
import { lineRoutes } from "./routes/line";
|
|
import { menusRoute } from "./routes/menus";
|
|
import { newsletterRoute } from "./routes/newsletter";
|
|
import { mediaRoute } from "./routes/media";
|
|
import { contactsRoute } from "./routes/contacts";
|
|
import { productAttributesRoute } from "./routes/product-attributes";
|
|
import { backupRoute } from "./routes/backup";
|
|
import { businessAnalyticsRoute } from "./routes/businessAnalytics";
|
|
import { toolsRoute } from "./routes/tools";
|
|
import { crmRoute } from "./routes/crm";
|
|
import { brandKnowledgeRoute } from "./routes/brandKnowledge";
|
|
import { categorySectionsRoute } from "./routes/categorySections";
|
|
const PORT = process.env.PORT ? parseInt(process.env.PORT) : 3051;
|
|
const CORS_ORIGIN = process.env.CORS_ORIGIN || "http://localhost:5173";
|
|
const JWT_SECRET = process.env.JWT_SECRET || "homaroma_secret_2026_brand";
|
|
const isDevelopment = process.env.NODE_ENV !== "production";
|
|
|
|
const isPrivateLanHostname = (hostname: string) => {
|
|
return (
|
|
hostname === "localhost" ||
|
|
hostname === "127.0.0.1" ||
|
|
hostname === "0.0.0.0" ||
|
|
hostname === "::1" ||
|
|
/^10\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(hostname) ||
|
|
/^192\.168\.\d{1,3}\.\d{1,3}$/.test(hostname) ||
|
|
/^172\.(1[6-9]|2\d|3[0-1])\.\d{1,3}\.\d{1,3}$/.test(hostname)
|
|
);
|
|
};
|
|
|
|
const allowLanDevOrigin = (request: Request) => {
|
|
const origin = request.headers.get("origin");
|
|
if (!origin) return false;
|
|
|
|
try {
|
|
return isPrivateLanHostname(new URL(origin).hostname);
|
|
} catch {
|
|
return false;
|
|
}
|
|
};
|
|
|
|
const app = new Elysia()
|
|
.onRequest(({ request }) => {
|
|
console.log(`\n➡️ [${request.method}] ${request.url}`);
|
|
})
|
|
.onAfterHandle(({ request, set }) => {
|
|
console.log(`✅ [${request.method}] ${request.url} → ${set.status ?? 200}`);
|
|
})
|
|
.onError(({ request, error, set }) => {
|
|
console.error(`❌ [${request.method}] ${request.url} → ${set.status ?? 500}`, error?.message ?? error);
|
|
})
|
|
.onParse(async ({ request }) => {
|
|
console.log('[onParse] Incoming request URL:', request.url);
|
|
if (request.url.includes('/api/line/webhook')) {
|
|
const text = await request.text();
|
|
console.log('[onParse] Intercepted LINE webhook raw body length:', text.length);
|
|
return text;
|
|
}
|
|
})
|
|
.use(
|
|
cors({
|
|
origin: [
|
|
...CORS_ORIGIN.split(",").map((o) => o.trim()),
|
|
...(isDevelopment ? [allowLanDevOrigin] : []),
|
|
"https://admin.homaroma.shop",
|
|
"https://spa.homaroma.shop", // LIFF (SPA)
|
|
"https://homaroma.shop", // Main Website
|
|
"http://localhost:5174", // Admin
|
|
"http://localhost:4173",
|
|
"http://localhost:3001", // SSR Next.js
|
|
"https://react5173.kitizone.com",
|
|
"https://elysia3051.kitizone.com"
|
|
],
|
|
methods: ["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"],
|
|
credentials: true,
|
|
})
|
|
)
|
|
.use(
|
|
jwt({
|
|
name: "jwt",
|
|
secret: JWT_SECRET,
|
|
exp: "24h",
|
|
})
|
|
)
|
|
.use(
|
|
swagger({
|
|
documentation: {
|
|
info: {
|
|
title: "HomAroma API",
|
|
version: "1.0.0",
|
|
description: "API for HomAroma & Essential Oil Products",
|
|
},
|
|
tags: [
|
|
{ name: "categories", description: "Product categories" },
|
|
{ name: "products", description: "Products and variants" },
|
|
{ name: "auth", description: "Authentication for Admin" },
|
|
],
|
|
},
|
|
})
|
|
)
|
|
.use(staticPlugin({
|
|
assets: "public",
|
|
prefix: "", // use empty string for root
|
|
}))
|
|
.get("/", () => ({
|
|
name: "HomAroma API",
|
|
version: "1.0.0",
|
|
status: "running",
|
|
docs: `/swagger`,
|
|
}))
|
|
// Explicit route for uploads to guarantee it works even if staticPlugin fails
|
|
.get("/uploads/:filename", ({ params: { filename }, set }) => {
|
|
const file = Bun.file(`public/uploads/${filename}`);
|
|
if (file.size === 0) {
|
|
set.status = 404;
|
|
return "File not found";
|
|
}
|
|
return file;
|
|
})
|
|
.group("/api", (app) =>
|
|
app.use(authRoute).use(categoriesRoute).use(categorySectionsRoute).use(productsRoute).use(productAttributesRoute).use(uploadRoute).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).use(menusRoute).use(newsletterRoute).use(mediaRoute).use(contactsRoute).use(backupRoute).use(businessAnalyticsRoute).use(toolsRoute).use(crmRoute).use(brandKnowledgeRoute)
|
|
)
|
|
.listen({ port: PORT, hostname: "0.0.0.0" });
|
|
|
|
console.log(
|
|
`🌿 HomAroma API is running at http://${app.server?.hostname}:${app.server?.port}`
|
|
);
|
|
console.log(
|
|
`📖 Swagger docs: http://${app.server?.hostname}:${app.server?.port}/swagger`
|
|
);
|