diff --git a/README.md b/README.md index 2f7f5136..ced2f53f 100644 --- a/README.md +++ b/README.md @@ -76,3 +76,5 @@ VITE_CLIENT_WEB_URL=http://localhost:5174 登录表单在开发和生产环境均默认留空,不包含联调账号、密码或演示提示。菜单、路由和操作权限只使用真实登录会话返回的服务端权限快照;前端控制仅改善体验,服务端仍须独立校验身份、角色权限和数据范围。接口失败时页面展示真实错误及追踪标识,不回退到假数据或模拟成功。 同机目标默认值:`API_UPSTREAM=http://gateway:9000`、`TEST_NETWORK=firstleg-test-network`;必须与后端同批 test Compose 配置一致。 + +TEST 入口同时转发两个独立 PDA 静态应用:`/pda-domestic/` 到共享网络中的 `pda-domestic-h5-test:80`,`/pda-overseas/` 到 `pda-overseas-h5-test:80`。各自构建、镜像和静态文件由对应 PDA 项目维护,Admin 不打包 PDA 源码。请求时解析 Docker DNS,PDA 未启动不会阻止 Admin 启动。国内 H5 的 `/gateway-api/` 在此转换为同一 `API_UPSTREAM` 的 `/api/`,海外 H5 直接使用既有 `/api/`;所有业务认证与仓绑定仍由统一 Gateway 和服务端校验。发布时先启动 PDA 容器,再验收两个路径的 HTML、资产、登录、权限与缺失资产 404。H5 通过不能替代 Android 真机扫码验收。 diff --git a/nginx/default.conf.template b/nginx/default.conf.template index 1ab97e09..c2891693 100644 --- a/nginx/default.conf.template +++ b/nginx/default.conf.template @@ -6,6 +6,21 @@ server { root /usr/share/nginx/html; index index.html; + # Resolve independent PDA containers at request time; their restart must not block Admin startup. + resolver 127.0.0.11 valid=10s ipv6=off; + location = /pda-domestic { return 308 /pda-domestic/; } + location = /pda-overseas { return 308 /pda-overseas/; } + location ^~ /pda-domestic/ { + set $pda_domestic_origin http://pda-domestic-h5-test:80; + proxy_pass $pda_domestic_origin$request_uri; + proxy_set_header Host $proxy_host; + } + location ^~ /pda-overseas/ { + set $pda_overseas_origin http://pda-overseas-h5-test:80; + proxy_pass $pda_overseas_origin$request_uri; + proxy_set_header Host $proxy_host; + } + # Docker 容器运行正常的检查地址,供 Jenkins 使用。 location = /healthz { default_type text/plain; @@ -42,6 +57,17 @@ server { proxy_set_header X-Forwarded-Proto $scheme; } + # Domestic PDA keeps its H5 module-safe prefix; authorization still belongs to the same Gateway. + location /gateway-api/ { + client_max_body_size 55m; + proxy_pass ${API_UPSTREAM}/api/; + proxy_http_version 1.1; + proxy_set_header Host $proxy_host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + # HTML must be revalidated on every visit so a new deployment cannot keep # referencing hashed chunks that were removed with the previous image. diff --git a/src/deployment/nginxConfig.test.ts b/src/deployment/nginxConfig.test.ts index de37fa7b..1b8bcffc 100644 --- a/src/deployment/nginxConfig.test.ts +++ b/src/deployment/nginxConfig.test.ts @@ -44,6 +44,18 @@ test('application shell is revalidated and missing chunks remain 404', () => { assert.match(config, /try_files \$uri \$uri\/ \/index\.html;/) }) +test('PDA static apps keep independent paths and share the authenticated Gateway', () => { + assert.match(config, /resolver 127\.0\.0\.11 valid=10s ipv6=off;/) + for (const app of ['domestic', 'overseas']) { + assert.ok(config.includes(`location = /pda-${app} { return 308 /pda-${app}/; }`)) + assert.ok(config.includes(`location ^~ /pda-${app}/ {`)) + assert.ok(config.includes(`set $pda_${app}_origin http://pda-${app}-h5-test:80;`)) + assert.ok(config.includes(`proxy_pass $pda_${app}_origin$request_uri;`)) + } + assert.match(config, /location \/gateway-api\/\s*\{[^}]*client_max_body_size 55m;[^}]*proxy_pass \$\{API_UPSTREAM\}\/api\//) + assert.doesNotMatch(config, /proxy_pass.*(?:auth:|lastleg-service:|firstleg-service:)/) +}) + test('container build excludes local environment values and waits for Nginx health', () => { const ignore = readFileSync(resolve(root, '.dockerignore'), 'utf8')