补上收银台深链 /pay/<checkoutToken> 的前缀路由(Nginx 三模板 + Pingora + 门禁)
- 现状:只把 `/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)
This commit is contained in:
@@ -18,6 +18,16 @@ const STAGE_ROUTE_ENTRIES = [
|
||||
['game-publish', '/games/publish'],
|
||||
] as const satisfies readonly (readonly [SelectionStage, string])[];
|
||||
|
||||
/**
|
||||
* 带动态段、需要整体回退 index.html 的对外路由前缀 → 归属 stage。
|
||||
* 例如收银台深链 `/pay/<checkoutToken>`(后端 `payment.rs` 用 `format!("/pay/{}", token)` 生成,
|
||||
* 前端从最后一个路径段读 token)。Nginx 必须按「前缀 + 恰好一个路径段」的锚定形状放行:
|
||||
* 只放行裸前缀会让真实收银台链接落到默认 location 变成 404,放行过宽又会把未知路径吞掉。
|
||||
*/
|
||||
export const APP_PREFIX_ROUTE_ENTRIES = [
|
||||
['/pay', 'payment-checkout'],
|
||||
] as const satisfies readonly (readonly [string, SelectionStage])[];
|
||||
|
||||
export const APP_STAGE_ROUTES: Record<SelectionStage, string> =
|
||||
Object.fromEntries(STAGE_ROUTE_ENTRIES) as Record<SelectionStage, string>;
|
||||
|
||||
@@ -41,10 +51,13 @@ export function normalizeAppPath(pathname: string) {
|
||||
export function resolveSelectionStageFromPath(
|
||||
pathname: string,
|
||||
): SelectionStage {
|
||||
if (normalizeAppPath(pathname).startsWith('/pay/')) {
|
||||
return 'payment-checkout';
|
||||
const normalizedPath = normalizeAppPath(pathname);
|
||||
for (const [prefix, stage] of APP_PREFIX_ROUTE_ENTRIES) {
|
||||
if (normalizedPath.startsWith(`${prefix}/`)) {
|
||||
return stage;
|
||||
}
|
||||
}
|
||||
return ROUTE_STAGE_BY_PATH.get(normalizeAppPath(pathname)) ?? 'platform';
|
||||
return ROUTE_STAGE_BY_PATH.get(normalizedPath) ?? 'platform';
|
||||
}
|
||||
|
||||
export function resolveInitialSelectionStageFromPath(
|
||||
|
||||
Reference in New Issue
Block a user