Skip to content

fix(seo): leetcode 中文 slug 旧 URL 301 到拼音新路径#318

Merged
longsizhuo merged 2 commits intomainfrom
fix/leetcode-chinese-slug-404
Apr 20, 2026
Merged

fix(seo): leetcode 中文 slug 旧 URL 301 到拼音新路径#318
longsizhuo merged 2 commits intomainfrom
fix/leetcode-chinese-slug-404

Conversation

@longsizhuo
Copy link
Copy Markdown
Member

背景

GSC 报 involutionhell.com 41 条 404,集中在
/docs/CommunityShare/Leetcode/<中文文件名> 这类路径。

根因

lib/source.ts 里的 transformer 会把 app/docs/career/interview-prep/leetcode/
下含中文的文件名转成拼音 slug(例:2309兼具大小写的最好英文字母_translated.md
对外是 /2309-jian-ju-...-translated)。

next.config.mjs 的 wildcard /docs/CommunityShare/Leetcode/:path*
/docs/career/interview-prep/leetcode/:path* 只做前缀替换,不做 slug 拼音化
所以旧 URL 301 跳过去之后 slug 还是中文,目标页依然 404。

改动

  • scripts/generate-leetcode-slug-map.mjs 构建时扫目录,输出
    generated/leetcode-slug-map.json 字面映射(当前 32 条)
  • proxy.ts 新增 redirectLeetcodeIfNeeded,Edge 端 O(1) 查表单跳 301
  • next.config.mjs 删掉 leetcode wildcard,改由 middleware 处理避免二跳
  • package.json prebuild 挂钩生成脚本
  • dev_docs/leetcode_slug_redirect.md 讲方案

选型理由

方案 否决原因
直接在 next.config 写 41 条硬编码 手写脆,文件增删没人同步
path-to-regexp wildcard 带参 空格 / [] / 中文在 Next.js 路由匹配不稳
middleware 动态跑 pinyin-pro 字典 ~1MB 塞进 Edge bundle 太大
✅ 构建时扫目录 + JSON + 查表 Edge bundle 只多几 KB

本地验证

$ pnpm build && pnpm start
# 典型旧 URL(对应 GSC 报告)
$ curl -I 'http://localhost:3000/docs/CommunityShare/Leetcode/2309%E5%85%BC%E5%85%B7%E5%A4%A7%E5%B0%8F%E5%86%99%E7%9A%84%E6%9C%80%E5%A5%BD%E8%8B%B1%E6%96%87%E5%AD%97%E6%AF%8D_translated'
# 301 → /docs/career/interview-prep/leetcode/2309-jian-ju-da-xiao-xie-de-zui-hao-ying-wen-zi-mu-translated ✓ (200)

抽查 5 条 GSC 报告的 404 URL,全部单跳 301 到正确拼音页(200)。

Test plan

  • pnpm typecheck 通过
  • pnpm lint 通过(无新增警告)
  • pnpm build 通过,middleware bundle 正常
  • 本地 5 条 GSC 404 URL 实测 301 到拼音页
  • 旧路径 ASCII slug 仍兼容(301 到新路径)
  • 新路径 ASCII slug 放行
  • 合入后在 GSC 里点「验证修复」触发重新爬取

GSC 报 41 条 /docs/CommunityShare/Leetcode/<中文文件名> 的 404。根因:
lib/source.ts 里的 transformer 会把 leetcode 目录下含中文的文件名转成拼音
slug,但 next.config.mjs 的 wildcard 只做前缀替换没做 slug 拼音化,跳过去
slug 还是中文导致 404。

改动:
- scripts/generate-leetcode-slug-map.mjs 构建时扫目录,输出
  generated/leetcode-slug-map.json「中文 stem → 拼音 stem」字面映射
- proxy.ts 新增 redirectLeetcodeIfNeeded:Edge 端 O(1) 查表单跳 301
- next.config.mjs 删 leetcode wildcard,改由 middleware 处理避免二跳
- prebuild 挂钩生成脚本,保证 JSON 永远同步最新文件列表
- 补 dev_docs/leetcode_slug_redirect.md 讲清方案
Copilot AI review requested due to automatic review settings April 20, 2026 11:43
@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 20, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
involutionhell-github-io Ready Ready Preview, Comment Apr 20, 2026 5:27pm
website-preview Ready Ready Preview, Comment Apr 20, 2026 5:27pm

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

该 PR 旨在解决 Google Search Console 中历史 Leetcode 文档旧链接(含中文文件名 slug)在 301 后仍 404 的问题,通过在 Edge 侧将旧中文 slug 单跳重定向到拼音化后的新路径,改善 SEO 抓取与用户访问体验。

Changes:

  • 新增构建脚本扫描 Leetcode 目录,生成 中文 stem → 拼音 stem 的 JSON 映射表
  • 在 Edge 侧(proxy/middleware)基于映射表对旧路径/中文 slug 做单跳 301 到新路径
  • 移除 next.config.mjs 中 Leetcode wildcard redirect,避免“前缀替换但 slug 未拼音化”的二跳/404

Reviewed changes

Copilot reviewed 5 out of 6 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
scripts/generate-leetcode-slug-map.mjs 构建时生成中文文件名到拼音 slug 的字面映射 JSON
generated/leetcode-slug-map.json 新增映射表产物,供 Edge 查表重定向
proxy.ts 新增 Leetcode 路径/中文 slug 的查表 301 重定向逻辑
next.config.mjs 删除 Leetcode wildcard redirect,改由 Edge 逻辑处理
package.json prebuild 增加生成映射表脚本,确保构建产物最新
dev_docs/leetcode_slug_redirect.md 记录方案、覆盖范围与验证方式

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread scripts/generate-leetcode-slug-map.mjs Outdated
Comment on lines +28 to +44
/**
* 与 lib/source.ts 中 convertSlugToPinyin 保持同步。
* 入参:单个 slug 片段(一般是文件名 stem)。
* 无中文直接原样返回;有中文则按拼音 + 非字母数字清洗 + 连字符拼接。
*/
function convertSlugToPinyin(text) {
const decodedText = decodeURIComponent(text);
if (!/[\u4e00-\u9fa5]/.test(decodedText)) return text;
return pinyin(decodedText, {
toneType: "none",
type: "array",
nonZh: "consecutive",
})
.map((t) => t.toLowerCase().replace(/[^a-z0-9]/g, ""))
.filter(Boolean)
.join("-");
}
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

已修:抽到 lib/leetcode-slug.tslib/source.ts 的 transformer 和 scripts/generate-leetcode-slug-map.mts 都 import 同一份 convertSlugToPinyin。脚本改成 .mts 走 Node 24 原生 TS 支持,避免 tsx v4.21 把 TS 转成 CJS 丢 named export 的问题。commit 25a076e

Comment thread proxy.ts Outdated
rawSlug = rest;
}

const mapped = SLUG_MAP[rawSlug];
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

已修:实测这确实是真 bug,不是理论风险。用 plain object 时 SLUG_MAP["__proto__"] 返回 [object Object]SLUG_MAP["constructor"] 返回 Object 构造函数(function Object() { [native code] }),会 301 到诡异 URL。改成 new Map(Object.entries(...)),Map 天然隔离原型链。实测 /docs/CommunityShare/Leetcode/__proto__ / constructor / hasOwnProperty 现在都按原样落地到不存在的新路径(正常 404),不会命中原型。commit 25a076e

Comment thread proxy.ts
Comment on lines 73 to +76
export function proxy(req: NextRequest) {
// Leetcode 老 URL / 中文 slug 优先做 301,避免后续 locale 逻辑给 404 页种 cookie
const leetcodeRedirect = redirectLeetcodeIfNeeded(req);
if (leetcodeRedirect) return leetcodeRedirect;
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这个是 Copilot 过时了——Next 16 把 middleware.ts 官方改名为 proxy.ts,导出符号也从 middleware 改成 proxy。仓库里 commit 7e079c3 "fix: 修复警告, 现在已经用Proxy代替Middleware了" 就是做这次迁移。

实测验证(production build + next start):

curl -I "http://localhost:3999/docs/CommunityShare/Leetcode/%5B146%5DLRU%20%E7%BC%93%E5%AD%98_translated"
# → 301 Location: /docs/career/interview-prep/leetcode/146lru-huan-cun-translated

Build 输出里也有 ƒ Proxy (Middleware) 那行,说明它被识别为 middleware。

1. 抽 convertSlugToPinyin 到 lib/leetcode-slug.ts,lib/source.ts 和生成脚本
   共用同一实现,消除双点维护(Copilot CR comment #1)
2. proxy.ts 里把 SLUG_MAP 从 plain object 换成 Map,实测 __proto__/constructor
   等原型链 key 会被错误命中(拿到 [object Object] / Object 构造函数),
   Map 天然隔离原型链(Copilot CR comment #2)
3. 脚本从 .mjs 改为 .mts 以便 import TypeScript 共享模块,用 Node 24 的
   原生 TS 支持执行,prebuild 走 node 不走 tsx(tsx v4.21 在 Node 24 下
   会把 TS 转成 CJS 导致命名 ESM 导出丢失)
@longsizhuo longsizhuo merged commit f392306 into main Apr 20, 2026
6 of 8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants