При использовании S3 или Cloudflare R2 для загрузки файлов вместе с пользовательским URL-адресом CDN, загрузка пользовательских эмодзи не учитывает конфигурацию 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 (CDN helper) игнорируется, фронтенд получает сырой URL-адрес хранилища. В зависимости от строгости политик доступа к хранилищу это приводит к неработающим изображениям или вынуждает Discourse проксировать эмодзи через внутренний avatar_proxy.
Решение
Я создал PR, который оборачивает присваивание URL-адресов в Discourse.store.cdn_url(), что позволяет загрузчику пользовательских эмодзи работать так же, как и стандартные изображения в постах и аватары.
Временное решение
Пока ожидается рассмотрение и слияние PR, я создал легковесный компонент темы, который заменяет сырой URL-адрес хранилища на правильный URL-адрес CDN непосредственно перед рендерингом пользовательского эмодзи в DOM (работает как для постов, так и для чата).
Если на вашем сайте наблюдается эта ошибка, вы можете установить этот компонент и настроить строки S3 в настройках администратора темы, чтобы исправить неработающие пользовательские эмодзи:
Примечание: если вы уже загрузили пользовательские эмодзи, которые сейчас не отображаются, выполнение команды discourse remap "//my-raw-bucket-url.com" "https://my-cdn.com" в контейнере исправит старые записи в базе данных, а компонент темы исправит все новые загруженные эмодзи до тех пор, пока исправление не будет включено в ядро Discourse.
Да, возможно, это проблема, специфичная только для Cloudflare R2. У меня на инстансе они ломаются. Кажется, ломаются только те, которые были загружены недавно.
Без исправления компонента темы мне приходится каждый раз запускать ремэппинг при загрузке нового. Возможно, PR требует доработки — я не очень хорошо разбираюсь в коде эмодзи.
Discourse использует двухуровневую конфигурацию CDN: один для статических файлов (assets), а другой — для проксирования самого приложения.
Стандартные эмодзи обслуживаются через один CDN, пользовательские — через другой, но в правильно настроенной конфигурации с двумя CDN оба уровня защищены.
Я уже подробно рассматривал этот вопрос в первой связанной теме здесь:
У меня они действительно были настроены, но, чёрт возьми, я допустил опечатку в конфигурации Cloudflare для DNS-записи, специфичной для CDN, на которую указывает DISCOURSE_CDN_URL, и никогда не проверял это при настройке, потому что мой сайт работал какая же это была каша.
По крайней мере, я узнал больше о создании кодов эмодзи, создав тот бессмысленный PR…
Именно так я учусь больше всего — углубляясь в кроличьи норы, которые никогда не приведут к чему-то конкретному. Но знания, которые я получаю от этого, бесценны!
Вау, какое приключение! Я в основном перенастроил всё своё хранилище объектов Cloudflare R2 и экземпляр Discourse, и, думаю, этот баг действительно относится к R2. Когда я исправил запись DNS Cloudflare и пересобрал экземпляр так, чтобы DISCOURSE_CDN_URL действительно указывал на него, это сломало кучу других вещей, таких как строки перевода, и вызвало множество ошибок в консоли, включая некоторые ошибки CORS. Это увело меня сегодня по многим кроличьим норам. Так что, похоже, использование DISCOURSE_CDN_URL несовместимо с Cloudflare R2. (это было очень странно — когда я изначально настраивал свою исходную запись DNS, я неверно ввёл запись DNS cdn.mysite.com так, что она резолвилась как cdn.mysite.com.mysite.com). Правильная настройка DISCOURSE_CDN_URL кажется несовместимой с хранилищем объектов Cloudflare R2. Возможно, здесь есть что-то ещё, что я не до конца понимаю.
В любом случае, когда я пересобрал с веткой моего PR, всё исправилось, потому что оно оборачивает присваивание в Discourse.store.cdn_url(), чтобы загрузки пользовательских эмодзи подчинялись той же логике маршрутизации и резервного копирования S3 CDN, что и стандартные загрузки изображений в постах.
Я повторно открыл PR и отредактировал описание. Но, думаю, если команда Discourse решит не объединить его, это нормально, потому что компонент темы исправляет проблему на клиентском уровне. Обратите внимание, что исправление в PR должно влиять только на конфигурации хранилища объектов R2 для пользовательских эмодзи, а не на другие обычные S3-совместимые, такие как AWS.
Этот PR был принят, и теперь пользовательские эмодзи работают корректно при использовании хранилища объектов Cloudflare R2 для загрузки файлов. Я предложил обновление для соответствующей документации по R2 здесь: Configure an S3 compatible object storage provider for uploads