feat(drivers): add Teldrive V2 driver - #3000
Conversation
pikachuren
left a comment
There was a problem hiding this comment.
🙏 感谢 @kkbdvl 提交!
🤖 AI 自动审核声明:本评审报告由 AI 自动生成,当前使用 Claude Opus 5 模型进行分析。
🎯 结论
✅ 建议 Approve(由维护者人工确认)— 代码注释解释了「为什么」而非「做了什么」,是本轮质量最高的新驱动之一
📖 概要
feat(drivers): add Teldrive V2 driver · 新增 Teldrive V2 驱动。
核心改动:新增 drivers/teldrive_v2 包(含单元测试),实现 tree-hash 算法,鉴权从 v1 的 cookie 改为 X-Api-Key 头。
🧭 整体方案
技术路线是针对 Teldrive v2 API 的完整适配。这个 PR 给我印象最深的是注释质量——几处关键设计都解释清楚了背后的约束,而不是复述代码:
shareToken上方的注释说明了为什么需要缓存:Teldrive 的GET /files/{id}/shares只返回 share 的 id、过期时间和下载次数,唯独不返回 token,token 只在创建时返回一次。因此复用 share 就必须自己记住 token。这是外部 API 的非显然约束,注释点明后,shareCache的存在就完全合理了。driver_test.go中注释说明「teldrive 把多个文档化状态都collapse 到 409」,解释了为何要对整个 4xx 做统一处理。treeHashBlockMiB注明了是 teldrive 内部internal/treehash的固定块大小。
这类注释正是代码里最该写的东西。此外驱动附带了 driver_test.go 与 tdhash_test.go 两个测试文件,在新驱动 PR 中很少见。
📊 变更统计
10 个文件(+1439 / -1 行) | 功能 ⭐⭐⭐⭐⭐ | 最小改动 ⭐⭐⭐⭐ | 前向兼容 ⭐⭐⭐⭐⭐ | 方案设计 ⭐⭐⭐⭐⭐
🚨 关键问题
P0(阻塞合并):无
P1(建议修复):无
P2(可选):
- 💡
shareCache使用sync.Map缓存 file UUID → share token,但看起来没有容量上限或淘汰机制(仅按expiresAt判断有效性)。长期运行且访问大量不同文件的实例,这个 map 会持续增长。是否考虑加一个定期清理过期项的协程,或改用带上限的 LRU 呢? - 💡 创建 share 是一个有副作用的写操作(在 Teldrive 侧生成公开分享链接)。请问驱动被移除或存储被删除时,这些已创建的 share 会被清理吗?如果不会,可能在用户的 Teldrive 上遗留大量公开链接,建议在文档中说明~
- 💡 分享 token 会拼进 URL 并返回给客户端,意味着持有该 URL 者无需鉴权即可下载。这是 share 机制的固有语义、也是实现直链所必需,但建议在 driver 的 help 文案中提示用户注意~
- 💡
go.mod有 1 行改动,看起来是依赖版本微调。建议在 PR 描述中说明改动原因~ - 💡
pkg/utils/hash/tdhash.go把 tree-hash 放在通用 hash 包下。若该算法仅 Teldrive 使用,是否考虑放在驱动包内以减少通用包的表面积?当然如果预期会被复用,放这里也合理~
🔐 安全审查
- 审查方式:扫描命令执行、TLS 绕过、硬编码凭证、可疑外连
- 安全评估:✅ 未发现恶意代码
- 详细结论:无
exec.Command、无InsecureSkipVerify、无硬编码密钥;ApiKey由用户配置并通过X-Api-Key请求头传递(优于放在 URL 中);请求目标为用户自建的 Teldrive 实例地址,Init中对url与api_key均做了必填校验。
📂 逐文件分析
drivers/teldrive_v2/driver.go / util.go / upload.go
改动意图:实现驱动主体、工具函数与上传。
代码逻辑:shareToken 优先查缓存,未命中或已过期才创建新 share 并记录 token;Link 用 token 拼接直链。
问题分析:逻辑正确,对「token 不可事后取回」这一约束的处理是恰当的;缓存增长与 share 清理可优化(P2)。
drivers/teldrive_v2/driver_test.go / pkg/utils/hash/tdhash_test.go
问题分析:新驱动自带测试,覆盖 hash 算法与错误状态处理,加分项。
drivers/teldrive_v2/meta.go
问题分析:ApiKey 的 help 写明了获取路径(Settings → API keys),对用户友好。
✅ 待处理清单
- [P2] 为
shareCache增加过期清理或容量上限 - [P2] 说明驱动移除时已创建 share 的清理策略
- [P2] 在 help 中提示分享链接的免鉴权特性
- [P2] 说明
go.mod改动原因
🎯 结论:✅ 建议 Approve — 实现完整、鉴权方式得当、自带测试,注释质量尤其突出,无阻塞项。
TelDrive v2 rewrites its HTTP API: every path moves under /api/v1, the listing is cursor-paginated instead of page-numbered, objects are addressed by UUID rather than path, uploads go through durable server-side sessions, and every mutating endpoint requires an Idempotency-Key. None of it is reachable from the existing Teldrive driver, so this adds a separate one and leaves the v1 driver untouched. Notable differences from the v1 driver: - Copy is a single request; the server copies a whole subtree transactionally, so no client-side recursion is needed. - Uploads create a session, PUT each part, then complete. Parts are idempotent on (uploadId, partNo), and an interrupted upload resumes by reusing its session and skipping parts the server already stored. - The client mtime is preserved, which the v1 driver could not do. - Files carry a BLAKE3 tree hash. It is registered as blake3_tree in pkg/utils/hash and reported on every object; when hash_enabled is set, each part is also sent with a checksum for the server to verify. Part uploads use a dedicated resty client: the shared one caps requests at 30s, which a part cannot meet because the server only responds once it has relayed the part to Telegram, and it would buffer the whole part in memory to compute Content-Length. Requires a TelDrive v2 server at e3142b5 or newer, where the move endpoint began accepting a conflictPolicy other than "fail". Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Thanks for the detailed review. I've pushed changes for two of the five P2 items and would like to explain my reasoning on the other three. ✅
|
0a52b8f to
a992395
Compare
Summary / 摘要
Adds a driver for TelDrive v2.
TelDrive v2 is a full rewrite of the server's HTTP API, not a version bump. It is
not backward compatible with the v1 API that the existing
drivers/teldrivespeaks, so the two cannot be served by one client:
drivers/teldrive)access_tokencookieX-Api-KeyheaderparentId)meta.totalPagesnextCursor)Idempotency-Keyheader requiredPUTparts → completeconflictPolicyBecause listing, addressing, auth and upload all differ, a shared client would be
two implementations behind one set of
if v2 {}branches. This PR adds aseparate package instead and does not touch
drivers/teldrive— existing v1storages keep working untouched.
User-visible behaviour
session and only sends the parts that are missing.
auto-renewed before expiry.
Implementation notes
pkg/utils/hash/tdhash.goregisters TelDrive's BLAKE3 tree hash(
blake3_tree) with the existing hash registry, following thegcidprecedent. Verified byte-for-byte against a live TelDrive server and against
rclone's
backend/teldrive/tdhash.base.RestyClienthas a 30stimeout and 3 built-in retries, both wrong for a part upload — the server only
responds once the part has been relayed to Telegram, and an automatic retry
would replay a body behind the driver's own retry policy.
here: instrumentation showed the request body drains in ~0.3s of a ~26s
request, so the remaining time is the server relaying to Telegram.
PreferProxy: true— without a share token the download URL is authenticatedby a header, which a browser following a 302 cannot send.
Dependencies
go.modchanges by one line.github.com/zeebo/blake3 v0.2.4was already inthe tree, pulled in by
rclone/rclone v1.75.0, and is only moving out of the// indirectblock becausepkg/utils/hash/tdhash.gonow imports it directly —this is what
go mod tidyproduces.go.sumis unchanged, which is theproof that no new module enters the build.
Related repository PRs / 关联仓库 PR:
Related Issues / 关联 Issue
Relates to #2034 (the v1 driver).
Testing / 测试
Tested against a real TelDrive v2 server (
ghcr.io/tgdrive/teldrive:v2, commite3142b5) backed by a real Telegram account, driven through the OpenList web UI.Exercised end to end: list, mkdir, rename, move, copy, remove (trash and hard
delete), download through the proxy, download via 302 share link, multi-part
upload, upload resume after an interrupted transfer, and conflict handling.
Hash agreement checked three ways — this driver, the TelDrive server, and
rclone 1.75.1's
teldrivehash — on the same 36 MiB file, whole-file andper-part. The vectors are pinned in
pkg/utils/hash/tdhash_test.go.go test ./...One note on
go test ./...: it is currently red onmainas well, before thischange. Go 1.27's vet runs a stricter
printfcheck, and ten pre-existingpackages (
drivers/123,drivers/189,drivers/189pc,drivers/chaoxing,drivers/google_drive,drivers/google_photo,drivers/lanzou, and threeunder
internal/offline_download/) fail it with "non-constant format string".I reproduced this on a clean
upstream/maincheckout — it is unrelated to thisPR, and none of the packages this PR adds or touches are affected. Happy to open
a separate PR for those if that would be useful.
Unit tests cover the pure logic that does not need a server: path normalisation,
chunk-size rounding, share-expiry parsing, retry classification, idempotency-key
stability, error formatting, and the BLAKE3 tree hash vectors.
Checklist / 检查清单
gofmt,go fmt, orprettierwhere applicable.AI Disclosure / AI 使用声明
Tools used / 使用工具:
Usage scope / 使用范围:
Code generation / 代码生成
Refactoring / 重构
Documentation / 文档
Tests / 测试
Translation / 翻译
Review assistance / 审查辅助
I have reviewed and validated all AI-assisted content included in this PR.
I have ensured that all AI-assisted commits include
Co-Authored-Byattribution.I can reproduce all AI-assisted content included in this PR without any AI tools.
I used Claude Code as an assistant while writing this driver: mapping the v2
OpenAPI surface onto OpenList's driver interfaces, drafting and refactoring the
implementation, and writing the tests. Every design decision - separate package
over a version flag, part-level progress, the share-token cache, which optional
interfaces to implement - was mine, and I validated the result against a live
TelDrive v2 server and a real Telegram account rather than against the spec
alone. The commit carries a
Co-Authored-Bytrailer accordingly.