Files
chunyu_prject_react/scripts/check_routes.mjs
T
chunyu 24f200bfa6 feat(C-01):路由/代理前缀冲突修复
SPA冲突路由改名+旧路径Navigate重定向(/api-docs→/open-api-docs、/api-detail→/open-api-detail、/article→/post、/user→/user-home);vite代理宽前缀改精确正则白名单;nginx.conf同步同口径;新增86路由回归脚本+Playwright 3项(86/86全绿)。
2026-09-15 15:25:59 +08:00

82 lines
3.8 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env node
/* C-01 路由回归脚本:73 条 SPA 路由直访检查。
* 检测三件套:① Django 404/JSON 劫持 ② #root 空(白屏)③ 正文长度。
* 用法:node scripts/check_routes.mjs --base http://127.0.0.1:5173
* 期望:PASS 73/73,hijacked=0,blank=0
*/
import { readFileSync } from 'node:fs'
const args = process.argv.slice(2)
const baseIdx = args.indexOf('--base')
const BASE = baseIdx >= 0 && args[baseIdx + 1] ? args[baseIdx + 1].replace(/\/$/, '') : 'http://127.0.0.1:5173'
// 与 src/App.tsx 的 <Route path> 一一对应(不含 * 兜底)
const ROUTES = [
'/', '/settings', '/history', '/favorites',
'/open-api-docs', '/open-api-directory',
'/profile', '/profile/email', '/profile/phone', '/profile/change-password',
'/use', '/document', '/open-api', '/utility', '/learn', '/articles', '/bug',
'/search', '/messages', '/chat',
'/open-api-detail/1', '/tool-detail', '/course-learn', '/post/1',
'/privacy', '/agreement', '/manual', '/system-message',
'/user-home/1', '/user-home', '/console',
'/article-manage', '/bug-detail', '/article-editor', '/learn-manage', '/learn-editor',
'/baidu-translate',
'/wallet/points', '/wallet/coins', '/wallet/coins/recharge', '/task-center', '/wallet/invite',
'/invite/abc123', '/forgot-password', '/register',
'/utility/json-formatter', '/utility/base64', '/utility/timestamp', '/utility/regex',
'/utility/code-runner', '/utility/text-diff', '/utility/encoding-converter',
'/utility/charset-converter', '/utility/color-converter', '/utility/code-formatter',
'/color-picker', '/qrcode-generator', '/utility/image-compressor', '/utility/calculator',
'/utility/date-calculator', '/utility/image-editor', '/changelog',
'/ip-location', '/currency-detail',
'/open-api-detail/currency', '/open-api-detail/baidu-translate',
'/open-api/air-quality-details', '/open-api/weather-details',
'/open-api/qrcode-generator', '/open-api/ip-location', '/open-api/password-generator',
'/shorturl-detail',
// 旧路径重定向(应返回 SPA,由前端 <Navigate> 承接,不应被代理劫持)
'/api-docs', '/api-directory', '/api', '/api-detail/1',
'/api-detail/currency', '/api-detail/baidu-translate',
'/api/air-quality-details', '/api/weather-details',
'/api/qrcode-generator', '/api/ip-location', '/api/password-generator',
'/article/1', '/user', '/user/1',
]
function isHijacked(status, text) {
if (status === 404 && /Django|Not Found|Page not found/i.test(text)) return true
const t = text.trim()
// 后端 JSON 特征:{"code":...} / {"detail":...} 且无前端根节点
if (/^\s*\{[\s\S]*"(code|detail|message)"[\s\S]*\}\s*$/.test(t.slice(0, 2000)) && !text.includes('id="root"')) return true
return false
}
// dev 模式下 index.html 的 #root 恒为空(客户端水合前),
// 因此"白屏"只能断言"是否返回了 SPA 外壳":含 id="root" 即算 SPA 到达。
// 真正的渲染白屏由 Playwright 抽查覆盖(见 PROGRESS)。
function isSpaShell(text) {
return text.includes('id="root"')
}
let pass = 0, hijacked = 0, blank = 0
const bad = []
for (const r of ROUTES) {
const url = BASE + r
try {
const res = await fetch(url, { redirect: 'manual' })
const text = await res.text()
const hj = isHijacked(res.status, text)
const bl = !hj && !isSpaShell(text)
if (hj) hijacked++
if (bl) blank++
if (!hj && !bl) { pass++; console.log(`ok ${r}`) }
else { bad.push(r); console.log(`${hj ? 'HIJACKED' : 'BLANK '} ${r} (status=${res.status} len=${text.length})`) }
} catch (e) {
bad.push(r)
console.log(`ERROR ${r} (${e.message})`)
}
}
console.log('---')
console.log(`ROUTES=${ROUTES.length} PASS=${pass} hijacked=${hijacked} blank=${blank}`)
if (bad.length) { console.log('BAD:'); bad.forEach((r) => console.log(' - ' + r)) }
process.exit(pass === ROUTES.length ? 0 : 1)