From 4c38f2a596ae6c1fe45276a05e2035f768deb451 Mon Sep 17 00:00:00 2001 From: Your Name Date: Thu, 18 Jun 2026 22:38:38 -0400 Subject: [PATCH] x --- apps/admin/src/pages/PageBuilderPage.tsx | 6 ++++- apps/admin/vite.config.ts | 4 ++++ apps/backend/src/index.js | 26 ++++++++++++++++++++-- apps/backend/src/index.ts | 25 +++++++++++++++++++++ packages/types/index.ts | 28 +++++++++++++++++++++--- 5 files changed, 83 insertions(+), 6 deletions(-) diff --git a/apps/admin/src/pages/PageBuilderPage.tsx b/apps/admin/src/pages/PageBuilderPage.tsx index 0895649..38f589a 100644 --- a/apps/admin/src/pages/PageBuilderPage.tsx +++ b/apps/admin/src/pages/PageBuilderPage.tsx @@ -43,7 +43,11 @@ interface Scent { } export default function PageBuilderPage() { - const frontendBase = (import.meta.env.VITE_FRONTEND_URL || window.location.origin).replace(/\/$/, ''); + const frontendBase = ( + import.meta.env.DEV + ? window.location.origin + : (import.meta.env.VITE_FRONTEND_URL || window.location.origin) + ).replace(/\/$/, ''); const authHeaders = () => ({ 'Content-Type': 'application/json', Authorization: `Bearer ${localStorage.getItem('homaroma_admin_token')}`, diff --git a/apps/admin/vite.config.ts b/apps/admin/vite.config.ts index c909975..3537aba 100644 --- a/apps/admin/vite.config.ts +++ b/apps/admin/vite.config.ts @@ -5,4 +5,8 @@ import tailwindcss from '@tailwindcss/vite' // https://vitejs.dev/config/ export default defineConfig({ plugins: [react(), tailwindcss()], + server: { + host: true, + allowedHosts: true, + }, }) diff --git a/apps/backend/src/index.js b/apps/backend/src/index.js index 1ede7e1..d44becd 100644 --- a/apps/backend/src/index.js +++ b/apps/backend/src/index.js @@ -30,6 +30,27 @@ import { mediaRoute } from "./routes/media"; 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_scent"; +const isDevelopment = process.env.NODE_ENV !== "production"; +const isPrivateLanHostname = (hostname) => { + 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) => { + const origin = request.headers.get("origin"); + if (!origin) + return false; + try { + return isPrivateLanHostname(new URL(origin).hostname); + } + catch { + return false; + } +}; const app = new Elysia() .onParse(async ({ request }) => { console.log('[onParse] Incoming request URL:', request.url); @@ -39,9 +60,10 @@ const app = new Elysia() return text; } }) - .use(cors({ +.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 @@ -96,4 +118,4 @@ const app = new Elysia() .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`); -//# sourceMappingURL=index.js.map \ No newline at end of file +//# sourceMappingURL=index.js.map diff --git a/apps/backend/src/index.ts b/apps/backend/src/index.ts index f73a699..5fb0220 100644 --- a/apps/backend/src/index.ts +++ b/apps/backend/src/index.ts @@ -30,6 +30,30 @@ import { mediaRoute } from "./routes/media"; 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_scent"; +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() .onParse(async ({ request }) => { @@ -44,6 +68,7 @@ const app = new Elysia() 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 diff --git a/packages/types/index.ts b/packages/types/index.ts index eed0305..f0744cf 100644 --- a/packages/types/index.ts +++ b/packages/types/index.ts @@ -59,9 +59,31 @@ export interface Product { images: ProductImage[] } -export const API_BASE = (typeof import.meta !== 'undefined' && (import.meta as any).env?.VITE_API_URL) - || ((globalThis as any).process?.env?.VITE_API_URL) - || 'http://localhost:3000/api' +const DEFAULT_DEV_API_PORT = 3051 + +function resolveApiBase() { + const viteEnv = (typeof import.meta !== 'undefined' ? (import.meta as any).env : undefined) + const envValue = viteEnv?.VITE_API_URL || (globalThis as any).process?.env?.VITE_API_URL + + if (envValue) { + const isDev = Boolean(viteEnv?.DEV) + const isLocalhostUrl = /^https?:\/\/(localhost|127\.0\.0\.1)(:\d+)?\/api\/?$/.test(envValue) + + if (isDev && typeof window !== 'undefined' && isLocalhostUrl) { + return `${window.location.protocol}//${window.location.hostname}:${DEFAULT_DEV_API_PORT}/api` + } + + return envValue + } + + if (typeof window !== 'undefined') { + return `${window.location.protocol}//${window.location.hostname}:${DEFAULT_DEV_API_PORT}/api` + } + + return 'http://localhost:3000/api' +} + +export const API_BASE = resolveApiBase() export const CATEGORY_IMAGES: Record = { 'Oil Ozone': '/images/oil_ozone.png',