20261008-ide1.0发布 #1

Merged
zzh merged 24 commits from test into master 2026-10-08 09:19:21 +08:00
10 changed files with 107 additions and 47 deletions
Showing only changes of commit 7c29005cb6 - Show all commits

View File

@ -1,8 +1,9 @@
# VITE_API_BASE_URL='http://localhost:5173/'
# 本地开发统一走线上 Admin API(与 /rbac、/order 等 Vite 代理一致)
VITE_OMS_ADMIN_URL=http://api.nanafa.com/admin
VITE_API_ADMIN_URL=http://api.nanafa.com/admin
# 本地开发统一走测试环境 API(与 /rbac、/order、/api 等 Vite 代理一致)
VITE_API_ORIGIN=https://api.test.ideship.com
VITE_OMS_ADMIN_URL=https://api.test.ideship.com/admin
VITE_API_ADMIN_URL=https://api.test.ideship.com/admin
# 智谱 AI(接口映射脚本生成)
# 在 https://open.bigmodel.cn 申请 API Key

View File

@ -1,12 +1,11 @@
# 复制到 itfx-front 仓库 .gitea/workflows/deploy.yml
# 前置: kaniko-git + kaniko-docker-config;act_runner(k3s-host) 只负责 kubectl
# 构建在集群 Kaniko Job(role=build),不跑宿主机 Docker
# push master → prod;push test → test
# 构建始终用 prod kubeconfig(Kaniko);仅 deploy 切集群
name: Build and Deploy Front
on:
push:
branches: [master, main]
workflow_dispatch:
branches: [master, test]
env:
IMAGE_NAME: itfx-front
@ -20,13 +19,45 @@ jobs:
- name: Build and deploy
run: |
set -euxo pipefail
export KUBECONFIG=/etc/rancher/k3s/k3s.yaml
TAG="${{ gitea.sha }}"
IMG="${{ env.TCR_REGISTRY }}/${{ env.TCR_NAMESPACE }}/${{ env.IMAGE_NAME }}:${TAG}"
REF="${{ gitea.ref_name }}"
case "$REF" in
test) TARGET=test ;;
master) TARGET=prod ;;
*) echo "unsupported branch: $REF"; exit 1 ;;
esac
SHA="${{ gitea.sha }}"
REPO="${{ gitea.repository }}"
REPO="${REPO:-${{ github.repository }}}"
test -n "$REPO" && [[ "$REPO" == */* ]]
echo "REPO=$REPO TAG=$TAG host=$(hostname)"
case "$TARGET" in
test) TAG="test-${SHA}" ;;
*) TAG="${SHA}" ;;
esac
IMG="${{ env.TCR_REGISTRY }}/${{ env.TCR_NAMESPACE }}/${{ env.IMAGE_NAME }}:${TAG}"
echo "TARGET=$TARGET REF=$REF REPO=$REPO TAG=$TAG host=$(hostname)"
export KUBECONFIG=/etc/rancher/k3s/k3s.yaml
unset NAMESPACE
cd /data/k3s
./scripts/ci-publish-front.sh "$REPO" "${{ env.IMAGE_NAME }}" "$TAG" "$TAG"
./scripts/ci-publish-front.sh "$REPO" "${{ env.IMAGE_NAME }}" "$TAG" "$SHA"
case "$TARGET" in
test)
export KUBECONFIG=/etc/rancher/k3s/k3s-test.yaml
export NAMESPACE=test
export MANIFEST_ROOT=/data/k3s-test
;;
*)
export KUBECONFIG=/etc/rancher/k3s/k3s.yaml
export NAMESPACE=prod
export MANIFEST_ROOT=/data/k3s
;;
esac
if [[ ! -f "$KUBECONFIG" ]]; then
echo "FAIL: missing KUBECONFIG=$KUBECONFIG host=$(hostname) uid=$(id -u)"
ls -la /etc/rancher/k3s/ /root/.kube/ 2>/dev/null || true
exit 1
fi
./scripts/ci-deploy-front.sh "$IMG"

View File

@ -1,5 +1,6 @@
# k3s / Kaniko:静态 SPA(方案 A)。API 直连 api.ideship.com,镜像内 nginx 不做反代
# 基础镜像走 DaoCloud,避免 Kaniko 直连 docker.io 超时
# k3s / Kaniko:静态 SPA(方案 A)。API 由浏览器按站点域名直连
# app.test.ideship.com → api.test.ideship.com;其余 → api.ideship.com
# 镜像内 nginx 不做反代;也可用构建期 VITE_API_ORIGIN / VITE_API_ADMIN_URL 覆盖
FROM docker.m.daocloud.io/library/node:20-alpine AS build
WORKDIR /app
RUN npm config set registry https://registry.npmmirror.com \

View File

@ -1,4 +1,4 @@
# k3s 方案 A:仅静态 SPA。API 由浏览器直连 api.ideship.com(本文件不做反代)。
# k3s 方案 A:仅静态 SPA。API 由浏览器按域名直连(prod: api.ideship.com / test: api.test.ideship.com)。
server {
listen 80;
server_name _;

View File

@ -15,6 +15,7 @@
import { getMethodText } from '@/views/api/upstreamInterface/utils';
import { useTabRowCounts } from '@/utils/useTabRowCounts';
import RequestLog from '@/views/api/requestLog/index.vue';
import { prodApiOrigin } from '@/utils/rewriteBrowserApiPath';
const { t } = useI18n();
@ -1336,21 +1337,29 @@
/**
* 开发态:把已知外域 URL 改写到 Vite 代理,避免浏览器 CORS。
* - api.nanafa.com → /nanafa/...
* - api.nanafa.com / api(.test).ideship.com → /nanafa/...
* - 旧 itfx IP → /api/...
* - lsp.globex.cn → /external/...
* 生产:nanafa 旧址改写到 api.ideship.com(方案 A)
* 生产/测试构建:nanafa 旧址改写到当前环境 API 根(方案 A)
*/
function rewriteDebuggerUrlForProxy(rawUrl: string): string {
const url = String(rawUrl || '').trim();
if (!url) return url;
const apiOrigin = prodApiOrigin();
// http(s)://api.nanafa.com/...
if (/^https?:\/\/api\.nanafa\.com(?::\d+)?(\/|$)/i.test(url)) {
// http(s)://api.nanafa.com/... 或 api(.test).ideship.com/...
if (/^https?:\/\/api\.(?:nanafa|ideship)\.com(?::\d+)?(\/|$)/i.test(url)
|| /^https?:\/\/api\.test\.ideship\.com(?::\d+)?(\/|$)/i.test(url)) {
if (import.meta.env.DEV) {
return url.replace(/^https?:\/\/api\.nanafa\.com(?::\d+)?/i, '/nanafa');
return url
.replace(/^https?:\/\/api\.nanafa\.com(?::\d+)?/i, '/nanafa')
.replace(/^https?:\/\/api\.test\.ideship\.com(?::\d+)?/i, '/nanafa')
.replace(/^https?:\/\/api\.ideship\.com(?::\d+)?/i, '/nanafa');
}
return url.replace(/^https?:\/\/api\.nanafa\.com(?::\d+)?/i, 'https://api.ideship.com');
return url
.replace(/^https?:\/\/api\.nanafa\.com(?::\d+)?/i, apiOrigin)
.replace(/^https?:\/\/api\.test\.ideship\.com(?::\d+)?/i, apiOrigin)
.replace(/^https?:\/\/api\.ideship\.com(?::\d+)?/i, apiOrigin);
}
// 旧 itfx 直连 IP
@ -1358,7 +1367,7 @@
if (import.meta.env.DEV) {
return url.replace('http://43.139.210.35:8888', '/api');
}
return url.replace('http://43.139.210.35:8888', 'https://api.ideship.com/itfx');
return url.replace('http://43.139.210.35:8888', `${apiOrigin}/itfx`);
}
// 示例外部服务商

View File

@ -1,9 +1,11 @@
/**
* Admin API 地址(方案 A)
* - 开发:相对路径,Vite 代理到 Admin
* - 生产:直连 https://api.ideship.com/admin(可用 VITE_API_ADMIN_URL 覆盖)
* - 生产/测试:{apiOrigin}/admin(可用 VITE_API_ADMIN_URL 覆盖)
*/
import { prodApiOrigin } from './rewriteBrowserApiPath'
function normalizeBase(base: string): string {
return base.trim().replace(/\/$/, '')
}
@ -12,12 +14,10 @@ function ensureLeadingSlash(path: string): string {
return path.startsWith('/') ? path : `/${path}`
}
const DEFAULT_PROD_ADMIN = 'https://api.ideship.com/admin'
function prodAdminBase(): string {
const override = import.meta.env.VITE_API_ADMIN_URL as string | undefined
if (override?.trim()) return normalizeBase(override)
return DEFAULT_PROD_ADMIN
return `${prodApiOrigin()}/admin`
}
/** 浏览器请求 Admin API(OpenAPI.BASE 或 omsUrl/orderUrl 等) */
@ -43,13 +43,13 @@ export function resolveDirectAdminApiUrl(): string {
return normalizeBase(override)
}
if (import.meta.env.DEV) {
return 'http://api.nanafa.com/admin'
return 'https://api.test.ideship.com/admin'
}
return DEFAULT_PROD_ADMIN
return prodAdminBase()
}
/** 静态资源(uploads)在 Admin wwwroot,须带 `/admin`。
* 生产默认 api 域名;开发空串走 Vite `/uploads` 代理。
* 生产/测试默认 api 域名;开发空串走 Vite `/uploads` 代理。
*/
export function resolveAdminAssetOrigin(): string {
const override = import.meta.env.VITE_API_ASSET_ORIGIN as string | undefined
@ -57,7 +57,7 @@ export function resolveAdminAssetOrigin(): string {
return normalizeBase(override)
}
if (!import.meta.env.DEV) {
return DEFAULT_PROD_ADMIN
return prodAdminBase()
}
return ''
}

View File

@ -1,5 +1,8 @@
/**
* 方案 A:生产直连 api.ideship.com PATHBASE;开发仍走 Vite 代理旧路径。
* 方案 A:浏览器直连 API 网关 PATHBASE;开发仍走 Vite 代理旧路径。
* - prod:https://api.ideship.com
* - test(app.test.ideship.com):https://api.test.ideship.com
* - 可用 VITE_API_ORIGIN 覆盖
*/
const SPA_API_PREFIXES = [
@ -16,10 +19,19 @@ const SPA_API_PREFIXES = [
] as const
const DEFAULT_PROD_API = 'https://api.ideship.com'
const DEFAULT_TEST_API = 'https://api.test.ideship.com'
function prodApiOrigin(): string {
/** 生产/测试 API 根(无尾斜杠) */
export function prodApiOrigin(): string {
const override = (import.meta.env.VITE_API_ORIGIN as string | undefined)?.trim()
return (override || DEFAULT_PROD_API).replace(/\/$/, '')
if (override) return override.replace(/\/$/, '')
if (typeof window !== 'undefined') {
const h = window.location.hostname
if (h === 'app.test.ideship.com' || h.endsWith('.test.ideship.com')) {
return DEFAULT_TEST_API
}
}
return DEFAULT_PROD_API
}
function isItfxSpaPath(afterApi: string): boolean {

View File

@ -14,9 +14,12 @@ import configCompressPlugin from './config/plugin/compress';
export default defineConfig(({ mode }) => {
const env = loadEnv(mode, process.cwd(), '');
// Admin API 目标:由 VITE_OMS_ADMIN_URL 决定(开发默认线上)
// 开发默认打测试环境;可用 VITE_API_ORIGIN / VITE_OMS_ADMIN_URL 覆盖
const apiOrigin = (
env.VITE_API_ORIGIN || 'https://api.test.ideship.com'
).replace(/\/$/, '');
const omsAdminTarget = (
env.VITE_OMS_ADMIN_URL || 'http://api.nanafa.com/admin'
env.VITE_OMS_ADMIN_URL || `${apiOrigin}/admin`
).replace(/\/$/, '');
return {
@ -34,16 +37,17 @@ export default defineConfig(({ mode }) => {
changeOrigin: true,
secure: false,
},
// 上传静态资源:/api/upload/xxx -> http://api.nanafa.com/upload/xxx
// 上传静态资源:/api/upload/xxx -> {api}/upload/xxx
// 必须放在通用 /api 规则前,避免被转发到 /itfx/upload 导致 404
'/api/upload': {
target: 'http://api.nanafa.com',
target: apiOrigin,
changeOrigin: true,
secure: false,
rewrite: (path) => path.replace(/^\/api/, ''),
timeout: 30000,
proxyTimeout: 30000,
},
// Admin API:目标由 VITE_OMS_ADMIN_URL 决定(默认 http://api.nanafa.com/admin)
// Admin API:目标由 VITE_OMS_ADMIN_URL 决定(默认 https://api.test.ideship.com/admin)
'/rbac': {
target: omsAdminTarget,
changeOrigin: true,
@ -281,10 +285,11 @@ export default defineConfig(({ mode }) => {
secure: false,
rewrite: (path) => path.replace(/^\/api/, ''),
},
// 轨迹(track)43.139.210.35:8889 -> http://api.nanafa.com/track,路径与 rbac、api 一致:/Track/xxx
// 轨迹(track):/Track/xxx → {api}/track/Track/xxx
'/Track': {
target: 'http://api.nanafa.com',
target: apiOrigin,
changeOrigin: true,
secure: false,
rewrite: (path) => '/track' + path,
configure: (proxy) => {
proxy.on('error', (err, req, res) => {
@ -292,11 +297,12 @@ export default defineConfig(({ mode }) => {
});
},
},
// API(itfx)43.139.210.35:8888 -> http://api.nanafa.com/itfx
// API(itfx):/api/... → {api}/itfx/...
// 使用正则表达式匹配,排除前端路由路径
'^/api/(?!upstreamInterface|upstreamInterfaceConfig|upstreamInterfaceConfigEdit|downstreamInterface|downstreamInterface/newPort|appList|appList/createApplication|requestLog|callStatistics|apiDebugger|trackApi)': {
target: 'http://api.nanafa.com/itfx',
target: `${apiOrigin}/itfx`,
changeOrigin: true,
secure: false,
rewrite: (path) => path.replace(/^\/api/, ''),
timeout: 30000,
proxyTimeout: 30000,
@ -321,9 +327,9 @@ export default defineConfig(({ mode }) => {
timeout: 30000,
proxyTimeout: 30000,
},
// api.nanafa.com 整站代理(ApiTest 调试):/nanafa/itfx/xxx → http://api.nanafa.com/itfx/xxx
// API 网关整站代理(ApiTest 调试,路径名历史遗留):/nanafa/itfx/xxx → {api}/itfx/xxx
'/nanafa': {
target: 'http://api.nanafa.com',
target: apiOrigin,
changeOrigin: true,
secure: false,
rewrite: (path) => path.replace(/^\/nanafa/, ''),

View File

@ -9,7 +9,7 @@ from pathlib import Path
@dataclass
class AgentSettings:
oms_url: str = "http://api.nanafa.com/admin"
oms_url: str = "https://api.test.ideship.com/admin"
token: str = ""
device_name: str = ""
bridge_port: int = 9333

View File

@ -110,7 +110,7 @@ class LicenseConfig:
class OmsConfig:
"""OMS 物流查价 API(POST /order/price-check)。"""
api_base_url: str = "http://api.nanafa.com/admin"
api_base_url: str = "https://api.test.ideship.com/admin"
timeout_sec: int = 30
auth_token: str = ""
contact_name: str = ""