fix(proxy): 治本 —— 陈旧 dsh-auth cookie 回写清理(档案 98,修 431 → Failed to load plugins)
根因(2026-09-14 实测):dsh **每次实例 (重)启动都换** `dsh-auth-<随机后缀>` 的 **cookie 名**;平台此前 只在**转发给实例时**丢弃旧的(`mergeCookieHeader`),**从不回写浏览器** ⇒ 浏览器 cookie jar **只增不减** ⇒ `Cookie` 请求头越涨越长 ⇒ 实测 **12.8 KB 放行 / 19.1 KB 就被 431**(且 19 KB 时连 `/` 也 431) ⇒ 被 Cloudflare / 本机 nginx 直接拒掉,**请求根本到不了平台** ⇒ 那条 11 MB 合并脚本取不到 ⇒ 客户端报 `bundle script … failed to load` ⇒ 界面「Failed to load plugins」。 (旁证全对:平台日志无该请求 ✓/无痕正常=空 cookie 罐 ✓/服务器侧一切正常 ✓) 修法(只动 proxy.ts 一处):在响应头块里对**陈旧名字**回写 `Set-Cookie: <name>=; Path=/; Max-Age=0`。 · **只在「本次响应确实下发了 dsh-auth-*」时才动手** —— 那时才确知当前有效名字,绝不误删在用的那个; · 其余情形什么都不做;不带 `Domain=`(dsh 下发的是 host-only cookie); · 即使判断有误,档案 51 的「401 → 取新 cookie 透明重放」也会自愈。 验证: · 本机 `npm run verify` EXIT=0;服务器 `ci.sh` CI OK(48 pass / 0 fail); · `scripts/verify-inject.cjs` 新增防回退断言「会清理陈旧 dsh-auth cookie」; · **端到端实测**:带 2 个假 `dsh-auth-*` 请求 `/?token=…` → 303 响应含 **3 条 Set-Cookie,其中 2 条 Max-Age=0** 正是那两个假名字 ✅ ⚠️ 它**救不了已经 431 的当下**(那时请求在 CF/nginx 就被拒、到不了平台)⇒ 用户需**先手动清一次** (只删 `dsh-auth-*`、保留 `sid`),此后由本机制维持小 jar。⚠️ 根因仍在(官方每重启换名)⇒ **少重启**仍是要点。
This commit is contained in:
1 parent
cc1dc5c9a8
commit
ed0c3c0ad5
2 files changed
+40
No files matched your search
@@ -84,6 +84,15 @@ if (fs.existsSync(PROXY_SRC)) {
|
||||
} else {
|
||||
console.log(' ✓ proxy.ts 有 /plugins/ ETag + 304 短路(省掉每次 11 MB 重下)')
|
||||
}
|
||||
// 档案 98:陈旧 dsh-auth cookie 必须**回写浏览器删除**(否则 Cookie 头只增不减,
|
||||
// 涨到 ~15 KB 就被 431 拒在 CF/nginx,请求到不了平台 —— 见档案 98 实测)。
|
||||
const hasStale = /const staleNames =/.test(src) && /Max-Age=0/.test(src) && /startsWith\('dsh-auth-'\)/.test(src)
|
||||
if (!hasStale) {
|
||||
console.log(' ✗ proxy.ts 缺「陈旧 dsh-auth cookie 回写清理」(Cookie 头会只增不减 ⇒ 431)')
|
||||
bad++
|
||||
} else {
|
||||
console.log(' ✓ proxy.ts 会清理陈旧 dsh-auth cookie(防 Cookie 头无限增长)')
|
||||
}
|
||||
}
|
||||
|
||||
console.log(bad ? '结论:' + bad + ' 项不合格 ❌ —— 别推上线' : '结论:全部合格 ✅')
|
||||
|
||||
@@ -376,6 +376,37 @@ function proxyHttp(
|
||||
return
|
||||
}
|
||||
// 重放成功后把新 cookie 交给浏览器:之后(含 SSE 自动重连)不再需要重放。
|
||||
// ── 档案 98(治本):把**陈旧的 `dsh-auth-*` cookie 从浏览器里清掉** ──────────
|
||||
// 由来(2026-09-14 实测):dsh **每次实例 (重)启动都换** `dsh-auth-<随机后缀>` 的 cookie 名
|
||||
// (见本文件头部注释)。浏览器把每一个旧名都留着,而平台此前只在**转发给实例时**丢弃
|
||||
// 旧的(`mergeCookieHeader`),**从不回写浏览器** ⇒ jar **只增不减** ⇒ `Cookie` 请求头
|
||||
// 越涨越长 ⇒ 涨到 ~15 KB(实测 12.8 KB 放行 / 19.1 KB 已被 431)就被 Cloudflare /
|
||||
// 本机 nginx 直接 **431** 拒掉 —— 请求**根本到不了平台** ⇒ 那条 11 MB 合并脚本取不到
|
||||
// ⇒ 客户端报 `bundle script … failed to load`(界面「Failed to load plugins」)。
|
||||
// ⚠️ 所以这一块**救不了已经 431 的当下**(那时请求进不来),它的作用是**防复发**:
|
||||
// 用户手动清一次之后,jar 不会再长回去。
|
||||
// 为什么只在「本次响应确实下发了 dsh-auth」时才动手:只有这时我们**确知当前有效的名字**,
|
||||
// 才能安全清掉其余旧名;否则(例如普通 API 响应不带该 cookie)宁可什么都不做。
|
||||
{
|
||||
const scNow = headers['set-cookie']
|
||||
const scList = Array.isArray(scNow) ? scNow : scNow === undefined ? [] : [String(scNow)]
|
||||
const freshNames = scList
|
||||
.map((c) => /^\s*(dsh-auth-[^=;\s]*)\s*=/.exec(String(c))?.[1] ?? '')
|
||||
.filter((n) => n !== '')
|
||||
if (freshNames.length > 0) {
|
||||
const staleNames = (request.headers.cookie ?? '')
|
||||
.split(';')
|
||||
.map((x) => (x.split('=')[0] ?? '').trim())
|
||||
.filter((n) => n.startsWith('dsh-auth-') && !freshNames.includes(n))
|
||||
if (staleNames.length > 0) {
|
||||
headers['set-cookie'] = [
|
||||
...scList,
|
||||
...staleNames.map((n) => `${n}=; Path=/; Max-Age=0; Expires=Thu, 01 Jan 1970 00:00:00 GMT`),
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (authCookie !== undefined && replayCookies.length > 0) {
|
||||
const prev = headers['set-cookie']
|
||||
headers['set-cookie'] = [
|
||||
|
||||
Reference in new issue
Block a user