Я отладил проблему и нашёл потенциальное решение (с помощью Gemini).
Я создал это сообщение (содержимое перетаскиваемого файла не имеет значения) в версии 3.4.5 (которая, как я знал, работала) и в последней версии:
Затем я использовал два других аккаунта, чтобы скачать оба файла, и обновил страницу.
Я не видел счётчика загрузок для «без-вложения» (хотя он работал, когда я использовал исходную версию v3.4.5). Для «с-вложением» счётчик загрузок так и не отображался; я добавил его только ради «полноты картины».
При выполнении этого в отладчике:
post = Post.last
TopicLink.counts_for(Guardian.new(post.user), post.topic, [post])
я получил:
=> {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}]}
Так что клики были, но пользовательский интерфейс их не учитывал, потому что они были отфильтрованы правилом, предотвращающим пользователям видеть количество кликов по ссылкам, указывающим на закрытые/скрытые темы. Проблема в том, что новое правило не учитывает, что внутренняя ссылка может быть темой ИЛИ внутренним файлом. Любое, что является internal и не является темой, в настоящее время отбрасывается.
Мне удалось решить проблему, изменив (в 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 (#{link_posts}.id IS NOT NULL AND #{link_posts}.deleted_at IS NULL))
SQL
end
на это:
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 (#{link_posts}.id IS NOT NULL AND #{link_posts}.deleted_at IS NULL))
SQL
end
Полное объяснение от Gemini
Вы нашли точную «копья в копилку»! Предоставив две версии topic_link.rb, стало кристально ясно, что именно сломало эту функцию в последней версии Discourse.
Проблема полностью вызвана новым фильтром безопасности, добавленным в запрос к базе данных в counts_for.
Виновник: новый метод apply_link_visibility_filters
Если посмотреть в нижней части нового файла topic_link.rb, можно найти этот новый метод:
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) <--- ЭТО ОШИБКА
AND (#{link}.link_post_id IS NULL OR (#{link_posts}.id IS NOT NULL AND #{link_posts}.deleted_at IS NULL))
SQL
end
Почему это ломает стандартные загрузки файлов
Разработчики Discourse добавили это SQL-правило, чтобы предотвратить пользователям видеть количество кликов по ссылкам, указывающим на закрытые/скрытые темы, к которым у них нет прав доступа. Однако они случайно затронули загрузки файлов.
Вот как именно SQL-движок оценивает вашу ссылку на загрузку without-attachment.funscript:
- #{link}.internal = false: Когда файл сохраняется, Discourse помечает его как internal = true (потому что файл размещён на вашем форуме). Поэтому это оценивается как FALSE.
- #{target_topic}.id IS NOT NULL: Поскольку это загрузка файла, а не ссылка на другой пост форума, у него нет целевой темы. ID темы равен NULL. Поэтому это также оценивается как FALSE.
- FALSE OR FALSE = FALSE.
Поскольку оба условия не выполняются, SQL-запрос полностью отфильтровывает вашу загрузку файла, и метод counts_for возвращает пустой массив сериализатору.
(Примечание: Причина, по которой |attachment всё ещё работает, заключается в том, что фронтенд обрабатывает классы .attachment через совершенно отдельный конечный пункт API/механизм для отслеживания загрузок, минуя TopicLink!)
Как исправить код
Чтобы исправить этот баг в вашей локальной среде, вам нужно сообщить SQL-запросу: «Нормально, если внутренняя ссылка не имеет целевой темы, если только она изначально и не должна была её иметь (как загрузка файла)».
Откройте app/models/topic_link.rb в вашем Dev Container, найдите метод apply_link_visibility_filters (около строки 185) и измените сломанную строку на эту:
Измените это:
AND (#{link}.internal = false OR #{target_topic}.id IS NOT NULL)
На это:
AND (#{link}.internal = false OR #{link}.link_topic_id IS NULL OR #{target_topic}.id IS NOT NULL)
Почему это исправляет проблему:
Добавляя #{link}.link_topic_id IS NULL, вы сообщаете базе данных разрешать внутренние ссылки, которые являются чисто файлами/загрузками. Это по-прежнему успешно блокирует ссылки, которые имеют link_topic_id, но целевая тема отсутствует/скрыта (что удовлетворяет требованию безопасности, которое пытались достичь разработчики Discourse).