Skip to content

feat(driver): add CtFile (城通网盘) storage driver - #123

Merged
PIKACHUIM merged 1 commit into
OpenListTeam:mainfrom
wumingzhinu:feat/driver-ctfile
Oct 16, 2026
Merged

PIKACHUIM merged 1 commit into
OpenListTeam:mainfrom
wumingzhinu:feat/driver-ctfile

Conversation

@wumingzhinu

Copy link
Copy Markdown
Contributor

feat(driver): add CtFile (城通网盘) storage driver

Summary / 摘要

基于城通网盘官方 OpenAPI(openapi.ctfile.com,rest.ctfile.com/v1)新增 CtFile 存储驱动,响应 OpenList#1217 的接入需求。

功能覆盖

  • 公有云 / 私有云挂载(cloud_type: public / private)
  • 目录浏览(folder/list + file/list,自动翻页 + 防死循环保护)
  • 下载直链(file/fetch_url;非 VIP 限速取决于账号等级)
  • 建目录、重命名(文件/文件夹)、删除(入回收站)、移动
  • 上传:file/upload 预签名地址 + multipart 直传,md5-大小 校验和,支持秒传命中跳过
  • Session 失效(401)自动转换为「开放接口登录密钥管理」重新生成的可操作提示
  • /task/refresh 定时巡检复用 checkCookieValid,Session 过期在管理后台标记 cookie_expired
  • 驱动别名:ctfile / ctfilepan / 400gb / 城通网盘

实现要点

  • 城通 OpenAPI 对 folder_id / file_id 是否携带 d/f 前缀的要求在官方文档与社区实现间不一致,调用层内置 raw / stripped 双形态自动回退;批量 ids 内置字符串 / 数组双形态回退
  • file/list 翻页以空页与重复首项双重终止,防服务端异常导致死循环
  • 纯 Web API 实现(fetch + FormData),无 Node 独占模块,兼容 Workers / Vercel / Node 容器

变更文件

  • 新增 src/backend/drivers/ctfile/{types,util,driver,util.test}.ts
  • internal/op/storage.ts:驱动实例化分支
  • server/admin.ts:驱动表单元数据 + normalizeDriver 别名(含中文名)
  • server/task.ts:巡检扩展 /(lanzou|ctfile|400gb)/i
  • README.md:国内网盘列表补充

已知限制

  • 城通网盘已关闭服务器端密码登录(/v1/user/auth/login 恒返回 410,网页密码登录要求扫码),驱动仅支持开放接口 Session(管理后台「开放接口登录密钥管理」生成)

  • 非 VIP 账号下载直链受官方限速(约 30KB/s),属账号侧限制

  • This PR has breaking changes. / 此 PR 包含破坏性变更。

  • This PR changes public API, config, storage format, or migration behavior. / 此 PR 修改了公开 API、配置、存储格式或迁移行为。
    (新增驱动配置项:session / cloud_type / base_url / order_by / order_direction / user_agent,均为新增,不影响既有存储)

  • This PR requires corresponding changes in related repositories. / 此 PR 需要关联仓库同步修改。

Related repository PRs / 关联仓库 PR :

  • OpenList: (可选:Go 版可参考本实现移植)
  • OpenList-Docs:

Related Issues / 关联 Issue

Relates to OpenListTeam/OpenList#1217

Testing / 测试

  • tsc --strict 类型检查(ctfile 驱动目录及其依赖图全量通过)
  • tsx --test src/backend/drivers/ctfile/util.test.ts — 10/10 通过:
    鉴权缺失提示、401→Session 失效、ID 前缀回退(folder/meta、fetch_url)、
    翻页终止、防死循环、ids 形态回退、private 命名空间、md5 校验和、默认值归一化
  • 对真实 rest.ctfile.com API 冒烟:未配置 / 失效 Session 均返回可操作错误提示
  • prettier --check 格式检查通过
  • 完整账号端到端验证(列表 / 下载 / 上传 / 移动)— 需要真实城通账号 Session,提交人可在合并前用自有账号验证

Checklist / 检查清单

  • I have read CONTRIBUTING. / 我已阅读 CONTRIBUTING。
  • I confirm this contribution follows the repository license, contribution policy, and code of conduct. / 我确认此贡献符合仓库许可证、贡献规范和行为准则。
  • I have formatted the changed code with prettier. / 我已使用 prettier 格式化变更代码。
  • I have requested review from relevant maintainers or code owners where applicable. / 我已在适用情况下请求相关维护者审查。

AI Disclosure / AI 使用声明

  • This PR includes AI-assisted content. / 此 PR 包含 AI 辅助内容。

Tools used / 使用工具:

  • Other (please specify) / 其他(请注明): MiMo (OpenCode)

Usage scope / 使用范围:

  • Code generation / 代码生成

  • Tests / 测试

  • Documentation / 文档

  • I have reviewed and validated all AI-assisted content included in this PR. / 我已审核并验证此 PR 中的所有 AI 辅助内容。

  • I have ensured that all AI-assisted commits include Co-Authored-By attribution. / 我已确保所有 AI 辅助提交都包含 Co-Authored-By 归属信息。(commit 已带 Co-Authored-By: MiMo (OpenCode) <noreply@opencode.ai>)

  • I can reproduce all AI-assisted content included in this PR without any AI tools. / 我可以在没有任何 AI 工具的情况下重现此 PR 中包含的所有 AI 辅助内容。(请提交人确认后勾选)

基于城通网盘官方 OpenAPI (https://openapi.ctfile.com/, rest.ctfile.com/v1)
实现存储驱动,响应 OpenList#1217 的城通网盘接入需求:

- 支持公有云/私有云挂载(public/private 命名空间)
- 目录浏览(folder/list + file/list 自动翻页与防死循环保护)
- 下载直链(file/fetch_url;非 VIP 限速取决于账号等级)
- 建目录、重命名(文件/文件夹)、删除(入回收站)、移动
- 上传(file/upload 预签名地址 + multipart 直传,md5-size 校验和可秒传)
- Session 失效(401)转换为「开放接口登录密钥管理」重新生成的明确提示
- folder_id / file_id 前缀(d/f)在文档与社区实现间要求不一致,
  调用层内置 raw/stripped 双形态自动回退;批量 ids 内置字符串/数组双形态回退
- /task/refresh 定时巡检复用 checkCookieValid,Session 过期在后台标记
- 驱动别名:ctfile / ctfilepan / 400gb / 城通网盘

说明:城通网盘已关闭服务器端密码登录(/v1/user/auth/login 恒返回 410,
网页密码登录要求扫码验证),因此驱动仅支持开放接口 Session 方式。

验证:
- tsc --strict 全量类型检查通过(ctfile 目录及其依赖图)
- node:test 单测 10/10 通过(鉴权、ID 形态回退、翻页、秒传校验和等)
- 对 rest.ctfile.com 真实 API 冒烟:未配置/失效 Session 均返回可操作提示
- prettier 格式检查通过

Co-Authored-By: MiMo (OpenCode) <noreply@opencode.ai>
@wumingzhinu

Copy link
Copy Markdown
Contributor Author

补充:Session 获取入口实测与免费账号可用性调研

PR 描述里只写了 Session 来自「开放接口登录密钥管理」,这里补充确切入口、实测约束和一条免费账号的替代方案(均为 2026-10 实测,中国大陆移动网络出口、非代理)。

一、官方入口的确切位置

  • 页面:https://home.ctfile.com/settings/openapi
    依据:home.ctfile.com 官方前端跳转脚本中的路由映射表 openapi: '/settings/openapi';该 URL 未登录时 302 到 www.ctfile.com/login?ref=...,登录后自动跳回。
  • 操作路径:登录 → 设置 → 开放接口 → 创建开放接口登录密钥 → 刷新页面 → 密钥下方显示的字符串(32 位 hex)即 v1 API 的 session 参数值。
  • 该密钥为长期有效,区别于登录接口签发的 3 天有效期 session。

二、程序化获取 Session 的各条路径实测(均已不可用)

路径 实测结果
POST/GET /v1/user/auth/login 密码登录 恒返回 {"code":410,"message":"请升级客户端"}。已测变体:POST JSON / GET query / POST form、Mozilla 与 okhttp UA、附加 unique_id/device_id/version 字段——全部 410。
网页密码登录 /p8/public/login/login 返回 LOGIN_REQUIRE_QRSCAN(新设备/未过验证时需 App 扫码)
WebDAV 挂载 定价与权益文案中「WebDAV 批量下载」列为标准会员(¥28/月)功能
开放接口密钥页创建密钥 有反馈称要求会员(确切提示文案待确认)

补充参考:2025-08 的社区文章(博客园《城通网盘API的链接研究》)与社区 Go 实现 Icy2010/ctFile 的 Login() 均记录当时可直接 GET /v1/user/auth/login?email=&password= 取 token,现接口已对非客户端关闭——官方文档中该登录接口的说明已过时,这也是本驱动仅支持 Session 模式的直接原因。

三、免费账号的替代方案(浏览器会话复用,待更多验证)

网页端与 v1 API 同属 rest.ctfile.com 服务,前端以 credentials:"include"(cookie)鉴权(account-pages chunk 中出现两处)。已在浏览器登录的用户可尝试:

  1. F12 → Network → 刷新 /settings/openapi → 从对 rest.ctfile.com 的请求 Payload / Cookie 中复制 session;

  2. 或 Application → Cookies → rest.ctfile.com / .ctfile.com 复制候选值;

  3. 验证:

    fetch('https://rest.ctfile.com/v1/user/info/profile', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ session: '候选值' })
    }).then((r) => r.text()).then(console.log)

    返回 code:200 即可直接粘贴进本驱动的 Session 配置项。

  4. 过期后重复上述步骤;/task/refresh 巡检会在 401 时把存储标记为 cookie_expired 提醒更新。

四、免费账号已知限制(账号侧,非驱动可控)

  • 下载直链官方限速约 30KB/s(文档明示,VIP 提速);
  • 上传中(未完成)的文件不可下载;
  • 非 VIP 带宽信息接口返回 0。

如果后续确认网页端 session 不被 v1 接受、且密钥页确实锁定会员,剩余路径只有官方 App 登录签名逆向或开通会员,届时再在本 PR 同步结论。

@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.

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

🎯 结论

✅ Approve — 功能完整、设计合理、测试覆盖到位,CI 全绿,符合生产合并标准

📖 概要

feat(driver): add CtFile (城通网盘) storage driver · 关联 #1217 · 基于官方 OpenAPI (openapi.ctfile.com) 新增 CtFile 存储驱动
核心改动:新增 drivers/ctfile/{driver,util,types,util.test}.ts(1101 行),集成到 op/storage.ts、admin.ts、task.ts,补充 README 驱动列表

🧭 整体方案

基于城通网盘官方 OpenAPI v1(rest.ctfile.com/v1,统一 POST JSON + Session 鉴权),纯 Web API 实现(无 Node 独占模块,兼容 Workers / Vercel / Node),支持公有云/私有云挂载、目录浏览(自动翻页+防死循环)、下载直链、建目录/重命名/删除/移动、上传(md5-size 秒传+multipart 直传)、Session 失效自动提示(401→可操作引导)。技术路线清晰,API 封装与故障回退(ID 前缀 raw/stripped、ids 字符串/数组、翻页终止)处理完备。

📊 变更统计

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

🚨 关键问题

无重大问题(P0/P1 均不存在)

P2(可选优化):

  • 💡 util.ts:28 MAX_LIST_PAGES = 500 按 500 页 × 单页默认 100 条 = 最多支持 5 万文件,超限时静默截断 → 建议日志记录或给出"文件过多,可能未列全"提示
  • 💡 driver.ts:289 move() 声明接收 _names: string[] 但未使用(直接移整个 srcPhysical 条目),与其他驱动的批量移动语义不同 → 可在注释标注"本驱动暂不支持批量移动,仅移动单个条目"避免误用
  • 💡 admin.ts:2007 新增驱动配置中 user_agent 默认值是写死的 Chrome 142 UA,若需在现代浏览器安全策略下假装"最新版 Chrome"可能需定期更新 UA 版本号 → 可文档注明或改用生成逻辑(当前写死 142.0.0.0 可短期使用)

📂 逐文件分析

drivers/ctfile/util.ts(API 客户端核心,474 行)

改动意图:封装城通网盘 v1 API 调用层,处理 Session 鉴权、401 自动转化、网络重试、ID 形态双向回退(raw/stripped)、翻页防死循环
代码逻辑:三层架构清晰 —— post() 处理传输层(网络重试、429/5xx、401 直接抛)+ call() 处理业务层(code !== 200 抛错)+ callWithId/callWithIds() 处理 ID 形态回退(400/404 时自动换形态重试)
实现质量:✅ 无注入风险(全 JSON POST);✅ 网络故障 300ms×retry 退避;✅ 401 统一转 SESSION_EXPIRED_MSG 可操作提示;✅ listFiles() 双重终止条件(空页 + 重复首项)防死循环,MAX_LIST_PAGES 兜底保护;✅ uploadTicket() 生成 md5-size 校验和触发秒传

drivers/ctfile/driver.ts(驱动主实现,327 行)

改动意图:实现 StorageDriver 接口,封装路径解析、缓存、文件夹/文件操作(list/get/mkdir/rename/remove/move/put)
代码逻辑:resolveFolderKey() 逐级递归解析路径(名称或ID匹配,写缓存);resolveEntry() 先找文件夹再找文件;list() 合并文件夹+文件并按配置排序;get() 调用 fetchDownloadUrl 并附 UA 头;put() 先 uploadTicket 判秒传,未秒传再 upload()
实现质量:✅ init() 启动即校验 Session(await this.client.profile());✅ checkCookieValid() 对接 /task/refresh 巡检;✅ pathKeyCache 缓存减少重复解析,文件夹改名/移动后 clear() 保持一致性;✅ 上传双重判断避免不必要的大文件传输;⚠️ copy() 直接抛异常说明不支持(符合城通 API 约束)

drivers/ctfile/util.test.ts(单元测试,228 行)

改动意图:覆盖关键边界:Session 缺失提示、401 转失效提示、ID 形态回退(folder/meta、fetch_url)、翻页终止(空页+重复首项)、ids 形态回退(字符串/数组)、private 命名空间、md5 校验和、默认值归一化
实现质量:✅ 用 stubFetch 脚本化 mock,捕获请求体断言;✅ 10 个测试覆盖核心分支;✅ 按官方文档/社区实现双重验证 ID 前缀处理;✅ 防死循环场景实测(服务端忽略 start 时检测重复首项)
遗漏:无端到端测试(列表/下载/上传/移动真实账号验证)—— PR 描述已标注"提交人可在合并前用自有账号验证",合理

op/storage.ts、admin.ts、task.ts(集成点)

改动意图:注册驱动别名(ctfile / ctfilepan / 400gb / 城通网盘)、添加表单元数据(session / cloud_type / base_url / order_by / order_direction / user_agent)、扩展 /task/refresh 巡检正则(/(lanzou|ctfile|400gb)/i)
实现质量:✅ normalizeDriver() 支持中文别名(提前检测 includes("城通")),避免归一化丢失;✅ driverConfigs 字段齐全(required / help / default),新手可用;✅ task.ts 巡检扩展无侵入
兼容性:✅ 仅新增配置项,不影响既有存储

README.md

改动意图:「国内网盘」列表补充"城通网盘"
实现质量:✅ 位置合理(蓝奏云后)

types.ts(类型定义,131 行)

改动意图:TypeScript 类型映射官方 API 响应结构(CtFileProfile / CtFileFolder / CtFileFile / CtFileUploadTicket 等)
实现质量:✅ 字段覆盖完整(含可选字段,适配官方文档与社区实现差异);✅ CtFileAddition 支持 cloud_type: "public" | "private" 多实例

🔐 依赖安全审查

不适用 — 本 PR 无依赖声明变更,仅新增业务代码

✅ 补充亮点(评审中发现)

  1. ID 形态自适应回退:官方文档与社区实现对 folder_id / file_id 是否携带 d/f 前缀的要求不一致,callWithId() 内置 prefer: "raw" | "stripped" 策略,失败自动换形态重试,完美解决接口不统一问题
  2. 翻页防死循环设计:listFiles() 检测「空页」与「重复首项」双重终止,防服务端异常导致无限循环,辅以 MAX_LIST_PAGES 兜底
  3. Session 失效统一提示:401 直接转换为含"开放接口登录密钥管理"操作路径的错误,用户可按提示自助修复,体验比裸 401 好
  4. 秒传优化:uploadTicket() 计算 md5-size 校验和,put() 检测 ticket.exists > 0 直接跳过真正上传,节省带宽
  5. PR 描述与补充 comment 质量极高:完整 commit 归属(Co-Authored-By: MiMo)、已知限制明确、补充 comment 做了 Session 获取入口的完整实测调研(含免费账号替代方案),可直接作为驱动文档素材

✅ 待处理清单

  • [P2] util.ts:28 MAX_LIST_PAGES 超限时补日志或提示
  • [P2] driver.ts:289 move() 注释标注"暂不支持批量移动"
  • [P2] admin.ts:2007 user_agent 默认值版本号策略(文档注明或生成逻辑)

🎯 结论:✅ Approve — 功能完整、防御式编程到位、测试覆盖合理、注释充分、实测调研翔实,完全达到生产合并标准。3 条 P2 可选优化,不阻塞合并。

@PIKACHUIM
PIKACHUIM merged commit e40d8bc into OpenListTeam:main Oct 16, 2026
1 check 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.

3 participants