Проблема отображения URL изображений в Cloudflare R2: подробное объяснение и решение

Описание проблемы

Недавно мы заметили проблему с отображением изображений на форуме, а именно:

  • Миниатюры изображений отображаются некорректно.
  • При клике на изображения полноэкранный вариант отображается правильно.
  • Инструменты разработчика браузера показывают неправильные URL-адреса изображений.

После расследования основной причиной оказался неверный домен в URL-адресах изображений:

  • Правильный URL: https://store.starorigin.cc/optimized/1X/[imageID].jpeg
  • Неправильный URL: https://info.7a4081a2d83d3f43fe6b1be1c926fd1c.r2.cloudflarestorage.com/optimized/1X/[imageID].jpeg

Система использует неправильный домен сырого бакета R2 вместо настроенного CDN-домена.

Технический анализ

Это известная проблема в Discourse при работе с изображениями, хранящимися в Cloudflare R2. В некоторых случаях, даже при настройке s3_cdn_url, Discourse может использовать прямой URL хранилища вместо CDN-URL при генерации оптимизированных изображений (например, миниатюр).

Это может быть связано со следующими факторами:

  • Версия Discourse
  • Настройка хранилища, совместимого с S3
  • Способ хранения URL-адресов в таблице OptimizedImage

Решение

Самое простое и эффективное решение — использование компонента темы Discourse для исправления на стороне клиента. Это не требует операций с базой данных или изменения конфигурации сервера. Необходимо лишь добавить небольшой фрагмент JavaScript-кода, который автоматически заменит неправильные URL-адреса на правильные в браузере.

Код компонента темы

import { apiInitializer } from "discourse/lib/api";

export default apiInitializer("0.11.1", (api) => {
  // Исправление уже загруженных изображений
  function fixImageUrls() {
    const badDomain = "info.7a4081a2d83d3f43fe6b1be1c926fd1c.r2.cloudflarestorage.com";
    const goodDomain = "store.starorigin.cc";

    // Исправление обычных изображений
    document.querySelectorAll(`img[src*="${badDomain}"]`).forEach(img => {
      img.src = img.src.replace(badDomain, goodDomain);
    });

    // Исправление изображений с отложенной загрузкой
    document.querySelectorAll(`img[data-src*="${badDomain}"]`).forEach(img => {
      img.setAttribute('data-src', img.getAttribute('data-src').replace(badDomain, goodDomain));
    });

    // Исправление фоновых изображений
    document.querySelectorAll('[style*="background"]').forEach(el => {
      if (el.style.backgroundImage && el.style.backgroundImage.includes(badDomain)) {
        el.style.backgroundImage = el.style.backgroundImage.replace(badDomain, goodDomain);
      }
    });

    // Исправление различных других потенциальных атрибутов
    ['srcset', 'data-large-src', 'data-small-src', 'data-download-href'].forEach(attr => {
      document.querySelectorAll(`[${attr}*="${badDomain}"]`).forEach(el => {
        el.setAttribute(attr, el.getAttribute(attr).replace(badDomain, goodDomain));
      });
    });
  }

  // Исправление изображений в редакторе
  api.decorateCooked($elem => {
    fixImageUrls();
  }, { id: 'fix-r2-image-urls' });

  // Исправление после первоначальной загрузки
  api.onPageChange(() => {
    fixImageUrls();
  });

  // Обработка динамически загружаемого контента
  const observer = new MutationObserver(mutations => {
    fixImageUrls();
  });

  // Запуск наблюдения после загрузки DOM
  if (document.readyState === "loading") {
    document.addEventListener('DOMContentLoaded', () => {
      fixImageUrls();
      startObserver();
    });
  } else {
    fixImageUrls();
    startObserver();
  }

  function startObserver() {
    observer.observe(document.body, {
      childList: true,
      subtree: true,
      attributes: true,
      attributeFilter: ['src', 'data-src', 'srcset', 'style']
    });
  }
});

Как работает код

Этот код выполняет следующие действия:

  1. Комплексное обнаружение: Находит все URL-адреса изображений, содержащие неправильный домен.
  2. Обработка различных элементов: Обрабатывает различные элементы изображений и атрибуты (теги img, изображения с отложенной загрузкой, фоновые изображения и т. д.).
  3. Динамический мониторинг: Использует MutationObserver для отслеживания изменений на странице, что гарантирует исправление также динамически загружаемого контента.
  4. Интеграция с Discourse: Интегрируется с API Discourse для обработки различных специальных сценариев.

Шаги по установке

  1. Войдите в учётную запись администратора Discourse.
  2. Перейдите в раздел Администрирование > Настройка > Компоненты тем.
  3. Нажмите кнопку Создать.
  4. Выберите опцию Создать новый компонент.
  5. Назовите его «Fix R2 Image URLs» (или любым другим предпочтительным именем).
  6. На вкладке «Javascript» вставьте код выше.
  7. Нажмите кнопку Создать.
  8. Нажмите кнопку Включить и выберите тему, к которой нужно применить компонент (обычно «По умолчанию»).

Проверка

После установки:

  1. Обновите страницу форума.
  2. Просмотрите сообщения, содержащие изображения.
  3. Убедитесь, что миниатюры отображаются правильно.
  4. С помощью инструментов разработчика браузера проверьте, что все запросы изображений указывают на CDN-домен.

Хотя решение на стороне клиента является самым простым, быстрым и наименее рискованным подходом, особенно когда прямой доступ к серверу ограничен.

Заключение

Этот простой компонент темы эффективно решает проблему URL-адресов изображений при интеграции Discourse с хранилищем Cloudflare R2, не требуя изменений на сервере или сложных настроек. Хотя он исправляет проблему на стороне клиента, а не устраняет коренную причину, его легко внедрить, он даёт немедленный результат и является идеальным решением.

Если ваш сайт Discourse также сталкивается с подобными проблемами, не стесняйтесь попробовать это решение.

4 лайка

Эта проблема затрагивает только чат? Пытаюсь понять масштаб.

Спасибо за ваш очень полезный отчет!

1 лайк

Да, эта ошибка затрагивает только чат. Мой провайдер S3 — Cloudflare R2. При отправке изображения в интерфейсе чата для него не применяются настройки ссылок CDN по умолчанию, из-за чего изображение не загружается.

1 лайк

Похоже, это тот же случай, что и в Upload images in chat can't be show normally when use s3 CDN, где была предпринята попытка исправления, но позже оно было отменено.