Contador de links ausente para anexos

Eu depurei o problema e tenho uma correção potencial (com a ajuda do Gemini).

Criei esta mensagem (o conteúdo do arquivo arrastado não é importante) na versão 3.4.5 (que eu sabia que funcionava) e na versão mais recente:

Em seguida, usei 2 outras contas para baixar ambos os arquivos e atualizei a página.

Não vi um contador de downloads para o “sem-anexo” (embora tenha funcionado quando usei a origem v3.4.5). O “com-anexo” nunca mostrou o contador de downloads; eu apenas o adicionei por “completude”.

Ao executar isso no depurador:

post = Post.last
TopicLink.counts_for(Guardian.new(post.user), post.topic, [post])

Obtive:

=> {8 =>
  [{url: "/uploads/short-url/iMUBjeLNs0eRWZhdYnh24XGXY0f.funscript", clicks: 2, title: nil, internal: true, reflection: false},
   {url: "/uploads/short-url/rgnPszmhmEizcRVBobULTkMP1UI.funscript", clicks: 0, title: nil, internal: true, reflection: false}]}

Então, os cliques estavam lá, mas a interface não os considerou porque foram filtrados pela regra que impede os usuários de ver contagens de cliques para links que apontam para tópicos privados/ocultos. O problema é que a nova regra não leva em conta que um link interno pode ser um tópico OU um arquivo interno. Qualquer coisa internal e não um tópico é atualmente descartada.

Fui capaz de resolver o problema alterando (em topic_link.rb):

def self.apply_link_visibility_filters(builder, link:, target_topic:, target_posts:)
    builder.where(<<\~SQL)
      #{target_topic}.deleted_at IS NULL
      AND (#{target_topic}.id IS NULL OR #{target_topic}.visible = true)
      AND (#{link}.internal = false OR #{target_topic}.id IS NOT NULL)
      AND (#{link}.link_post_id IS NULL OR (#{target_posts}.id IS NOT NULL AND #{target_posts}.deleted_at IS NULL))
    SQL
  end

para isso:

def self.apply_link_visibility_filters(builder, link:, target_topic:, target_posts:)
    builder.where(<<\~SQL)
      #{target_topic}.deleted_at IS NULL
      AND (#{target_topic}.id IS NULL OR #{target_topic}.visible = true)
      AND (#{link}.internal = false OR #{link}.link_topic_id IS NULL OR #{target_topic}.id IS NOT NULL)
      AND (#{link}.link_post_id IS NULL OR (#{target_posts}.id IS NOT NULL AND #{target_posts}.deleted_at IS NULL))
    SQL
  end
Explicação completa do Gemini

Você encontrou a causa exata! Ao fornecer as duas versões de topic_link.rb, fica cristalino exatamente o que quebrou esse recurso na versão mais recente do Discourse.

O problema é causado inteiramente por um novo filtro de segurança adicionado à consulta de banco de dados em counts_for.

O Culpado: o novo método apply_link_visibility_filters

Se você olhar perto do final do novo arquivo topic_link.rb, encontrará este novo método:

codeRuby

def self.apply_link_visibility_filters(builder, link:, target_topic:, target_posts:)
    builder.where(<<~SQL)
      #{target_topic}.deleted_at IS NULL
      AND (#{target_topic}.id IS NULL OR #{target_topic}.visible = true)
      AND (#{link}.internal = false OR #{target_topic}.id IS NOT NULL)  <--- ESTE É O BUG
      AND (#{link}.link_post_id IS NULL OR (#{target_posts}.id IS NOT NULL AND #{target_posts}.deleted_at IS NULL))
    SQL
  end

Por que isso quebra uploads de arquivo padrão

Os desenvolvedores do Discourse adicionaram esta regra SQL para impedir que os usuários vejam contagens de cliques para links que apontam para tópicos privados/ocultos para os quais não têm permissão de visualização. No entanto, eles acidentalmente capturaram uploads de arquivo no fogo cruzado.

Aqui está exatamente como o mecanismo SQL avalia seu link de upload without-attachment.funscript:

  1. #{link}.internal = false: Quando um upload é salvo, o Discourse o marca como internal = true (porque o arquivo é hospedado no seu fórum). Portanto, isso avalia como FALSO.
  2. #{target_topic}.id IS NOT NULL: Como este é um upload de arquivo e não um link para outra publicação do fórum, ele não tem um tópico de destino. O ID do tópico é NULL. Portanto, isso também avalia como FALSO.
  3. FALSO OU FALSO = FALSO.

Como ambas as condições falham, a consulta SQL filtra completamente seu upload de arquivo, e o método counts_for retorna um array vazio para o serializador.

(Nota: O motivo de |attachment ainda funcionar é porque o frontend trata classes .attachment por meio de um endpoint de API/mecanismo completamente separado para rastreamento de downloads, contornando o TopicLink por completo!)

Como corrigir o código

Para corrigir este bug no seu ambiente local, você precisa dizer à consulta SQL: “Está tudo bem se um link interno não tiver um tópico de destino, desde que ele nunca deveria ter um para começar (como um upload de arquivo).”

Abra app/models/topic_link.rb no seu Dev Container, encontre o método apply_link_visibility_filters (por volta da linha 185) e altere a linha quebrada para isso:

Altere isso:

AND (#{link}.internal = false OR #{target_topic}.id IS NOT NULL)

Para isso:

AND (#{link}.internal = false OR #{link}.link_topic_id IS NULL OR #{target_topic}.id IS NOT NULL)

Por que isso corrige:
Ao adicionar #{link}.link_topic_id IS NULL, você está dizendo ao banco de dados para permitir links internos que são puramente arquivos/uploads. Ele ainda bloqueará com sucesso links que têm um link_topic_id, mas o target_topic está ausente/oculto (o que atende ao requisito de segurança que os desenvolvedores do Discourse estavam tentando alcançar).