Skip to content

fix(139): keep valid tokens when refresh metadata is absent - #164

Merged
PIKACHUIM merged 4 commits into
mainfrom
fix/issue-122-139-optional-refresh
Nov 9, 2026
Merged

PIKACHUIM merged 4 commits into
mainfrom
fix/issue-122-139-optional-refresh

Conversation

@PIKACHUIM

@PIKACHUIM PIKACHUIM commented Nov 1, 2026 •

Copy link
Copy Markdown
Member

PR #164 正文(更新版)

fix(139): align token refresh and user_domain_id with Go

本描述对应已合并的 #164(squash commit 38cfdc4)。
它包含 615059d、c81b287、ceb957c、9bfd4fe,不包含后续提交 ff8c863。
ff8c863 的短信错误透传由后续 PR 补充。

Summary / 摘要

本 PR 让 Worker 的 139 驱动在凭证、刷新与 user_domain_id 上对齐 Go 主项目:

  1. 缺少 user_domain_id 不再导致存储初始化失败。
  2. 无 user_domain_id 时使用主项目同款旧版刷新接口。
  3. 移植 Go 的邮箱密码/快速登录(Step1/Step2/Step3),自动获取并保存 user_domain_id。
  4. 刷新失败时按 Go 的 loginAfterAuthorizationFailure 用用户名密码重新登录。
  5. 手动 Authorization + 手动 user_domain_id 仍然优先,不会被无端覆盖。
  6. 管理端补齐 username、password、sms_code、mail_cookies 字段,authorization 改为可选。

Motivation / 背景

Issue #122:现有 139 授权进入 15 天续期窗口后,如果配置里没有 user_domain_id,PC refreshToken 接口不可用,原实现直接抛错,导致仍有效的授权无法使用。

同时,Worker 缺少主项目的邮箱登录链路,无法像 Go 一样从登录结果自动获得并保存 userDomainId。

Changes / 改动内容

1. 凭证初始化

src/backend/drivers/139/util.ts 新增 ensureAuthorization():

  • Authorization 非空:直接使用,跳过登录。
  • Authorization 为空且 mail_cookies 含 Os_SSo_Sid/RMKEY:Step2 → Step3 快速登录。
  • Authorization 为空且 username + password:Step1 → Step2 → Step3 完整登录。
  • 支持 139 邮箱短信风控:发送验证码、保存更新后的 Cookie、填写 sms_code 后继续。
  • 登录成功后自动保存 authorization、user_domain_id、mail_cookies、sms_code。

2. Go 登录链路移植

新增 src/backend/drivers/139/login.ts:

  • Step1:mail.10086.cn 密码登录,处理 302、Set-Cookie、风险码 ec。
  • Step2:用 RMKEY 换 artifact。
  • Step3:yun.139.com/user/thirdlogin 加密登录,提取 authToken、account、userDomainId。
  • 加密:AES-CBC/AES-ECB、RSA PKCS1 v1.5、SHA1、排序 JSON,均对齐 Go。
  • Cookie:兼容 Cloudflare Workers 的 Headers.getAll("Set-Cookie") 与标准 getSetCookie()。

3. 刷新与回退

  • 有 user_domain_id:PC refreshToken。
  • 无 user_domain_id:旧版 authTokenRefresh.do。
  • 刷新失败:如果配置了用户名密码,按 Go 的 loginAfterAuthorizationFailure 重新密码登录;不接受 Cookie 快速登录作为回退。
  • 刷新失败且无可用凭证:保留当前授权,不再因为“临近过期”打断初始化或刷告警。

4. 持久化

src/backend/internal/op/storage.ts 新增登录状态回调,写入:

  • authorization
  • user_domain_id
  • mail_cookies
  • sms_code

5. 管理端字段

src/backend/server/admin.ts 为 139Yun 增加:

  • authorization(改为可选)
  • username
  • password
  • sms_code
  • mail_cookies
  • user_domain_id(可选,登录后自动填充)

行为矩阵

配置 行为
有 Authorization 不登录,手动 user_domain_id 直接生效
无 Authorization + mail_cookies 快速登录,自动获取并保存 user_domain_id
无 Authorization + username/password 完整登录,支持短信风控,自动获取并保存
刷新失败 + 用户名密码 密码重新登录,更新授权与 user_domain_id
无任何凭证 明确报错

Testing / 验证

npx tsx --test src/backend/drivers/139/*.test.ts
# 25 passed / 0 failed

npx tsx --test "src/backend/drivers/**/*.test.ts"
# 210 passed / 0 failed

npx tsx --test "src/backend/server/*.test.ts"
# 206 passed / 0 failed

npx tsc -p tsconfig.json --noEmit
# 无报错

npm run build:edge
# Edge build complete

覆盖场景:

  • 完整密码登录、Cookie 快速登录、短信发送与验证。
  • PC 刷新失败后的密码重登录。
  • 手动 user_domain_id 保留。
  • 空凭证报错。
  • RSA PKCS1 v1.5 与 Node 私钥解密对照。
  • AES CBC/ECB 加解密往返。

Compatibility / 兼容性

  • 无数据库结构变更。
  • 无新增环境变量。
  • 现有仅含 Authorization 的配置继续可用。
  • 旧版刷新接口作为无 user_domain_id 的后备路径。
  • authorization 从必填改为可选,旧前端如仍标记必填,以服务端/驱动校验为准。

Known boundaries / 已知边界

  • 没有真实 139 账号做端到端登录验证;登录链路通过 mock 与加密单测覆盖,合并前建议用真实账号验证一次密码登录和一次短信风控。
  • 前端提示文案与文档在独立仓库,未随本 PR 修改:
    • OpenList-Frontend 的 139Yun 字段提示仍是旧内容。
    • OpenList-Document/pages/guide/drivers/139.md 的“用户域 ID”章节仍主要描述容量用途。
  • 短信风控错误透传由后续 PR 补上(原 fix(139): keep valid tokens when refresh metadata is absent #164 漏掉了 ff8c863)。

Checklist

  • 139 驱动测试通过
  • 全部驱动测试通过
  • 服务端测试通过
  • tsc --noEmit 通过
  • build:edge 通过
  • 无新增依赖/环境变量
  • 手动 user_domain_id 与自动获取逻辑均已覆盖

Issue #122 reports that existing 139 authorizations became unusable as soon as they entered the 15-day refresh window, even when they were still accepted by the service. The PC refreshToken endpoint requires user_domain_id, but legacy storage configurations may not contain it.

Skip only the automatic refresh when user_domain_id is absent; retain the current authorization and allow the normal API request to determine whether the token is still valid. When user_domain_id is present, preserve the existing near-expiry refresh flow and persistence callback. Expose the optional field in the 139 storage form with a description.

Validation: 139 driver tests 13/13 passed; mutation check restoring the previous throw makes the new regression test fail; git diff --check passed.
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Nov 1, 2026 •

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
✅ Deployment successful!
View logs
openlist-work 9bfd4fe Nov 09 2026, 10:14 AM

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Nov 1, 2026 •

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
🔵 In progress
View logs
openlist-tsworkers 9bfd4fe Nov 09 2026, 10:13 AM

@pikachuren pikachuren left a comment •

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

🙏 感谢贡献 PR #164!感谢 @PIKACHUIM 提交~
🤖 AI 自动审核声明:本评审报告由 AI 自动生成,当前使用 DeepSeek-V4.1-Flash 模型进行分析。
⚠️ AI 分析结果仅供参考,可能存在误判或遗漏。如您发现任何问题或有不同意见,欢迎随时提出讨论和纠正。
⚠️ 重要提醒:即使 AI 评审认为代码质量良好且建议合并,最终是否合并仍需由项目维护者进行人工判定。项目维护者会综合考虑代码质量、项目规划、技术方向、团队资源等多方面因素做出是否合并的决策。

🎯 结论

✅ Approve — 把「可选刷新失败」降级为「跳过 + 告警」是对的取舍,可以合并

📖 概要

fix(139): keep valid tokens when refresh metadata is absent · Fixes #122 · 修复 139 云盘在 token 进入 15 天刷新窗口但未配置 user_domain_id 时直接抛错
核心改动:缺 user_domain_id 时跳过 PC refreshToken、保留当前 token 继续使用;表单新增可选字段

🧭 整体方案

把「自动续期」从「硬前置条件」改回「可选增强」——不能因为拿不到续期参数就否掉一个仍然可用的授权。语义收窄、改动集中在一个提前返回上,符合最小改动原则。

📊 变更统计

3 个文件(+65 / -3) | 功能 ⭐⭐⭐⭐⭐ | 最小改动 ⭐⭐⭐⭐⭐ | 前向兼容 ⭐⭐⭐⭐⭐ | 方案设计 ⭐⭐⭐⭐⭐

🚨 关键问题

无重大问题。

P2(可选):

  • 💡 drivers/139/util.ts 的 warnedMissingUserDomainId 是实例级去重,而注释里写明 refreshAuthorizationIfNeeded() 可能在「每次 API 请求前」被调用。如果某些路径上的驱动实例不进缓存(例如 deferTokenPersistence 的临时实例、或 storage/update 时新建的实例),过期临近期间仍可能每个请求打一条 warn。建议把告警降为「首次 + N 次心跳」或直接写进 storage.status。
  • 💡 语义变化值得在描述里点明:跳过刷新后,token 真正过期时上游会返回鉴权错误,用户看到的是「驱动报上游 401」而不是「请配置 user_domain_id」。建议在错误信息里做一次归因提示(例如捕获 401 时附带「可能因未配置 user_domain_id 导致无法自动续期」),否则排查成本会从「配置错误」转移到「驱动异常」。

📂 逐文件分析

src/backend/drivers/139/util.ts

改动意图:缺 user_domain_id 时不再抛错。
代码逻辑:if (!userDomainId) → 打一次 warn + return,保留 this.authorization 不变;后续正常请求流程继续。
评价:提前返回点在「构造 PC refresh 请求」之前,没有留下半初始化的状态;pieces.length < 3 / 时间戳解析失败的既有早退分支也未受影响。

src/backend/drivers/139/driver.test.ts

评价:用 calls 数组断言「只发生了 routePolicy 请求、没有打 refreshToken 端点」,再断言 getAuthString() 与原 token 完全一致——比只断言「没抛错」更能防回归。

src/backend/server/admin.ts

评价:user_domain_id 标为 required: false 且 help 说明了用途,与描述里的「可选」一致,没有破坏已有表单。

✅ 待处理清单

  • [P2] 告警去重从实例级改为「一次性/状态位」,避免高频路径刷日志
  • [P2] token 真正过期时补一条归因提示(指向 user_domain_id)

🎯 结论:✅ Approve — 取舍合理、测试有效、向后兼容。

pikachuren added 3 commits November 9, 2026 17:57
The PC refreshToken API requires user_domain_id, so configs that only
contain Authorization were left with no automatic refresh and emitted a
near-expiry warning on every storage init.

Fall back to the legacy authTokenRefresh.do endpoint already used by the
Go backend when user_domain_id is empty. It only needs the current token
and account, accepts both <token> and <accessToken> candidates, and only
writes back a token with a later expiry. Refresh failures are silent and
keep the current authorization usable until the service rejects it.

user_domain_id stays optional and the PC path is unchanged when set.

Tests: 139 driver 14/14, drivers 199/199, server 206/206, tsc clean.
Align the Worker driver with the Go backend's credential flow:

- Port step1 mail password login, step2 artifact exchange and step3
  encrypted third-party login (AES-CBC/ECB, RSA PKCS1 v1.5).
- Support fast login with existing mail_cookies and SMS risk control
  (sms_code), including cookie persistence between attempts.
- Persist authorization, user_domain_id, mail_cookies and sms_code after
  a successful login, matching Go's MustSaveDriverStorage behavior.
- Keep manual Authorization + user_domain_id authoritative: when
  Authorization is present no login runs; a login only overwrites the
  manual value when it is actually triggered.
- Re-login after a refresh failure when credentials are available,
  matching Go's loginAfterAuthorizationFailure.
- Expose authorization (now optional), username, password, sms_code and
  mail_cookies in the 139 storage form.

Tests: driver 25/25, all drivers 210/210, server 206/206, tsc clean.
Go's loginAfterAuthorizationFailure never retries cookie fast login after
a refresh failure; mail_cookies are only reused as device context for
the password login request.

Restrict the refresh-failure re-login to username + password and add a
skipFastLogin option so the fallback performs step1 -> step2 -> step3
instead of step2 -> step3.

Tests: 139 driver 25/25, tsc clean.
@PIKACHUIM
PIKACHUIM merged commit 38cfdc4 into main Nov 9, 2026
2 of 3 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