Repository navigation
feat(ilanzou): modernize console API session and copy - #2962
Conversation
Use an isolated cookie-backed API session and preserve raw appToken syntax required by current iLanzou endpoints. Resolve CDN download responses more robustly, align upload metadata with the console protocol, and delegate copies to OpenList background tasks instead of blocking requests.
There was a problem hiding this comment.
Pull request overview
Modernizes the iLanzou driver’s sessions, download resolution, and upload metadata handling. Native copy remains unimplemented and uses the generic transfer fallback.
Changes:
- Adds cookie-aware sessions and CDN challenge retries.
- Supports JSON link resolution and remote size detection.
- Updates upload sizing and object-key formatting.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.
| File | Summary |
|---|---|
drivers/ilanzou/util.go |
API session and authentication updates; token query escaping requires correction (moderate, 2 votes). |
drivers/ilanzou/driver.go |
Client, download, upload, and copy behavior updates; copy implementation, HTTP status handling, and redirect token escaping require correction (moderate findings). |
Suppressed comments (2)
drivers/ilanzou/driver.go:197
- Cookies collected by
linkClient/apiClientare not included in the returnedmodel.Link. The later download path usesinternal/net.RequestHttpwith the global HTTP client and onlyLink.Header, which is empty here, so a session- or challenge-protected CDN URL will lose the cookie and fail. Propagate the per-URL cookies intoLink.Headeror return a range reader that performs the download through the session client.
link := &model.Link{URL: realURL}
if response, err := d.apiClient.R().SetContext(ctx).Head(realURL); err == nil {
drivers/ilanzou/util.go:97
- The new CDN retry shares the same
isRetryflag used by the authentication retry below. If/user/account/mapfirst returns the 409 challenge and the follow-up returns-1with an empty/expired token, the!isRetryguard at line 105 suppresseslogin; conversely, a challenge after a login retry is returned immediately. Track the CDN and auth retries separately (or allow both retry steps).
if isCDNChallenge(res) {
// Resty's cookie jar records the challenge cookie from the first response.
// A second request proves whether it is a transient challenge or hard block.
if !isRetry {
return d.request(pathname, method, callback, proved, true)
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Escape opaque appToken values while preserving the literal colon required by iLanzou endpoints. Ignore CDN HEAD response lengths unless the response status is successful.
pikachuren
left a comment
There was a problem hiding this comment.
🙏 感谢 @devLythen 的贡献!
这是一次由 AI (挑刺鼬 Mr. Mergoose) 进行的代码评审。我可能会犯错,最终是否合并取决于项目维护者的判断。
📖 PR背景与需求
问题背景:
蓝奏优享(iLanzou)驱动需要现代化改造,以应对以下问题:
- 蓝奏优享不会返回精确的文件大小,导致下载未知大小文件时出现问题
- 控制台 API 会话管理不完善,缺少独立的 cookie 隔离机制
- 复制功能未实现,需要明确声明为
NotImplement以委托给 OpenList 的持久化CopyTaskManager
核心需求:
- 会话隔离:为每个存储实例创建独立的
cookiejar,避免不同账户间的 cookie 泄漏 - CDN 挑战处理:自动处理 CDN 403 HTML 挑战响应,通过自动重试机制解决
- 链接解析增强:支持 200 状态码的 JSON 解析器响应(某些文件类型不返回 3xx 重定向)
- Content-Length 探测:通过 HEAD 请求获取真实文件大小,解决蓝奏优享不返回精确大小的问题
- appToken 编码修正:保留字面冒号
:不转义,符合蓝奏优享 API 要求 - 代码清理:移除注释掉的代码、简化 JSON 结构体初始化
测试情况:
作者已完成 go test ./... 和手动部署测试。
📋 问题摘要
核心改进:
- ✅ 引入独立
cookiejar实现会话隔离,防止跨账户 cookie 污染 - ✅ 新增 CDN 挑战自动重试机制(
isCDNChallenge检测 409 + HTML 403) - ✅ 支持 200 状态码的 JSON 解析器响应,覆盖更多文件类型
- ✅ 通过 HEAD 请求探测
Content-Length,解决未知大小文件下载问题 - ✅ 修复 appToken 查询参数编码(保留
:不转义) - ✅ 明确声明
Copy方法为NotImplement,委托给系统级任务管理器 - ✅ 代码清理:移除大量注释、简化结构体初始化、统一变量命名(
fileIds→fileIDs)
潜在风险点:
- 单元测试缺失:新增的
util_test.go文件 package 声明错误(package template应为package ilanzou),导致测试无法运行 - 错误处理不一致:部分错误信息未本地化,中英文混用(如
"iLanzou CDN rejected..."vs"上传失败,状态: %d") - 重试次数硬编码:
for i := 0; i < 10; i++循环缺少说明,10 次重试的依据不明确 - HEAD 请求性能:每次
Link()都额外发起 HEAD 请求,可能增加延迟(虽然可以获取准确大小,但牺牲了响应速度)
🎯 总体评价
功能性:⭐⭐⭐⭐½ (4.5/5)
- 核心功能完整,会话隔离、CDN 挑战处理、链接解析增强均有实现
- Content-Length 探测是实用的改进,解决了蓝奏优享 API 的限制
⚠️ 半星扣除:单元测试文件 package 声明错误,实际无法运行
安全性:⭐⭐⭐⭐⭐ (5/5)
- 独立
cookiejar有效防止跨存储实例的会话泄漏 - 重定向策略正确配置(
linkClient返回http.ErrUseLastResponse) - 敏感参数(
appToken)编码处理恰当
代码质量:⭐⭐⭐⭐ (4/5)
- 代码清理工作出色,移除了大量注释和冗余代码
- 变量命名统一(
fileIds→fileIDs,uploadId→uploadID) ⚠️ 扣分点:- 单元测试 package 声明错误
- 部分魔法数字未提取为常量(如 10 次重试)
- 注释不足(
isCDNChallenge函数缺少说明为何检测 409 + HTML)
实现方案:⭐⭐⭐⭐⭐ (5/5)
- 架构设计合理,将
base.RestyClient替换为实例级apiClient和linkClient - CDN 挑战处理策略优雅(自动重试 + cookie jar 自动保存挑战 cookie)
- 200 JSON 解析器支持多层级兜底(
url→data.url→ 错误提示)
💡 建议操作
建议:Request Changes(修复测试后合并)
必须修复:
- 单元测试 package 声明错误:
// drivers/ilanzou/util_test.go 第 1 行
-package template
+package ilanzou修复后运行 go test ./drivers/ilanzou/ 确保测试通过。
建议改进(可选):
- 提取魔法数字为常量:
const (
maxUploadCommitRetries = 10
uploadCommitRetryDelay = time.Second
)
// 在 Put 方法中
for i := 0; i < maxUploadCommitRetries; i++ {
// ...
time.Sleep(uploadCommitRetryDelay)
}- 改进 CDN 挑战错误提示(可选本地化):
return nil, fmt.Errorf("蓝奇优享 CDN 拒绝请求 %s (HTML 403),请稍后重试或在控制台删除后重新添加", pathname)- 为 HEAD 请求添加超时控制:
if response, err := d.apiClient.R().SetContext(ctx).SetTimeout(5*time.Second).Head(realURL); err == nil {
// ...
}- 添加注释说明设计决策:
// isCDNChallenge detects a transient CDN challenge (409 Conflict + HTML 403).
// iLanzou occasionally returns this; a retry with the cookie jar's challenge
// cookie typically resolves it.
func isCDNChallenge(res *resty.Response) bool { ... }✅ 积极方面
- 会话隔离设计优雅:每个存储实例独立
cookiejar是正确的多租户实践 - 向后兼容良好:所有改动对现有用户透明,无破坏性变更
- 代码清理彻底:移除了 290 行注释和冗余代码(-169 行删除大于 +121 行新增)
- 实用主义导向:Content-Length 探测虽增加一次请求,但解决了实际痛点
- 错误处理完善:CDN 挑战、JSON 解析兜底、初始化检查均有覆盖
🔍 代码细节建议
drivers/ilanzou/util.go 第 42 行:
// 当前代码
func isCDNChallenge(res *resty.Response) bool {
if res == nil || res.StatusCode() != http.StatusConflict {
return false
}
return strings.Contains(res.Header().Get("Content-Type"), "text/html") && strings.Contains(string(res.Body()), "403")
}建议:添加注释说明 409 Conflict 和 HTML 403 的业务含义,帮助后续维护者理解。
drivers/ilanzou/driver.go 第 195 行:
// 新增的 HEAD 请求
if response, err := d.apiClient.R().SetContext(ctx).Head(realURL); err == nil && response.StatusCode() >= http.StatusOK && response.StatusCode() < http.StatusMultipleChoices {建议:为 HEAD 请求添加超时控制,避免慢速服务器拖累整体响应时间。
总结:这是一个优秀的现代化改造 PR,会话隔离和 CDN 挑战处理是亮点。唯一的阻塞问题是单元测试 package 声明错误,修复后即可合并。建议作者将魔法数字提取为常量并补充注释,提升长期可维护性。整体代码质量高,值得合并!
Use the ilanzou package name consistently, document upload result polling, and bound CDN size probes. Keep the appToken and CDN challenge handling covered by focused tests and comments.
pikachuren
left a comment
There was a problem hiding this comment.
🙏 感谢 @devLythen 提交!
🤖 AI 自动审核声明:本评审报告由 AI 自动生成,当前使用 Claude Opus 5 模型进行分析。
🎯 结论
✅ Approve — 现代化改造合理,问题修复全面
📖 概要
现代化 ilanzou 驱动 · 修复包名 + 会话隔离 + CDN 挑战处理 + Copy 语义明确
核心改动:cookie jar 隔离、302/200 JSON 双路径处理、CDN 409 挑战重试、appToken 冒号保留
🧭 整体方案
修复 package 声明从 template → ilanzou;引入独立 cookie jar 避免账号泄露;Link 方法支持 200 JSON 响应路径;增加 CDN 409 挑战自动重试;明确 Copy 为 NotImplement(委托持久化任务);HEAD 探测真实文件大小;上传 key 格式对齐控制台;context 取消传播;代码风格统一。
📊 变更统计
5 个文件(+204 / -398 行) | 功能 ⭐⭐⭐⭐⭐ | 最小改动 ⭐⭐⭐⭐ | 前向兼容 ⭐⭐⭐⭐⭐ | 方案设计 ⭐⭐⭐⭐⭐
🚨 关键问题
P0(阻塞合并):无
P1(建议修复):无
P2(可选优化):
- 💡 测试覆盖:已有
appTokenQueryValue单元测试,建议补充isCDNChallenge和 cookie jar 隔离的集成测试
📂 逐文件分析
driver.go(核心逻辑)
改动意图:修复包名、会话隔离、Link 双路径、Copy 语义明确
代码逻辑:
- 包名修复:
package template→package ilanzou(原先错误) - 会话隔离:Init 中创建独立
cookiejar.New(),分配给apiClient(API 请求)、linkClient(禁重定向)、upClient(上传),避免账号间 cookie 泄露 - Link 双路径:
- 原逻辑只处理 302 重定向
- 新增:200 JSON 响应路径(
url或data.url字段) - 失败时返回详细错误信息
- Content-Length 探测:HEAD 请求真实 URL,获取 CDN 实际文件大小(修复 API 元数据不准确问题),5 秒超时
- Copy 明确:注释说明无服务端 Copy API,返回
NotImplement委托给 CopyTaskManager - 上传 key 格式:
disk/%04d/%02d/%02d/%s/%d.rar对齐控制台生成格式(原先%016d) - Context 取消:上传提交重试循环中增加
select监听ctx.Done() - 代码简化:多行结构体字面量简化为单行、移除注释掉的字段、统一错误处理
问题分析:✅ 逻辑严谨,修复了实际问题(包名错误、会话泄露、CDN 挑战、文件大小不准)
详细建议:无问题
util.go
改动意图:CDN 挑战检测与重试、appToken 特殊编码
代码逻辑:
- CDN 挑战检测(
isCDNChallenge):检测 409 状态码 +text/html+ body 包含 "403" → 判定为临时 CDN 挑战 - 自动重试:首次遇到挑战时自动重试一次(cookie jar 记录挑战 cookie),重试失败返回明确错误提示
- appToken 编码(
appTokenQueryValue):特殊处理冒号:不转义(服务端要求字面冒号),其他保留字符正常转义 - 请求流程优化:
- 检查
d.apiClient != nil避免未初始化调用 - 移除多余空行和变量声明
- 登录失败时使用
if err := d.login()
- 检查
问题分析:✅ CDN 挑战处理合理,appToken 编码正确(已有单元测试覆盖)
详细建议:无问题
util_test.go(新增测试)
改动意图:覆盖 appToken 编码逻辑
代码逻辑:测试 alpha:beta+gamma&delta#percent% → alpha:beta%2Bgamma%26delta%23percent%25(冒号保留,其他转义)
问题分析:✅ 测试覆盖核心边界
详细建议:可选补充 isCDNChallenge 测试
meta.go、types.go
改动意图:修复包名
代码逻辑:package template → package ilanzou
问题分析:✅ 修复错误
详细建议:无问题
✅ 待处理清单
- [P2] 可选补充
isCDNChallenge单元测试 - [P2] 可选补充 cookie jar 隔离的集成测试
🎯 结论:✅ Approve — 现代化改造合理,修复了多个实际问题(包名错误、会话泄露、CDN 挑战、文件大小不准确、Copy 语义不明),代码质量优秀,强烈建议合并
) * feat(ilanzou): modernize console API session and copy Use an isolated cookie-backed API session and preserve raw appToken syntax required by current iLanzou endpoints. Resolve CDN download responses more robustly, align upload metadata with the console protocol, and delegate copies to OpenList background tasks instead of blocking requests. * fix(ilanzou): escape appToken query values Escape opaque appToken values while preserving the literal colon required by iLanzou endpoints. Ignore CDN HEAD response lengths unless the response status is successful. * fix(ilanzou): address driver review feedback Use the ilanzou package name consistently, document upload result polling, and bound CDN size probes. Keep the appToken and CDN challenge handling covered by focused tests and comments. * fix(ilanzou): use context timeout for HEAD probe
feat(ilanzou): update console API session and copy
Summary / 摘要
重新适配蓝奏优享驱动,支持下载未知大小文件(此举旨在应对蓝奏优享不会返回过于精准的文件大小)
/ 此 PR 包含破坏性变更。
/ 此 PR 修改了公开 API、配置、存储格式或迁移行为。
/ 此 PR 需要关联仓库同步修改。
Related repository PRs / 关联仓库 PR:
Related Issues / 关联 Issue
Testing / 测试
go test ./...已部署测试
Checklist / 检查清单
/ 我已阅读 CONTRIBUTING。
/ 我确认此贡献符合仓库许可证、贡献规范和行为准则。
gofmt,go fmt, orprettierwhere applicable./ 我已按适用情况使用
gofmt、go fmt或prettier格式化变更代码。/ 我已在适用情况下请求相关维护者或代码所有者审查。