ad0430fcd2
- 现状:只把 `/pay`、`/profile/payment` 加进 allowlist 只能让 check:nginx-spa-routes 变绿; payment.rs 生成的 checkoutUrl 是 `/pay/<checkoutToken>`,深链仍落默认 location 的 try_files → 404。同一批漂移里 check:pingora-route-parity 也是红的(Pingora MAIN_SPA_PATHS 缺 /pay、/profile/payment),只是被 lint 链里先失败的门禁掩盖,修一条要跑到链尾 - 真相源:src/routing/activeAppPageRoutes.ts 新增 APP_PREFIX_ROUTE_ENTRIES ('/pay' → payment-checkout),resolveSelectionStageFromPath 改用它 - 门禁:scripts/check-nginx-spa-routes.mjs 要求三份模板都有锚定前缀 location `location ~* "^/pay/[^/]+/?$"`(裸前缀仍由精确 location 负责;前缀 location 必须镜像精确 location 的维护闸与 try_files 回退);新增 scripts/check-nginx-spa-routes.test.mjs 正/反用例 (把前缀写成精确匹配或过宽裸前缀都会红),由 npm run check:nginx-spa-routes 一起执行; check-pingora-route-parity 新增 MAIN_SPA_PREFIX_PATHS 与前端前缀路由的逐条比对 - 模板:deploy/nginx/genarrative.conf、deploy/nginx/genarrative-dev-http.conf、 deploy/container/nginx.conf 各加一条锚定前缀 location - Pingora:MAIN_SPA_PATHS 补 /pay、/profile/payment;新增 MAIN_SPA_PREFIX_PATHS 与 is_main_spa_prefix_path(大小写不敏感,只认「前缀 + 恰好一段」),矩阵新增 pay_checkout_spa_fallback 用例,并给网关补一条前缀正/反单测 - 文档:Pingora 试点文档的路由表与门禁说明、deploy/nginx/README 与本地开发/生产运维文档 同步前缀路由口径与线上 curl 复验方式 - 本地实跑:node --test scripts/check-nginx-spa-routes.test.mjs(4 passed)、 node scripts/check-nginx-spa-routes.mjs(OK,14 SPA routes / 1 prefix routes / 3 templates)、 npm run check:pingora-route-parity(OK,25 routes)、 cargo test -p pingora-gateway -- pay_checkout_deep_link matches_nginx_route_parity_matrix(2 passed)
48 lines
1.8 KiB
JavaScript
48 lines
1.8 KiB
JavaScript
import assert from 'node:assert/strict';
|
|
import { test } from 'node:test';
|
|
|
|
import {
|
|
buildPrefixRouteNginxPattern,
|
|
collectExpectedPrefixRoutes,
|
|
collectPrefixRoutePatternFailures,
|
|
} from './check-nginx-spa-routes.mjs';
|
|
|
|
test('前缀路由按「前缀 + 恰好一个路径段 + 可省略尾部斜杠」锚定,裸前缀交给精确 location', () => {
|
|
const pattern = buildPrefixRouteNginxPattern('/pay');
|
|
assert.equal(pattern, '^/pay/[^/]+/?$');
|
|
assert.deepEqual(collectPrefixRoutePatternFailures(pattern, '/pay'), []);
|
|
|
|
const matcher = new RegExp(pattern, 'iu');
|
|
assert.ok(matcher.test('/pay/checkout-token'));
|
|
assert.ok(matcher.test('/PAY/CheckOutToken/'));
|
|
assert.ok(!matcher.test('/pay'));
|
|
assert.ok(!matcher.test('/pay/'));
|
|
assert.ok(!matcher.test('/pay/two/segments'));
|
|
assert.ok(!matcher.test('/paycheckout/token'));
|
|
assert.ok(!matcher.test('/payment/token'));
|
|
});
|
|
|
|
test('把前缀路由写回精确匹配(只放行裸前缀)会被门禁拒绝', () => {
|
|
const failures = collectPrefixRoutePatternFailures('^/pay/?$', '/pay');
|
|
assert.equal(failures.length, 1);
|
|
assert.match(failures[0], /必须锚定为 \^\/pay\/\[\^\/\]\+\/\?\$/u);
|
|
});
|
|
|
|
test('过宽的裸前缀形状(会吞掉同名邻居路径)也会被门禁拒绝', () => {
|
|
const failures = collectPrefixRoutePatternFailures('^/pay', '/pay');
|
|
assert.equal(failures.length, 1);
|
|
assert.match(failures[0], /必须锚定为/u);
|
|
});
|
|
|
|
test('前缀路由真相源来自前端路由表且当前包含 /pay', () => {
|
|
const prefixRoutes = collectExpectedPrefixRoutes();
|
|
assert.ok(
|
|
prefixRoutes.some((route) => route.path === '/pay'),
|
|
'APP_PREFIX_ROUTE_ENTRIES 必须声明 /pay',
|
|
);
|
|
for (const route of prefixRoutes) {
|
|
assert.match(route.path, /^\//u);
|
|
assert.notEqual(route.stage.trim(), '');
|
|
}
|
|
});
|