diff --git a/backend/.env.example b/backend/.env.example index acb7711..bb95189 100644 --- a/backend/.env.example +++ b/backend/.env.example @@ -6,6 +6,6 @@ DB_PORT=5432 DB_NAME= PORT=5000 JWT_SECRET=change-me -ALLOWED_ORIGINS=http://localhost:3000 +ALLOWED_ORIGINS=http://localhost:3010,http://127.0.0.1:3010,http://192.168.7.45:3010 SESSION_COOKIE_NAME=sid SESSION_TTL_DAYS=30 diff --git a/frontend/.env.example b/frontend/.env.example index 610beb1..3545d04 100644 --- a/frontend/.env.example +++ b/frontend/.env.example @@ -1,2 +1,3 @@ -VITE_API_URL=http://localhost:5000 -VITE_ALLOWED_HOSTS=localhost,127.0.0.1 +# Leave blank to infer the API host from the browser URL, e.g. http://192.168.7.45:5000. +VITE_API_URL= +VITE_ALLOWED_HOSTS=localhost,127.0.0.1,192.168.7.45 diff --git a/frontend/src/config.ts b/frontend/src/config.ts index 0aa5d12..c1d58bd 100644 --- a/frontend/src/config.ts +++ b/frontend/src/config.ts @@ -1 +1,41 @@ -export const API_BASE_URL = import.meta.env.VITE_API_URL || "http://localhost:5000"; \ No newline at end of file +const LOOPBACK_HOSTS = new Set(["localhost", "127.0.0.1", "::1"]); + +function trimTrailingSlash(value: string) { + return value.replace(/\/+$/, ""); +} + +function getDefaultApiBaseUrl() { + if (typeof window === "undefined") { + return "http://localhost:5000"; + } + + const protocol = window.location.protocol === "https:" ? "https:" : "http:"; + return `${protocol}//${window.location.hostname}:5000`; +} + +function shouldUseBrowserHostForApi(url: URL) { + if (typeof window === "undefined") { + return false; + } + + return LOOPBACK_HOSTS.has(url.hostname) && !LOOPBACK_HOSTS.has(window.location.hostname); +} + +function resolveApiBaseUrl() { + const configuredApiUrl = import.meta.env.VITE_API_URL?.trim(); + if (!configuredApiUrl) { + return getDefaultApiBaseUrl(); + } + + try { + const apiUrl = new URL(configuredApiUrl); + if (shouldUseBrowserHostForApi(apiUrl)) { + apiUrl.hostname = window.location.hostname; + } + return trimTrailingSlash(apiUrl.toString()); + } catch { + return trimTrailingSlash(configuredApiUrl); + } +} + +export const API_BASE_URL = resolveApiBaseUrl(); diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts index 8f1ab7f..69f425b 100644 --- a/frontend/vite.config.ts +++ b/frontend/vite.config.ts @@ -2,9 +2,12 @@ import react from '@vitejs/plugin-react'; import { defineConfig, loadEnv } from 'vite'; const env = loadEnv('', process.cwd()); -const allowedHosts = env.VITE_ALLOWED_HOSTS +const configuredAllowedHosts = env.VITE_ALLOWED_HOSTS ? env.VITE_ALLOWED_HOSTS.split(',').map((host) => host.trim()) : []; +const allowedHosts = Array.from( + new Set(['localhost', '127.0.0.1', '192.168.7.45', ...configuredAllowedHosts].filter(Boolean)) +); export default defineConfig({ plugins: [react()],