diff --git a/docker-compose.yaml b/docker-compose.yaml index a941085..cbcf703 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -74,8 +74,6 @@ services: image: node:22-slim working_dir: /app command: sh -c "npm install && npm run dev" - environment: - NEXT_PUBLIC_API_BASE_URL: "http://localhost:8000" volumes: - ./frontend:/app - /app/node_modules diff --git a/frontend/src/lib/api.ts b/frontend/src/lib/api.ts index 0e03707..64f1cb7 100644 --- a/frontend/src/lib/api.ts +++ b/frontend/src/lib/api.ts @@ -1,5 +1,5 @@ /** - * 后端 API 客户端。基址走 NEXT_PUBLIC_API_BASE,缺省指向本地后端 :8000。 + * 后端 API 客户端。显式配置优先;本地开发缺省沿用页面主机名并连接 :8000。 * * JSON 契约与后端 schemas.py 对齐(camelCase),所以返回体可直接喂给页面 state。 * 注意:api_key 读取时后端永远打码,写回打码占位符表示"不改 key"(写时哨兵)。 @@ -7,8 +7,32 @@ import { loginPathWithReturnTo } from "@/lib/auth-redirect"; -export const API_BASE = - process.env.NEXT_PUBLIC_API_BASE_URL ?? "http://localhost:8000"; +const LOOPBACK_HOSTS = new Set(["localhost", "127.0.0.1", "[::1]"]); + +function resolveApiBase(): string { + const configured = process.env.NEXT_PUBLIC_API_BASE_URL?.replace(/\/$/, ""); + if (typeof window === "undefined") { + return configured || "http://localhost:8000"; + } + if (!configured) { + return `${window.location.protocol}//${window.location.hostname}:8000`; + } + + // localhost and 127.0.0.1 reach the same machine but are different cookie + // sites. Keep an explicitly configured scheme/port while aligning equivalent + // loopback hostnames with the address actually used in the browser. + const configuredUrl = new URL(configured); + if ( + LOOPBACK_HOSTS.has(configuredUrl.hostname) && + LOOPBACK_HOSTS.has(window.location.hostname) + ) { + configuredUrl.hostname = window.location.hostname; + return configuredUrl.toString().replace(/\/$/, ""); + } + return configured; +} + +export const API_BASE = resolveApiBase(); export type ModelType = "LLM" | "ASR" | "TTS" | "Realtime" | "Embedding" | "Agent";