“显示完整帖子”按钮在子文件夹安装中不起作用

我最近将我们的 Discourse 安装迁移到了一个子文件夹。执行此操作后,“显示完整帖子”按钮停止工作——点击以展开内容,但它没有加载完整的帖子。

我的 WP Discourse 配置中没有任何更改。

https://tecnoblog.net/comunidade/t/paramount-oferece-us-108-bilhoes-em-dinheiro-para-tomar-warner-da-netflix/157441

当直接在浏览器中访问嵌入式 URL 时,它返回 404 错误:

https://tecnoblog.net/comunidade/posts/483289/expand-embed

1 个赞

这不相关,该路由只响应 application/json 内容类型。https://tecnoblog.net/comunidade/posts/483289/expand-embed.json 返回

"\"\u003cdiv\u003e\u003cdiv\u003e\u003c/div\u003e\u003c/div\u003e\\n\u003chr\u003e\\n\u003csmall\u003eEste é um tópico de discussão auxiliar para a entrada original em \u003ca href='https://tecnoblog.net/noticias/paramount-oferece-us-108-bilhoes-em-dinheiro-para-tomar-warner-da-netflix'\u003ehttps://tecnoblog.net/noticias/paramount-oferece-us-108-bilhoes-em-dinheiro-para-tomar-warner-da-netflix\u003c/a\u003e\u003c/small\u003e\\n\""

\u003cdiv\u003e\u003cdiv\u003e\u003c/div\u003e\u003c/div\u003e 应该是内容。

你是否也更改了博客网址?

onebox 的显示对我来说也很奇怪,我期望它会有一个缓存的截断内容,所以我假设 body.present? 在上面的条件中是 false。

你能进入 Rails 控制台并检查 TopicEmbed.where(topic_id: 157441).pick(:embed_url) 是否向你显示了正确的博客内容网址吗?

你能在 https://tecnoblog.net/comunidade/logs 上发现任何相关的错误吗?

2 个赞

哦,好的!

它返回帖子的网址:

discourse(prod) => TopicEmbed.where(topic_id: 157441).pick(:embed_url)
=> “``https://tecnoblog.net/noticias/paramount-oferece-us-108-bilhoes-em-dinheiro-para-tomar-warner-da-netflix”

我不认为日志中有任何相关的错误。

没有!博客网址一直都是 tecnoblog.net

还值得一提的是,服务器的 IP 在 CF(Cloudflare)的防火墙中被绕过了:

2 个赞

我曾多次遇到并调试过此类问题,这很复杂,请耐心配合。

请运行以下脚本并在此处分享输出

# 用您正在调试的主题 ID 或 URL 替换此处内容
topic_id = 386983

# 1. 检查 TopicEmbed 是否存在及其内容
te = TopicEmbed.find_by(topic_id: topic_id)
puts "TopicEmbed 存在: #{te.present?}"
puts "嵌入 URL: #{te&.embed_url}"
puts "内容缓存存在: #{te&.embed_content_cache.present?}"
puts "内容缓存长度: #{te&.embed_content_cache&.length || 0}"
puts "内容 SHA1: #{te&.content_sha1}"

# 2. 检查实际缓存的内容(前 500 个字符)
puts "\n--- 缓存内容预览 ---"
puts te&.embed_content_cache&.truncate(500)

# 3. 尝试从远程 URL 获取
if te&.embed_url.present?
  puts "\n--- 尝试远程获取 ---"
  begin
    response = TopicEmbed.find_remote(te.embed_url)
    puts "远程获取成功: #{response.present?}"
    puts "远程正文存在: #{response&.body.present?}"
    puts "远程正文长度: #{response&.body&.length || 0}"
    puts "远程标题: #{response&.title}"
    puts "远程正文: #{response&.body&.truncate(500)}"
  rescue => e
    puts "远程获取失败: #{e.message}"
  end
end

# 4. 检查 expanded_for 会返回什么
if te.present?
  puts "\n--- 测试 expanded_for ---"
  post = Post.find(te.post_id)

  # 清除缓存以强制重新获取
  Discourse.cache.delete("embed-topic:#{topic_id}")

  begin
    expanded = TopicEmbed.expanded_for(post)
    puts "展开内容存在: #{expanded.present?}"
    puts "展开内容长度: #{expanded&.length || 0}"
  rescue => e
    puts "expanded_for 失败: #{e.message}"
  end
end

# 5. 检查相关设置
puts "\n--- 站点设置 ---"
puts "embed_truncate: #{SiteSetting.embed_truncate}"
puts "allowed_embed_selectors: #{SiteSetting.allowed_embed_selectors}"
puts "blocked_embed_selectors: #{SiteSetting.blocked_embed_selectors}"

这将显示为什么 https://tecnoblog.net/comunidade/t/governo-renova-app-da-cnh-para-baratear-obtencao-do-documento/157462?u=falco 出现故障

4 个赞
discourse(prod)> # Replace with the topic ID or URL you’re debugging
discourse(prod)> topic_id = 386983
discourse(prod)>
discourse(prod)> # 1. Check if TopicEmbed exists and its content
discourse(prod)> te = TopicEmbed.find_by(topic_id: topic_id)
discourse(prod)> puts “TopicEmbed exists: #{te.present?}”
discourse(prod)> puts “Embed URL: #{te&.embed_url}”
discourse(prod)> puts “Content cache present: #{te&.embed_content_cache.present?}”
discourse(prod)> puts “Content cache length: #{te&.embed_content_cache&.length || 0}”
discourse(prod)> puts “Content SHA1: #{te&.content_sha1}”
discourse(prod)>
discourse(prod)> # 2. Check the actual cached content (first 500 chars)
discourse(prod)> puts “\n— Cached content preview —”
discourse(prod)> puts te&.embed_content_cache&.truncate(500)
discourse(prod)>
discourse(prod)> # 3. Try fetching from the remote URL
discourse(prod)* if te&.embed_url.present?
discourse(prod)*   puts “\n— Attempting remote fetch —”
discourse(prod)*   begin
discourse(prod)*     response = TopicEmbed.find_remote(te.embed_url)
discourse(prod)*     puts “Remote fetch success: #{response.present?}”
discourse(prod)*     puts “Remote body present: #{response&.body.present?}”
discourse(prod)*     puts “Remote body length: #{response&.body&.length || 0}”
discourse(prod)*     puts “Remote title: #{response&.title}”
discourse(prod)*     puts “Remote body: #{response&.body&.truncate(500)}”
discourse(prod)*   rescue => e
discourse(prod)*     puts “Remote fetch FAILED: #{e.message}”
discourse(prod)*   end
discourse(prod)> end
discourse(prod)>
discourse(prod)> # 4. Check what expanded_for would return
discourse(prod)* if te.present?
discourse(prod)*   puts “\n— Testing expanded_for —”
discourse(prod)*   post = Post.find(te.post_id)
discourse(prod)*
discourse(prod)*   # Clear cache to force fresh fetch
discourse(prod)*   Discourse.cache.delete(“embed-topic:#{topic_id}”)
discourse(prod)*
discourse(prod)*   begin
discourse(prod)*     expanded = TopicEmbed.expanded_for(post)
discourse(prod)*     puts “Expanded content present: #{expanded.present?}”
discourse(prod)*     puts “Expanded content length: #{expanded&.length || 0}”
discourse(prod)*   rescue => e
discourse(prod)*     puts “expanded_for FAILED: #{e.message}”
discourse(prod)*   end
discourse(prod)> end
discourse(prod)>
discourse(prod)> # 5. Check relevant settings
discourse(prod)> puts “\n— Site Settings —”
discourse(prod)> puts “embed_truncate: #{SiteSetting.embed_truncate}”
discourse(prod)> puts “allowed_embed_selectors: #{SiteSetting.allowed_embed_selectors}”
discourse(prod)> puts “blocked_embed_selectors: #{SiteSetting.blocked_embed_selectors}”
TopicEmbed exists: false
Embed URL:
Content cache present: false
Content cache length: 0
Content SHA1:

— Cached content preview —

— Site Settings —
embed_truncate: true
allowed_embed_selectors:
blocked_embed_selectors:
=> nil
discourse(prod)>

:thinking:

1 个赞

您确定这是正确的主题 ID 吗?https://tecnoblog.net/comunidade/t/-/386983 链接到 404 页面。

1 个赞

原来如此。我链接的主题实际上是 157462

我的错!

这是正确帖子ID的结果

TopicEmbed 存在: true
Embed URL: https://tecnoblog.net/noticias/governo-renova-app-da-cnh-para-baratear-obtencao-do-documento
Content cache 存在: true
Content cache 长度: 22
Content SHA1:

— 缓存内容预览 —

<div><div></div></div>

— 尝试远程获取 —
Remote fetch 成功: true
Remote body 存在: true
Remote body 长度: 22
Remote title:
Remote body: 

— 测试 expanded_for —
Expanded content 存在: true
Expanded content 长度: 309

— 站点设置 —
embed_truncate: true
allowed_embed_selectors:
blocked_embed_selectors:
=> nil

您的 Cloudflare 绕过成功了吗?看起来 https://tecnoblog.net/noticias/governo-renova-app-da-cnh-para-baratear-obtencao-do-documento 的正文只有 22 个字符,没有标题标签。

是的!来自 discourse 服务器的所有请求都已绕过:

Screenshot 2025-12-12 at 14.44.21

我注意到嵌入的 URL 末尾没有斜杠。所有 URL 都应该有尾随斜杠。

Screenshot 2025-12-12 at 14.45.51

所以也许 discourse 没有遵循重定向?

但为什么它保存的 URL 没有尾随斜杠呢?

1 个赞

这很容易测试,请尝试

url = "https://tecnoblog.net/noticias/governo-renova-app-da-cnh-para-baratear-obtencao-do-documento/"
response = TopicEmbed.find_remote(url)
puts "远程获取成功: #{response.present?}"
puts "远程正文存在: #{response&.body.present?}"
puts "远程正文长度: #{response&.body&.length || 0}"
puts "远程标题: #{response&.title}"
puts "远程正文: #{response&.body&.truncate(500)}"

我认为它有效:

discourse(prod)> url = “https://tecnoblog.net/noticias/governo-renova-app-da-cnh-para-baratear-obtencao-do-documento/”
discourse(prod)> response = TopicEmbed.find_remote(url)
discourse(prod)> puts “Remote fetch success: #{response.present?}”
discourse(prod)> puts “Remote body present: #{response&.body.present?}”
discourse(prod)> puts “Remote body length: #{response&.body&.length || 0}”
discourse(prod)> puts “Remote title: #{response&.title}”
discourse(prod)> puts “Remote body: #{response&.body&.truncate(500)}”
Remote fetch success: true
Remote body present: true
Remote body length: 3776
Remote title: Governo renova app da CNH para baratear obtenção do documento • Tecnoblog
Remote body: 

<figure><img src="https://files.tecnoblog.net/wp-content/uploads/2025/12/cnh-brasil-app-1060x596.jpg">

	<figcaption>Aplicativo CNH do Brasil (imagem: Emerson Alecrim/Tecnoblog)</figcaption></figure>

</div>

<details>
    Resumo
    <div><ul>
<li>App CNH do Brasil substitui CDT e passa a oferecer recursos para obtenção da CNH, em especial, aulas teóricas gratuitas;</li>
<li>Aulas práticas continuam obrigatórias, mas a carga horária mínima foi reduzida de ...
=> nil


在没有尾部斜杠的情况下,如下所示:

discourse(prod)> url = “https://tecnoblog.net/noticias/governo-renova-app-da-cnh-para-baratear-obtencao-do-documento”
discourse(prod)> response = TopicEmbed.find_remote(url)
discourse(prod)> puts “Remote fetch success: #{response.present?}”
discourse(prod)> puts “Remote body present: #{response&.body.present?}”
discourse(prod)> puts “Remote body length: #{response&.body&.length || 0}”
discourse(prod)> puts “Remote title: #{response&.title}”
discourse(prod)> puts “Remote body: #{response&.body&.truncate(500)}”
Remote fetch success: true
Remote body present: true
Remote body length: 22
Remote title:
Remote body: 
=> nil

在帖子帖文的 slug 发生变化时,旧帖子中也会出现同样的错误。

例如,在这篇帖子中,以前使用的网址是:

https://tecnoblog.net/486925/o-que-e-pirataria-digital/

现在,它已更改为:

https://tecnoblog.net/responde/o-que-e-pirataria-digital/

1 个赞

这似乎是主要问题。当使用 在另一个网站上通过 Javascript 嵌入 Discourse 评论 时,您可以通过一个参数来控制它,修复起来非常简单。

我不熟悉 WP-Discourse 是如何确定这个的,它应该使用帖子的规范(canonical)地址,但我不确定。@angus 有什么想法吗?

有没有办法强制 Discourse 更新一个分类下的所有嵌入式网址,并跟随它到最终目的地?

我打算在嵌入式 Discourse(您一直在测试的完整嵌入)准备好投入生产时迁移到它。但是,如果嵌入式网址不匹配,那么它可能会为每个帖子创建新主题并丢失评论……

1 个赞

运行

te = TopicEmbed.find_by(topic_id: 157462)
te.embed_url = te.embed_url + "/"
te.save

这能修复 https://tecnoblog.net/comunidade/t/governo-renova-app-da-cnh-para-baratear-obtencao-do-documento/157462 吗?

它奏效了!

但是有没有针对这种情况的修复方法?

Gemini 建议了以下代码:

# 配置
CATEGORY_SLUG = 'tb' 
category = Category.find_by(slug: CATEGORY_SLUG)

unless category
  puts "错误:未找到类别 '#{CATEGORY_SLUG}'。"
  exit
end

puts "正在开始对类别 '#{category.name}' 中的 URL 进行完整扫描..."
puts "这可能需要一些时间,具体取决于主题的数量和您网站的响应速度..."

count_updated = 0
count_errors = 0
count_ok = 0

Topic.where(category_id: category.id).find_each do |topic|
  current_url = topic.custom_fields["embed_url"]
  
  # 如果没有 embed_url,则跳过
  next unless current_url.present?

  begin
    # 发送 GET 请求并跟随重定向
    response = Faraday.get(current_url)
    final_url = response.env.url.to_s

    # 如果请求成功 (200 OK)
    if response.status == 200
      # 检查最终 URL 是否与数据库中保存的 URL 不同
      # 比较忽略细微差异,但这里我们比较精确的字符串
      if final_url != current_url
        puts "\n[更新] 主题 ##{topic.id}:"
        puts "   从:   #{current_url}"
        puts "   到: #{final_url}"
        
        topic.custom_fields["embed_url"] = final_url
        topic.save_custom_fields(true)
        count_updated += 1
      else
        # print "." # 取消注释以查看视觉进度(点点)
        count_ok += 1
      end
    else
      puts "\n[HTTP 错误 #{response.status}] 主题 ##{topic.id} - URL: #{current_url}"
      count_errors += 1
    end

  rescue Faraday::ConnectionFailed, Faraday::TimeoutError => e
    puts "\n[连接失败] 主题 ##{topic.id} - URL: #{current_url} - #{e.message}"
    count_errors += 1
  rescue StandardError => e
    puts "\n[一般错误] 主题 ##{topic.id} - #{e.message}"
    count_errors += 1
  end
  
  # 可选:短暂暂停,以免使您的 WordPress 服务器超载
  # sleep 0.1 
end

puts "\n\n最终摘要:"
puts "------------------------------------------------"
puts "已检查的主题 (正常): #{count_ok}"
puts "已更新的主题:      #{count_updated}"
puts "发现的错误:        #{count_errors}"
puts "------------------------------------------------"

终于取得了一些进展 :sweat_smile:

这样的脚本是个好主意,只是在运行它之前先进行备份

哪怕只是这个小表的备份也会很有帮助。

1 个赞

好的!我会在团队完成轮班后再试着运行它。

1 个赞

嘿,各位,我看到尾部斜杠又出问题了 :slight_smile:

[尾部斜杠是] 主要问题。当使用 通过 Javascript 在其他网站上嵌入 Discourse 评论 时,您可以通过一个参数来控制它,修复起来非常简单。

仅供参考,Discourse 中的所有主题嵌入都会从 embed_url 中删除尾部斜杠;请参阅 TopicEmbed.normalize_url。由于一个涉及 javascript 嵌入和 WP Discourse 嵌入交叉的独立案例,我们在这两种嵌入方法中标准化了这种处理。请参阅 Apply TopicEmbed url normalisation to embed urls inserted in the PostCreator by angusmcleod · Pull Request #30641 · discourse/discourse · GitHub

@Thiago_Mobilon 在这次迁移过程中,您是否也更新了您的 Discourse?有可能是我们将 embed_url 标准化处理应用于 WP Discourse 嵌入的结果,这是在迁移到子文件夹安装的同时发生的。您目前运行的是哪个版本的 Discourse?(如果您知道的话,迁移前运行的是哪个版本?)

顺便说一句,当我在最新版本的 Discourse 上在本地运行以下两个命令时,我得到了相同的结果,即文章的 HTML 正文

# 带尾部斜杠
TopicEmbed.find_remote("https://tecnoblog.net/noticias/governo-renova-app-da-cnh-para-baratear-obtencao-do-documento/")

# 不带尾部斜杠
TopicEmbed.find_remote("https://tecnoblog.net/noticias/governo-renova-app-da-cnh-para-baratear-obtencao-do-documento")

# 产生相同的结果

您是否可能在 WordPress 端做了一些更改?

2 个赞

您好,Angus!

不,这些是不同的问题。尾部斜杠在我们迁移到子文件夹时就开始出现,但也有很多几年前的旧 URL,它们的 slug 现在不同了。

我不得不重建安装,所以是的,我认为这个新的标准可能是原因。

我建议的修复此问题的方法是:Discourse 能否至少跟踪一两个重定向来检索数据?这将解决尾部斜杠问题,并在将来可能发生 URL 更改时使网站更安全。

此外,这样做更安全,因为没有必要运行脚本来更新旧主题,那也可能对数据库造成一些损害。