feat: 路由遮蔽检查脚本;切换前把前后端接口逐条对了一遍
## 切换前核对:前端调的接口后端有没有漏
写脚本把前端 224 个调用点和后端注册的路由逐条比对。**零缺口** —— 唯一报出来的
一条是我的正则被嵌套括号截断了(`users/${encodeURIComponent(...)}/badges`),
后端那条路由是有的。「后端有、前端没调」那 25 条也逐个看过,全是脚本的假阳性
(把 `.get("user")` 这类非路由调用当成了路由)和假阴性(前端用三元表达式拼路径,
正则看不见,比如 `GET /me` 其实在 shared/api.ts 里被调)。
结论是没发现缺口,但这个脚本不够可靠、不足以证明"一定没有",所以没留进仓库。
## 路由遮蔽检查(留成常驻脚本)
比"有没有漏"更值得防的是遮蔽:**Hono 按注册顺序匹配,不是静态优先**。
`/problems/:id` 注册在 `/problems/random` 前面的话,后者永远进不去 ——
不报错、不警告,只是静默走进前一条的 handler。阶段 4 真实发生过一次,
两个教师用的分析端点被吃掉,一直到评审才发现。
全仓 167 条路由按真实注册顺序扫:**零遮蔽**。
这个结论敢下,是因为检测器本身也验了:
- 自检用例里放了阶段 4 那个历史真实案例,能抓到;边界(两边都是参数、
段数不同、不同前缀)不误报
- 核对了 24 个 router 全在扫描范围内,没有漏扫
- 反向验证:往 problem.ts 末尾加一条注册在 `:displayId` 之后的字面量路由,
脚本立刻报出来并 exit 1
未经验证的检测器报"没问题"是没有意义的 —— 这个教训今天已经吃过两次
(tree-sitter 那次、SQL 内存那次)。
脚本落在 apps/api/src/scripts/check-route-shadowing.ts,
`bun run --filter '@oj2/api' check:routes`,加完路由跑一下。
CLAUDE.md 里也写了。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
14
CLAUDE.md
14
CLAUDE.md
@@ -35,8 +35,9 @@ bun run dev # api(3000) + worker + web(5173) 一起起
|
||||
常用检查:
|
||||
|
||||
```bash
|
||||
bunx tsc --noEmit -p apps/api # 后端类型检查
|
||||
cd apps/web && bun run build # 前端构建(vite 不做类型检查,构建即验证)
|
||||
bunx tsc --noEmit -p apps/api # 后端类型检查
|
||||
bun run --filter '@oj2/api' check:routes # 路由遮蔽检查,加完路由跑一下
|
||||
cd apps/web && bun run build # 前端构建(vite 不做类型检查,构建即验证)
|
||||
```
|
||||
|
||||
**不要写测试** —— 沿用上一代的项目约定。验证靠实跑:起服务、打接口、看结果。
|
||||
@@ -69,6 +70,15 @@ dev 直接起不来。
|
||||
「把整个程序再跑一遍」→ 指数级 fork。这不是假想,开发时炸过一次开发机。
|
||||
`OJ2_SQL_CHILD` 那道递归闸不要删。
|
||||
|
||||
### 加路由要防遮蔽
|
||||
|
||||
**Hono 按注册顺序匹配,不是静态优先**(实测确认过,别凭直觉)。`/problems/:id`
|
||||
注册在 `/problems/random` 前面的话,后者永远进不去 —— 而且不报错、不警告,
|
||||
只是静默走进前一条的 handler。阶段 4 真实发生过一次,两个教师用的分析端点被吃掉,
|
||||
一直到评审才发现。
|
||||
|
||||
加完路由跑 `bun run --filter '@oj2/api' check:routes`。
|
||||
|
||||
### 判题状态码要三处同步
|
||||
|
||||
`apps/api/src/judge/status.ts`、`apps/web/src/utils/constants.ts`、
|
||||
|
||||
Reference in New Issue
Block a user