Skip to content

feat(drivers): add Cloudflare R2 driver (Workers binding + S3 API) - #131

Merged
PIKACHUIM merged 5 commits into
mainfrom
feat/r2-storage
Oct 16, 2026
Merged

PIKACHUIM merged 5 commits into
mainfrom
feat/r2-storage

Conversation

@PIKACHUIM

Copy link
Copy Markdown
Member

feat(drivers): add Cloudflare R2 driver (Workers binding + S3 API)

Base: main ← Compare: feat/r2-storage
Commit: 2e4e74a · Files changed: 8 (+1447 / −2)
Open PR: https://github.com/OpenListTeam/OpenList-Worker/pull/new/feat/r2-storage

Summary / 摘要

Adds a dedicated Cloudflare R2 storage driver with two access paths,
binding-first with automatic fallback:

  1. Workers R2 bucket binding — no API token, no secrets. The bucket is
    resolved from the request env (r2_buckets in wrangler.jsonc), the storage's
    bucket_binding field names which binding to use.
  2. S3-compatible API — account_id (auto-derives the endpoint) or an explicit
    endpoint, plus access key / secret. Reuses the existing S3Driver for signing,
    listing and presigned links.

新增独立的 Cloudflare R2 存储驱动,两种接入方式,binding 优先、自动回退:

  1. Workers R2 bucket binding —— 无需 API Token / 密钥。绑定对象取自请求
    env(wrangler.jsonc 的 r2_buckets),存储配置里的 bucket_binding 字段
    指定用哪个绑定。
  2. S3 兼容 API —— 填 account_id(自动推导 endpoint)或显式 endpoint,
    加 access key / secret;签名、列举与预签名直链复用现有 S3Driver。

Why / 为什么:R2 在 Workers 上的首选用法是绑定直连(零密钥、免出网、可跨账号
共享桶),而本项目此前只能靠「S3 驱动 + 手填 r2 endpoint」曲线使用;Go 版
OpenList 有独立 r2 驱动,本 PR 在 TS 版补齐并扩展出 binding 能力。

用户可感知的行为变化

  • 后台「存储 → 新增」多出 R2 驱动(默认挂载点 /r2)。
  • 生产环境可只填一个绑定名就接入 R2:先在 wrangler.jsonc 声明
    "r2_buckets": [{ "binding": "R2", "bucket_name": "your-bucket" }],
    再把存储的 bucket_binding 填 R2。不再需要创建 R2 API Token。
  • 需要浏览器直连(302)而不是经 Worker 转发时,填 custom_host
    (r2.dev 公开域名或自定义域名)即可。
  • 历史配置完全不受影响:此前用「S3 驱动 + r2 endpoint + 密钥」创建的存储
    仍然照原样工作(见下方「兼容性」)。

重要实现变化

  • 新增 src/backend/drivers/r2/(driver.ts 719 行 / types.ts 106 行 /
    r2.test.ts 455 行)。
  • op/storage.ts:r2 / cloudflarer2 / cfr2 / r2native 路由到 R2Driver;
    紧跟新增驱动分支之后保留原有 S3 分支(s3|doge|minio|ceph|aws|b2|cos|oss|kodo)。
  • internal/model/db.ts:新增 getEnvCtx(),把请求级 env 上下文暴露给驱动层
    (原先只有 setEnvCtx,仅内部使用)。
  • server/admin.ts:新增 R2 表单(16 个字段);normalizeDriver 让 r2 /
    cloudflarer2 / cfr2 归一到新的 R2 驱动(此前是 S3)。
  • wrangler.jsonc:新增「方案 E:R2 对象存储」注释块,含绑定声明示例与
    binding vs api 的行为差异说明。
  • README 驱动清单补 R2。

配置 / 存储 / API 变化

  • 新增可选配置:wrangler.jsonc 的 r2_buckets(不配也能用 api 模式)。

  • 无破坏性变更:不涉及已有存储的 addition 格式、不涉及数据库/缓存格式、
    不涉及对外 API 与 WebDAV/S3 网关行为。

  • 唯一的行为差异在 normalizeDriver:新建存储时选 r2 别名现在会落到新
    R2 驱动,而不是旧 S3 驱动(详见「兼容性」)。

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

  • This PR changes public API, config, storage format, or migration behavior.
    / 此 PR 修改了公开 API、配置、存储格式或迁移行为。

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

Related repository PRs / 关联仓库 PR:

  • OpenList:
  • OpenList-Docs:

Related Issues / 关联 Issue

无直接 issue(新功能)。与 Go 版 drivers/r2 对齐,并额外支持 Workers 绑定直连。

Design / 设计说明

两条路径如何选择

条件 走哪条 下载链路
能解析到 bucket_binding(或 mode=binding) binding:R2Bucket 直连 无 custom_host → 服务端流式转发;有 → 公开直链 302
解析不到绑定且 mode=auto api:复用 S3Driver 预签名直链 302
mode=binding 但解析不到绑定 报错(不静默回退) —

auto 是默认值:配了绑定就用绑定,没配就按 api 配置校验(缺 bucket /
endpoint / 密钥时给出「缺哪一项、去哪里拿」的中文指引)。

绑定名未填写时按 R2 → R2_BUCKET → CF_R2 → OPENLIST_R2 顺序探测,
env、请求级上下文、globalThis 三处分别查找(部分平台把绑定注入为全局标识符)。

关键取舍一:为什么 binding 模式的下载是「服务端流式转发」

Cloudflare 的 Workers 绑定 API 不提供预签名 URL:R2Bucket 的方法集是
head / get / put / delete / list / createMultipartUpload /
resumeMultipartUpload,预签名 URL 属于 S3 接口能力(R2 文档中
R2 → S3 → Presigned URLs 单列一节)。

因此本驱动的直链策略是:

  1. 配了 custom_host → 拼公开直链(r2.dev / 自定义域名),浏览器直连;
  2. api 模式 → S3Driver 预签名直链;
  3. binding 模式且无 custom_host → raw_url 返回空串,由 server/raw.ts
    既有的 createReadStream 分支流式回传(支持 Range)。

顺带覆盖了一个协议差异:server/raw.ts 传下来的 Range 是 S3 语义的
{ start, end }(闭区间),而 R2 的 range 是 { offset, length },
驱动内做了换算。

关键取舍二:绑定在「每次操作」时解析,不缓存在驱动实例上

驱动实例是跨请求缓存的(cacheKey = ${storageConfig.id}_${storageConfig.modified})。
若把绑定对象或某个请求的 env 存进实例,会让绑定随 isolate 生命周期滞留
(以及在多账号/多 Worker 场景下指向错误的绑定)。因此驱动每次 list / get /
put 都从当前请求的上下文重新解析:getEnvCtx()(index.ts 中间件已按
请求写入)→ globalThis。这也让「存储保存流程拿不到 env」的情形自然降级而不
污染缓存。

关键取舍三:init() 只告警、不抛错

驱动实例可能在尚未注入请求 env 的时机被创建(后台 storage/load_all、
保存存储时)。若此时因解析不到绑定而抛错,存储会被标成 error 甚至禁用。
因此 init() 仅打印带修复指引的告警,真正的失败发生在 list / get / put。

目录语义

与 S3 驱动对齐:delimiter="/" 列目录 + 占位对象表示空目录
(默认 .openlist)+ root_folder_path 前缀 + 递归 copy / move /
remove(R2 的 delete 支持批量数组,按 1000/批)。

分页只依据 truncated 判断是否继续(R2 文档明确警告:设置了 include 时单页
返回条数可能少于 limit,不能拿条数推断),cursor 原样回传续查。

Compatibility / 兼容性

  • driver 字段已是 "S3" 的历史存储:不受任何影响,仍然走 S3 驱动
    (endpoint 指向 R2 的用法继续有效)。
  • driver 字段是 "r2" 的历史存储(非通过后台表单创建、手写导入的配置):
    现在会命中新的 R2 驱动。两者目录语义一致;若该存储原先只填了
    endpoint + key + secret,api 模式可直接工作(region 默认 auto)。
    如需强制回到旧行为,改填 driver: "S3" 即可。
  • 其余:无数据库格式 / 缓存格式 / API 变化,无需迁移。

Testing / 测试

  • go test ./... — 本仓库为 TypeScript,不适用。
  • Manual test / 手动测试: 见下方「供审查者手动验证」。

已执行的自动化检查 / Automated checks(分支 feat/r2-storage @ 2e4e74a):

Command / 命令 Result / 结果
npx tsc -p tsconfig.json --noEmit 0 errors
npx tsx --test "src/backend/drivers/**/*.test.ts" 160/160 pass
npx tsx --test "src/backend/server/*.test.ts" 197/197 pass
npx tsx --test "src/backend/internal/model/*.test.ts" 39/39 pass
npx tsx --test src/backend/internal/model/store/store.test.ts 12/12 pass
npx tsx --test "src/backend/internal/cache/*.test.ts" 24/24 pass
npx tsx --test "src/backend/pkg/*.test.ts" 42/42 pass
npx tsx --test src/backend/drivers/189/*.test.ts 20/20 pass
npx tsx scripts/_regress.mjs ALL PASS
npx prettier --check "src/backend/drivers/r2/*.ts" All matched files use Prettier code style

New tests / 新增测试(9 例,drivers/r2/r2.test.ts,内存版 R2Bucket mock):

  1. normalizeR2Addition 默认值与 root_folder_path 归一化;
  2. 绑定解析三处探测(env 参数 / 请求级上下文 / globalThis),且同名变量
    只是普通字符串时必须判为不可用
    ;
  3. 列目录:目录 + 文件、占位对象过滤、pageSize=1 时跟随 cursor 翻页、
    深层对象不泄漏到当前层;
  4. root_folder_path 前缀作用于 list / get / put 的桶内 key;
  5. mkdir 写占位对象;remove 区分文件(单 key)与目录(递归);
  6. rename / move / copy:文件 copy+delete、目录递归(复制不删源);
  7. createReadStream:无 Range 全量、有 Range 按 offset/length 切片;
  8. 绑定缺失时 mode=auto 回退 api(并断言 endpoint 由 account_id 推导)、
    mode=binding 明确报错、无任何配置时给出「缺少 bucket」指引;
  9. getDriver 路由:R2 / r2 / CloudflareR2 / cfr2 均命中 R2Driver。

供审查者手动验证 / Manual verification steps:

  1. 部署后确认绑定生效(binding 模式):
    wrangler.jsonc 声明 r2_buckets → 后台新建 R2 存储并填 bucket_binding
    → 浏览该挂载点,应能列出桶内对象且未配置任何 API Token。
  2. 下载验证:点开一个视频,应走 /d... 服务端流式(Response 带
    Accept-Ranges: bytes),拖拽进度条可正常 seek。
  3. 写操作验证:新建目录(桶内出现 .openlist 占位对象)、上传、重命名、
    删除目录(对象全部移除)。
  4. 回退验证:清空 bucket_binding、填入 R2 API Token 三项,确认改用
    预签名直链 302。

未做真机/线上 R2 验证:本机无 R2 桶与 Workers 部署环境,因此 binding 侧的正确性
由内存 mock 与 tsc 保证,真实桶上的行为需由审查者按上面 4 步确认。这是本 PR
唯一未经真实环境验证的部分。

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 gofmt, go fmt, or prettier where applicable.
    / 我已按适用情况使用 gofmt、go fmt 或 prettier 格式化变更代码。
  • I have requested review from relevant maintainers or code owners where applicable.
    / 我已在适用情况下请求相关维护者或代码所有者审查。

说明:仅对新增的 drivers/r2/* 执行了 prettier(该目录 100% 合规)。
对既有的 storage.ts / admin.ts / db.ts 刻意未整体格式化:仓库现有代码与
prettier 默认配置(printWidth: 80)不一致,全文件格式化会产生上百行与本 PR 无关的
重排,故只做最小化功能改动,保持 diff 可审。

AI Disclosure / AI 使用声明

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

Tools used / 使用工具:

  • ChatGPT
  • Codex
  • GitHub Copilot
  • Claude
  • Gemini
  • Other (please specify) / 其他(请注明): CodeBuddy (AI coding assistant)

Usage scope / 使用范围:

  • Code generation / 代码生成

  • Refactoring / 重构

  • Documentation / 文档

  • Tests / 测试

  • Translation / 翻译

  • Review assistance / 审查辅助(用 R2 官方文档核对绑定 API 能力边界)

  • 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 归属信息。

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

Operational Note / 运维提示

  • binding 模式的带宽成本模型变了:下载/播放由 Worker 转发字节流
    (R2 出网到 Worker 免费,但 Worker 需要处理请求)。大文件并发播放压力会比
    api 模式(浏览器直连预签名 URL)高。生产上如果已有公开域名,建议填
    custom_host 让流量回到直连 302 路径。
  • r2.dev 公开域名有速率限制:适合测试,正式对外建议绑自定义域名。
  • 直传(get_direct_upload_info)仅在 api 模式可用:binding 不提供 PUT
    预签名,调用时会得到明确报错而不是静默失败。

Scope / Follow-ups / 范围与后续

  • In scope / 本 PR 范围: R2 驱动(binding + api)、注册/表单/文档/测试。
  • Not in scope / 不在本 PR 范围:
    • 分片上传(createMultipartUpload)与 s3_presigned 直传工具对 binding
      模式的适配;
    • env_check / 存储「测试连接」按钮对 R2 的专门体检项(目前沿用
      check_status 的通用列表探测);
    • OpenList-ReactWeb 预览组件若要复用 R2 直链,需由前端侧确认
      raw_url 与 base_path 的拼接(与本 PR 无关);
    • 其他云厂商的对象存储(B2/OSS 等)的 binding 化——本 PR 的结构可以复用,
      但各家绑定 API 差异需要分别对接。

Commits / 提交

  • 2e4e74a — feat(drivers): add Cloudflare R2 driver (Workers binding + S3 API)
    (8 files changed, +1447 / −2)

新增独立 R2 驱动,同时支持两种接入方式(binding 优先,自动回退):

- Workers R2 bucket binding 直连:无需 API Token,绑定名可在存储配置指定(bucket_binding),未指定时按 R2/R2_BUCKET/CF_R2/OPENLIST_R2 顺序探测;驱动实例跨请求复用,绑定在每次操作时从请求级 env 上下文(getEnvCtx)重新解析,避免绑定对象随 isolate 滞留。

- S3 兼容 API:account_id 自动推导 endpoint(https://<account>.r2.cloudflarestorage.com),或显式 endpoint;复用 S3Driver 的签名/列表/预签名实现,R2 固定 region=auto。

- 目录语义与 S3 对齐(delimiter 列目录、占位对象、root_folder_path 前缀、递归 copy/move/remove、批量删除)。

- 下载链路:binding 模式返回空 raw_url 触发服务端流式下载(createReadStream,Range 换算 R2 的 offset/length);配 custom_host(r2.dev/自定义域名)时改走公开直链。binding 不提供预签名直链(Workers 绑定无该能力),直传仅在 api 模式提供并给出明确指引。

- 注册与配置:storage.ts 路由 r2/cloudflarer2/cfr2/r2native;admin driverConfigs 新增 R2 表单并让 r2 别名指向新驱动(历史 driver=S3 的 R2 配置不受影响);wrangler.jsonc 补 r2_buckets 绑定说明;README 驱动清单更新。

- 测试:9 个用例覆盖绑定解析、cursor 翻页、root 前缀、mkdir/remove/rename/move/copy、Range 流式读取、api 回退与路由别名(内存版 R2Bucket mock)。
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Oct 16, 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 d0123f1 Oct 16 2026, 12:17 PM

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Oct 16, 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-tsworkers d0123f1 Oct 16 2026, 12:17 PM

pikachuren added 2 commits October 16, 2026 18:10
主 README 的驱动清单新增了 Cloudflare R2,本次把同一句同步到 readmes/ 下全部 12 个语言版本(ar/de/en/es/fr/hi/it/ja/ko/pt/ru/zh-TW),并按各文件既有的标点惯例处理(中日韩印用全角括号与「、」,其余用半角括号与逗号,阿拉伯语用「،」)。
…facts

- README 与 12 个语言版本:把安装向导、JWT_SECRET、DB_* / CACHE_* / SINGLEFLIGHT 等大段配置说明压缩为表格 + doc.oplist.org 链接,正文只保留默认可用路径与建议显式配置的项

- 顺带修正过时描述:前端为 SolidJS + Hope UI(原写 React 19 + Ant Design)、运行平台补阿里云 ESA、数据存储口径改为 D1/KV/Blob/DO 与外部 MySQL

- .env.example / .dev.vars.example:缓存配置说明由「详见 README」改为指向文档站

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

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

🎯 结论

✅ Approve — 设计精巧、实现完整、测试到位,CI 全绿,无阻塞问题

📖 概要

feat(drivers): add Cloudflare R2 driver (Workers binding + S3 API)
核心改动:新增独立 R2 驱动(binding 优先 + api 回退)+ admin 表单 + 测试 + 文档

🧭 整体方案

Why:R2 在 Workers 上的首选用法是 binding 直连(零密钥、免出网、可跨账号共享桶),本项目此前只能靠"S3 驱动 + 手填 r2 endpoint"曲线使用;Go 版有独立 r2 驱动但无 binding,本 PR 在 TS 版补齐并扩展。

How:新增 drivers/r2/(driver.ts 719 行 + types.ts 106 行 + test 455 行),两条路径:

  1. binding — wrangler.jsonc 声明 r2_buckets → 存储配置填 bucket_binding → 驱动层三处探测(env 参数 / getEnvCtx() / globalThis)并校验方法形态(list/get/put/head/delete);下载:配了 custom_host 返回公开直链 302,否则 raw_url 为空串触发 raw.ts 的 createReadStream 流式转发(R2 binding 不提供预签名 URL),Range 从 S3 语义 {start,end} 转换为 R2 语义 {offset,length}
  2. api — account_id 推导 endpoint 或显式填 endpoint + key/secret,整体委托 S3Driver(签名 / 列举 / 预签名直链)

Mode 选择:mode=auto(默认)→ 能解析到 binding 就用 binding、解析不到且有 api 配置就用 api、都没有时给出"缺 bucket/endpoint/密钥"指引;mode=binding 明确要求 binding(解析不到直接报错,不静默回退)

设计亮点:

  • 每次操作解析绑定,不缓存在驱动实例(实例跨请求缓存,缓存 env / 绑定对象会滞留 isolate 生命周期 + 多账号场景绑定错误)
  • 三处探测 + 方法形态校验(同名变量是普通字符串时判不可用)
  • init() 只告警不抛错(后台 load_all / 保存存储时尚未注入请求 env,抛错会禁用存储)
  • 翻页只看 truncated(R2 文档明确警告:设置了 include 时单页条数可能少于 limit,不能用返回值推断)
  • 目录语义与 S3 对齐(占位对象 .openlist + root_folder_path + 递归 copy/move/remove,R2 delete 支持批量数组 1000/批)

方案合理性:✅ binding 优先 + api 回退是 CF Workers 最佳实践(绑定免密钥免出网);raw_url 为空串触发流式转发是唯一可行方案(binding 不提供预签名 URL);每次解析绑定避免了 isolate 滞留与多账号混淆;Range 转换正确(S3 {start,end} 闭区间 → R2 {offset,length});测试覆盖核心分支(9 个用例 + 内存 R2Bucket mock)

📊 变更统计

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

🚨 关键问题

无重大问题(P0/P1 均无)

P2(可选优化):

  • 💡 driver.ts:619-643 createReadStream 对 api 模式抛错"请使用预签名直链",但 api 模式下 raw_url 已返回预签名直链(S3Driver),raw.ts 不会调到这里 —— 此分支实际不可达,可以 throw new Error("unreachable: api mode returns presigned raw_url") 标明
  • 💡 driver.ts:147 bindingError 内建的修复指引可补充"或切换到 api 模式(填 account_id + key/secret)"
  • 💡 PR 描述提到"未做真机/线上 R2 验证",建议合并前由维护者在有 R2 桶的 Workers 环境执行手动验证步骤(后台新建 R2 存储 + binding 模式列表/下载/写操作 + api 模式预签名直链)

📂 逐文件分析

drivers/r2/driver.ts(719 行)

改动意图:binding + api 双模式 R2 驱动,binding 优先、api 回退,下载策略区分 custom_host 直链 / api 预签名 / binding 流式转发
核心逻辑:

  • L80-100:resolveR2Bucket 三处探测(env 参数 / getEnvCtx() / globalThis)+ 方法形态校验(list/get/put/head/delete 函数)
  • L140-160:resolveBucket() 每次操作重新解析(不缓存在驱动实例)
  • L256-304:listPrefix 翻页只看 truncated(不用返回值条数推断),cursor 原样回传,delimitedPrefixes 提取子目录
  • L340-350:objectRawUrl 返回 custom_host 直链或空串(空串触发 raw.ts createReadStream)
  • L618-643:createReadStream Range 转换(S3 {start,end} 闭区间 → R2 {offset,length})
    设计亮点:✅ 每次解析绑定避免 isolate 滞留;三处探测覆盖 CF Workers(env)+ 其他平台(globalThis);init() 只告警不抛错(保存流程尚无 env 时不禁用存储);翻页策略严格按文档(truncated + cursor 推进失败终止)

drivers/r2/r2.test.ts(455 行)

改动意图:9 组用例覆盖 binding 解析、mode 选择、目录语义、Range、回退
测试覆盖:

  1. normalizeR2Addition 默认值与 root_folder_path 归一化
  2. binding 三处探测 + 同名字符串变量判不可用
  3. 列目录(目录+文件、占位对象过滤、pageSize=1 翻页、深层不泄漏)
  4. root_folder_path 前缀作用于 list/get/put
  5. mkdir 占位对象 + remove 区分文件/目录
  6. rename/move/copy(文件 copy+delete、目录递归)
  7. createReadStream 无 Range 全量 + 有 Range 切片
  8. 绑定缺失时 mode=auto 回退 api(断言 endpoint 由 account_id 推导)+ mode=binding 报错 + 无配置时指引
  9. getDriver 路由(R2/r2/CloudflareR2/cfr2 均命中)
    实现质量:✅ 内存 R2Bucket mock 模拟完整(list 翻页 + cursor、get Range、批量 delete),断言精确(cursor 推进、Range offset/length、endpoint 推导)

internal/model/db.ts(+6 行)

改动意图:新增 getEnvCtx() 暴露请求级上下文给驱动层
实现质量:✅ 简单导出(export function getEnvCtx() { return envCtx }),配套 setEnvCtx 已存在,无副作用

internal/op/storage.ts(+9 行)

改动意图:路由 r2/cloudflarer2/cfr2/r2native 到 R2Driver,保留原有 S3 分支
实现质量:✅ 紧跟新增分支后保留 s3|doge|minio|ceph|aws|b2|cos|oss|kodo 分支(历史 S3 存储不受影响)

server/admin.ts(+153 行)

改动意图:新增 R2 表单(16 字段)+ normalizeDriver 让 r2/cloudflarer2/cfr2 归一到 R2 驱动
实现质量:✅ 字段完整(binding + api 双模式);normalizeDriver 逻辑清晰(历史 driver: "S3" 不变,新建 r2 归到 R2)

wrangler.jsonc / README / readmes/*(文档)

改动意图:补「方案 E:R2 对象存储」注释块 + 驱动清单
实现质量:✅ 绑定示例清晰(r2_buckets 声明 + binding vs api 行为差异说明)

✅ 待处理清单

  • [P2] driver.ts:619 createReadStream api 模式抛错改为 unreachable 标注
  • [P2] driver.ts:147 bindingError 补充 api 模式回退指引
  • [P2] 合并前由维护者在有 R2 桶的 Workers 环境执行手动验证

🎯 结论:✅ Approve — 设计精巧(binding 优先 + 每次解析)、实现完整(9 测试用例 mock 覆盖)、文档到位,CI 全绿,无阻塞问题

pikachuren added 2 commits October 16, 2026 20:15
…sed lookups

- 驱动名由 R2 改为 Cloudflare R2(与既有的 GitHub Releases 等带空格命名一致);
  normalizeDriver 把 R2 / r2 / cloudflare_r2 / cfr2 全部归一到它。
  本分支可能已存在的 driver=R2 记录会在下次保存时自动改名,运行时两种写法
  都能命中新驱动。
- 修 server/fs.ts 的直传工具判定:改为按「归一化后的驱动名」匹配(小写 + 去非
  字母数字)。原实现拿原始字符串做 ^ 锚定正则,驱动名带空格后会漏判,导致
  api 模式下 s3_presigned 直传入口消失。
- R2 的直传工具只在 api 模式宣告:binding 无预签名能力,新增
  r2CanSignDirectUpload()(判定与 R2Driver.resolveMode 一致),
  不再向前端暴露必然报错的入口。
- 删除 op/storage.ts 的 S3 分支里已成死代码的 r2(已被上方 R2 分支接管)。
- 测试 +2:驱动名归一(含 S3 家族不被吞掉的回归护栏)、直传能力三态判定。
该文件是本机部署配置(含真实 JWT_SECRET、KV namespace id 与 R2 绑定),与 .gitignore 中已有的 wrangler.local.jsonc / wrangler.d1.jsonc 属同类「本地专用配置、可能含真实密钥、禁止入库」条目。
@PIKACHUIM
PIKACHUIM merged commit 661a0be into main Oct 16, 2026
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