从 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 修复被合并到核心代码中。

3 个赞

测试默认表情

:smiley:

测试自定义表情

:falco:

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

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

1 个赞

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

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

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

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

2 个赞

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

编辑:

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

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