从 S3/R2 加载的自定义表情符号绕过 CDN 路由

概述

当使用 S3 或 Cloudflare R2 进行上传并配合自定义 CDN URL 时,自定义表情符号的上传不会遵循 CDN 配置,而是尝试直接从原始存储桶 URL 加载。

问题描述

当管理员上传自定义表情符号时,上传程序会创建一条 upload 记录,并将原始存储桶 URL 保存到数据库中(例如 //my-bucket.s3.amazonaws.com/...//my-bucket.r2.cloudflarestorage.com/...)——这是 Discourse 的标准行为。

然而,当 app/models/emoji.rb/site.json 生成表情符号缓存时,它会将 upload.url 直接传递给 emoji 对象:

e.url = emoji.upload&.url

由于跳过了 CDN 辅助方法,前端接收到了原始存储桶 URL。因此,根据存储桶访问策略的严格程度,这会导致图片无法显示,或者迫使 Discourse 通过内部的 avatar_proxy 路由表情符号。

解决方案

我提交了一个 PR,将 URL 赋值操作包裹在 Discourse.store.cdn_url() 中,这使得自定义表情符号加载器的行为与标准帖子图片和头像的路由方式保持一致。

临时修复方案

在等待该 PR 被审核和合并期间,我创建了一个轻量级的主题组件,它在自定义表情符号渲染到 DOM 之前,将原始存储桶 URL 替换为正确的 CDN URL(适用于帖子和聊天)。

如果你的站点正在经历此问题,你可以安装此组件,并在主题管理设置中配置你的 S3 字符串,以修复任何损坏的自定义表情符号:

注意: 如果你已经上传了目前损坏的自定义表情符号,在容器中运行 discourse remap "//my-raw-bucket-url.com" "https://my-cdn.com" 将修复数据库中旧的记录,而主题组件将修复任何新上传的表情符号,直到 PR 修复被合并到核心代码中。

5 个赞

测试默认表情

:smiley:

测试自定义表情

:falco:

2 个赞

嗯,也许这只是 Cloudflare R2 特有的问题。在我的实例上确实出现了故障。而且似乎只有新上传的内容才会出问题。

如果没有主题组件的修复,每次上传新内容时我都得运行 remap。这个 PR 可能还需要再完善一下——我对 emoji 代码不太熟。

2 个赞

Discourse 采用双 CDN 架构:一个用于静态资源,另一个用于代理应用。

标准表情使用一个 CDN,自定义表情使用另一个 CDN,但在配置正确且双 CDN 架构正常运行的网站上,两者都受到 CDN 的保护。

我在之前的相关主题中已经讨论过这一点:

您的站点是否已配置并正常运行这两个 CDN?

4 个赞

哦,我得去查看一下——我之前不知道这一点。谢谢!

编辑:

我编写了 Cloudflare R2 的部分内容,所以假设我的设置是正确的?我可能忽略了什么?

2 个赞

1 个赞

在此确认一下,我在最近的更改之后安装并测试了 PR 分支,它修复了该问题。

1 个赞

这是我编写的维基指南中的这一部分:

您是否同时设置了这两个变量?

这是将 Meta 从 AWS 迁移回 Metal 时产生的一个遗留问题,缺少一个新的 cook,刚刚重新构建了 HTML 以修复它。

您的测试站点是否同时设置了这两个 CDN 环境变量?

3 个赞

我确实设置了,但天哪,我在 Cloudflare 配置中为 DISCOURSE_CDN_URL 指向的 CDN 专用 DNS 记录打错了一个字,而且我在设置时从未测试过它,因为我的站点当时能正常运行 :joy: 真是乱成一团。

至少我通过那个毫无意义的 PR 学到了更多关于表情符号代码的知识…
:woman_facepalming:t2:

谢谢 Falco!

3 个赞

哈哈,没关系!

我学到的最多知识,往往都来自那些追根究底却最终一无所获的“兔子洞”。但从中获得的知识可是无价之宝!

3 个赞

哇,这趟折腾可真是够呛。我基本上重新配置了我的整个 Cloudflare R2 对象存储和 Discourse 实例,而且我觉得这个 Bug 确实跟 R2 有关。当我修复了 Cloudflare DNS 记录并重建实例,使 DISCOURSE_CDN_URL 真正指向它时,结果搞坏了一堆其他东西,比如翻译字符串,还在控制台里抛出了多个错误,包括一些 CORS 错误。这让我今天钻了好多死胡同。所以看来使用 DISCOURSE_CDN_URL 似乎与 Cloudflare R2 不兼容。(这真的很奇怪——当我最初设置原始 DNS 条目时,我错误地输入了 cdn.mysite.com 的 DNS 记录,导致它解析成了 cdn.mysite.com.mysite.com)。正确设置 DISCOURSE_CDN_URL 似乎与 Cloudflare R2 对象存储不兼容。这里可能还有一些我没完全搞懂的地方。

总之,当我用我的 PR 分支重新构建时,一切都修好了,因为它将赋值包裹在 Discourse.store.cdn_url() 中,使得自定义表情包的上传遵循与标准帖子图片上传相同的 S3 CDN 路由和回退逻辑。

我重新打开了 PR 并编辑了描述。不过我想如果 Discourse 团队选择不合并它,那也没关系,因为主题组件在客户端层面修复了这个问题。请注意,PR 中的修复应仅影响 R2 对象存储配置中的自定义表情包,而不影响其他常规的 S3 兼容服务,如 AWS。

2 个赞

该 PR 已被合并,现在在使用 Cloudflare R2 对象存储进行上传时,自定义表情符号问题已修复。我在此处发布了对相关 R2 文档的更新建议:Configure an S3 compatible object storage provider for uploads

3 个赞

问题已解决,如果再次出现此问题,请提交新的报告