From 1bc2838def361346482b2ea8e178fc303c2fe1a5 Mon Sep 17 00:00:00 2001 From: zzh <1368735060@qq.com> Date: Thu, 24 Sep 2026 11:24:03 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20test=20=E7=AB=99=E7=82=B9=E7=99=BB?= =?UTF-8?q?=E5=BD=95=E5=BC=BA=E5=88=B6=E8=B5=B0=20api.test=EF=BC=8C?= =?UTF-8?q?=E9=81=BF=E5=85=8D=E8=AF=AF=E7=94=A8=E6=AD=A3=E5=BC=8F=20API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Dockerfile 误用 production 构建会把 VITE_API_ORIGIN 打成 api.ideship.com;运行时按 hostname 纠正,并支持 FRONT_BUILD_SCRIPT=build:test。 Co-authored-by: Cursor --- .gitea/workflows/deploy.yml | 7 +++++++ Dockerfile.k3s | 8 ++++++-- src/utils/adminApi.ts | 20 ++++++++++++++++++-- src/utils/rewriteBrowserApiPath.ts | 16 +++++++++++----- 4 files changed, 42 insertions(+), 9 deletions(-) diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml index da12e8b..a1cc650 100644 --- a/.gitea/workflows/deploy.yml +++ b/.gitea/workflows/deploy.yml @@ -1,6 +1,7 @@ # 复制到 itfx-front 仓库 .gitea/workflows/deploy.yml # push master → prod;push test → test # 构建始终用 prod kubeconfig(Kaniko);仅 deploy 切集群 +# 前端镜像:test 分支须传 FRONT_BUILD_SCRIPT=build:test,否则会打进正式 API 域名 name: Build and Deploy Front on: @@ -38,6 +39,12 @@ jobs: IMG="${{ env.TCR_REGISTRY }}/${{ env.TCR_NAMESPACE }}/${{ env.IMAGE_NAME }}:${TAG}" echo "TARGET=$TARGET REF=$REF REPO=$REPO TAG=$TAG host=$(hostname)" + case "$TARGET" in + test) export FRONT_BUILD_SCRIPT=build:test ;; + *) export FRONT_BUILD_SCRIPT=build ;; + esac + echo "FRONT_BUILD_SCRIPT=$FRONT_BUILD_SCRIPT" + export KUBECONFIG=/etc/rancher/k3s/k3s.yaml unset NAMESPACE cd /data/k3s diff --git a/Dockerfile.k3s b/Dockerfile.k3s index 6d3ac67..baaa055 100644 --- a/Dockerfile.k3s +++ b/Dockerfile.k3s @@ -1,8 +1,12 @@ # k3s / Kaniko:静态 SPA(方案 A)。API 由浏览器按站点域名直连 # test.ideship.com → api.test.ideship.com;其余 → api.ideship.com -# 镜像内 nginx 不做反代;也可用构建期 VITE_API_ORIGIN / VITE_API_ADMIN_URL 覆盖 +# FRONT_BUILD_SCRIPT: +# prod 默认 build(.env.production → api.ideship.com) +# test 传 build:test(.env.ideship-test → api.test.ideship.com) +# 即使误用 production 构建,运行时也会按 hostname 纠正到测试 API。 FROM docker.m.daocloud.io/library/node:20-alpine AS build WORKDIR /app +ARG FRONT_BUILD_SCRIPT=build RUN npm config set registry https://registry.npmmirror.com \ && npm install -g pnpm@9.15.9 COPY package.json pnpm-lock.yaml ./ @@ -10,7 +14,7 @@ COPY package.json pnpm-lock.yaml ./ RUN pnpm config set registry https://registry.npmmirror.com \ && pnpm install --frozen-lockfile --ignore-scripts COPY . . -RUN pnpm run build +RUN pnpm run ${FRONT_BUILD_SCRIPT} FROM docker.m.daocloud.io/library/nginx:1.27-alpine COPY --from=build /app/dist /usr/share/nginx/html/ diff --git a/src/utils/adminApi.ts b/src/utils/adminApi.ts index 93eb045..8b48e79 100644 --- a/src/utils/adminApi.ts +++ b/src/utils/adminApi.ts @@ -2,9 +2,12 @@ * Admin API 地址(方案 A) * - 开发:相对路径,Vite 代理到 Admin * - 生产/测试:{apiOrigin}/admin(可用 VITE_API_ADMIN_URL 覆盖) + * - 浏览器在 test.ideship.com 时强制 api.test.ideship.com/admin */ -import { prodApiOrigin } from './rewriteBrowserApiPath' +import { isTestFrontHost, prodApiOrigin } from './rewriteBrowserApiPath' + +const DEFAULT_TEST_ADMIN = 'https://api.test.ideship.com/admin' function normalizeBase(base: string): string { return base.trim().replace(/\/$/, '') @@ -14,7 +17,14 @@ function ensureLeadingSlash(path: string): string { return path.startsWith('/') ? path : `/${path}` } +function isBrowserOnTestFront(): boolean { + return typeof window !== 'undefined' && isTestFrontHost(window.location.hostname) +} + function prodAdminBase(): string { + if (isBrowserOnTestFront()) { + return DEFAULT_TEST_ADMIN + } const override = import.meta.env.VITE_API_ADMIN_URL as string | undefined if (override?.trim()) return normalizeBase(override) return `${prodApiOrigin()}/admin` @@ -38,12 +48,15 @@ export function resolveOpenApiBase(): string { /** 桌面 Agent 等直连 Admin 网关 */ export function resolveDirectAdminApiUrl(): string { + if (isBrowserOnTestFront()) { + return DEFAULT_TEST_ADMIN + } const override = import.meta.env.VITE_API_ADMIN_URL as string | undefined if (override?.trim()) { return normalizeBase(override) } if (import.meta.env.DEV) { - return 'https://api.test.ideship.com/admin' + return DEFAULT_TEST_ADMIN } return prodAdminBase() } @@ -52,6 +65,9 @@ export function resolveDirectAdminApiUrl(): string { * 生产/测试默认 api 域名;开发空串走 Vite `/uploads` 代理。 */ export function resolveAdminAssetOrigin(): string { + if (isBrowserOnTestFront()) { + return DEFAULT_TEST_ADMIN + } const override = import.meta.env.VITE_API_ASSET_ORIGIN as string | undefined if (override?.trim()) { return normalizeBase(override) diff --git a/src/utils/rewriteBrowserApiPath.ts b/src/utils/rewriteBrowserApiPath.ts index 8431aeb..b844f5f 100644 --- a/src/utils/rewriteBrowserApiPath.ts +++ b/src/utils/rewriteBrowserApiPath.ts @@ -2,7 +2,8 @@ * 方案 A:浏览器直连 API 网关 PATHBASE;开发仍走 Vite 代理旧路径。 * - prod:https://api.ideship.com * - test(test.ideship.com):https://api.test.ideship.com - * - 可用 VITE_API_ORIGIN 覆盖 + * - 浏览器在测试站点时优先按域名判定(避免 test 镜像误用 production 环境变量打成正式 API) + * - 非测试站点可用 VITE_API_ORIGIN 覆盖 */ const SPA_API_PREFIXES = [ @@ -21,7 +22,7 @@ const SPA_API_PREFIXES = [ const DEFAULT_PROD_API = 'https://api.ideship.com' const DEFAULT_TEST_API = 'https://api.test.ideship.com' -function isTestFrontHost(hostname: string): boolean { +export function isTestFrontHost(hostname: string): boolean { return ( hostname === 'test.ideship.com' || hostname === 'app.test.ideship.com' || // 旧域名兼容 @@ -29,13 +30,18 @@ function isTestFrontHost(hostname: string): boolean { ) } +function isBrowserOnTestFront(): boolean { + return typeof window !== 'undefined' && isTestFrontHost(window.location.hostname) +} + /** 生产/测试 API 根(无尾斜杠) */ export function prodApiOrigin(): string { - const override = (import.meta.env.VITE_API_ORIGIN as string | undefined)?.trim() - if (override) return override.replace(/\/$/, '') - if (typeof window !== 'undefined' && isTestFrontHost(window.location.hostname)) { + // 测试站点强制走测试 API,不被错误打进包的 VITE_API_ORIGIN 覆盖 + if (isBrowserOnTestFront()) { return DEFAULT_TEST_API } + const override = (import.meta.env.VITE_API_ORIGIN as string | undefined)?.trim() + if (override) return override.replace(/\/$/, '') return DEFAULT_PROD_API }