From 7fa81481df2d30b4f38d3fe44bd5b26e42e6b15e Mon Sep 17 00:00:00 2001 From: flym Date: Sat, 12 Sep 2026 01:37:16 +0800 Subject: [PATCH] fix(cms): keep admin root route segments undefined to avoid 404 after login --- .../(payload)/admin/[[...segments]]/page.tsx | 8 +++++++- docs/production-deployment-2026-09-11.md | 19 +++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/apps/cms/src/app/(payload)/admin/[[...segments]]/page.tsx b/apps/cms/src/app/(payload)/admin/[[...segments]]/page.tsx index eea0650..270b8f0 100644 --- a/apps/cms/src/app/(payload)/admin/[[...segments]]/page.tsx +++ b/apps/cms/src/app/(payload)/admin/[[...segments]]/page.tsx @@ -8,5 +8,11 @@ export const generateMetadata = ({ params, searchParams }: { params: Promise; searchParams: Promise> }) { const resolvedParams = await params; - return RootPage({ config: configPromise, importMap, params: Promise.resolve({ segments: resolvedParams.segments ?? [] }), searchParams }); + // 注意:admin 根路由(/cms/admin)必须让 segments 保持 undefined,不能兜底成空数组。 + // RootPage 用 `Array.isArray(params.segments)` 判断是否为根路由:传 `[]` 时 + // formatAdminURL({ path: "/" }) 会算出 "/admin/"(带尾斜杠),与 adminRoute "/admin" + // 不相等,导致已登录用户在根路由(仪表盘)命中 notFound() 收到 404。 + // @payloadcms/next 的类型声明把 segments 写成必填 string[](与实现不符),故此处断言。 + const adminParams = Promise.resolve({ segments: resolvedParams.segments }) as Promise<{ segments: string[] }>; + return RootPage({ config: configPromise, importMap, params: adminParams, searchParams }); } diff --git a/docs/production-deployment-2026-09-11.md b/docs/production-deployment-2026-09-11.md index 56552e1..9282e49 100644 --- a/docs/production-deployment-2026-09-11.md +++ b/docs/production-deployment-2026-09-11.md @@ -34,3 +34,22 @@ - 生产 `api.env` 尚未注入 SMTP/阿里云短信凭据(新配置项默认空值=功能关闭),注册验证能力待凭据注入后启用,属待办而非本次部署内容。 - Caddy 未改动(CMS 根路径跳转由 CMS 自身 `/cms → /cms/admin` 处理)。 + +## 追加:CMS 管理端登录后 404 根因与修复(重要) + +**现象**:登录 CMS 后访问 `https://www.kaotings.com/cms/admin` 返回 404(Next 默认 "This page could not be found."),未登录时正常显示登录页;其它管理路径(`/account`、`/collections/*`、`/globals/*`)全部正常。与网络、DNS、浏览器、HTTP/3、CDN 无关(曾误判,已逐一排除:证书指纹一致、请求确实到达服务器并记录在 Caddy 日志中)。 + +**根因**(三处代码叠加): +1. `apps/cms/src/app/(payload)/admin/[[...segments]]/page.tsx` 把路由参数兜底成空数组:`segments: resolvedParams.segments ?? []`。 +2. `@payloadcms/next` 的 `RootPage` 用 `Array.isArray(params.segments) ? `/${params.segments.join('/')}` : null` 计算 `currentRoute`;传入 `[]` 时 `path` 变成 `"/"`。 +3. `payload/shared` 的 `formatAdminURL` 对 `{adminRoute:'/admin', path:'/'}` 返回 `"/admin/"`(带尾斜杠,且该分支不做去尾斜杠处理),于是 `currentRoute("/admin/") !== adminRoute("/admin")`。 + +而 `getRouteData` 中**只有根路由**的视图解析依赖 `currentRoute === adminRoute` 判断(`case 0`),条件不成立时 `DefaultView` 为空,`RootPage` 对**已登录用户**执行 `notFound()` → 404;未登录用户走另一分支,因此表现为"登录后才 404"。 + +**修复**:`page.tsx` 中不再把 `segments` 兜底为空数组,保持 `undefined`(`@payloadcms/next` 类型声明把 `segments` 写成必填数组,与实现不符,故加一处类型断言并注释说明)。修复后已登录访问根路由返回 200 并正常渲染 `Dashboard - Payload`。 + +**其它同批处理**: +- CMS 改为在服务器上构建(本地 Windows 构建产物中 `.next/node_modules/*` 符号链接会退化为空目录,不可直接部署;服务器构建会生成有效的 `-> ../../node_modules/` 符号链接)。 +- 新增 4G swap,避免服务器构建 OOM。 +- Caddy 增加了访问日志(`log { output stdout }`),便于后续排查。 +- 发布:`releases/cms-ad87a71-c`(含本次修复),回滚可指向 `releases/cms-ad87a71-b`(可运行,但根路由登录后 404)。