i have managed to fix this with an updated theme component that has settings for the domains. thus anyone can use it without having to hard code their own component. this works for any s3 compatible storage:
if you do patch it yourself, the initializer should look like this
import { apiInitializer } from "discourse/lib/api";
export default apiInitializer("1.8.0", (api) => {
// ⚠️ Ensure these are your exact domains!
// look at the image paths for post thumbnail & chat thumbnail and compare!
const badDomain = "yoursite-uploads.xxx.r2.cloudflarestorage.com";
const goodDomain = "uploads.yoursite.com";
function fixImage(img) {
if (img.src && img.src.includes(badDomain)) {
img.src = img.src.replace(badDomain, goodDomain);
}
if (img.dataset?.src && img.dataset.src.includes(badDomain)) {
img.dataset.src = img.dataset.src.replace(badDomain, goodDomain);
}
}
// 1. Safely fix images rendered in standard posts/HTML
api.decorateCooked($elem => {
const container = $elem[0];
if (!container) return;
container.querySelectorAll(`img[src*="${badDomain}"]`).forEach(fixImage);
}, { id: 'fix-r2-chat-bug' });
// 2. High-performance observer strictly for dynamically injected Chat elements
const observer = new MutationObserver(mutations => {
for (const mutation of mutations) {
// If a new chat message or element is injected into the DOM
if (mutation.type === 'childList') {
for (const node of mutation.addedNodes) {
if (node.nodeType === 1) { // ELEMENT_NODE
if (node.tagName === 'IMG') fixImage(node);
// Only search INSIDE the new node, not the whole document
node.querySelectorAll(`img[src*="${badDomain}"]`).forEach(fixImage);
}
}
}
// If an existing image's src attribute changes (lazy loading)
else if (mutation.type === 'attributes') {
if (mutation.target.tagName === 'IMG') {
fixImage(mutation.target);
}
}
}
});
// Start the observer immediately
observer.observe(document.body, {
childList: true,
subtree: true,
attributes: true,
attributeFilter: ['src', 'data-src']
});
});