Ho debuggato il problema e ho una potenziale soluzione (con l’aiuto di Gemini).
Ho creato questo messaggio (il contenuto del file trascinato non è importante) nella versione 3.4.5 (che sapevo funzionasse) e nella versione più recente:
Ho quindi utilizzato altri 2 account per scaricare entrambi i file e ho aggiornato la pagina.
Non ho visto un contatore di download per “senza-allegato” (anche se funzionava quando usavo la sorgente v3.4.5). “con-allegato” non ha mai mostrato il contatore di download, l’ho aggiunto solo per “completezza”.
Quando eseguo questo nel debugger:
post = Post.last
TopicLink.counts_for(Guardian.new(post.user), post.topic, [post])
Ho ottenuto:
=> {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}]}
Quindi i clic c’erano, ma l’interfaccia utente non li ha considerati perché erano filtrati dalla regola che impedisce agli utenti di vedere i conteggi dei clic per i link che puntano a topic privati/nascosti. Il problema è che la nuova regola non tiene conto del fatto che un link interno può essere un topic OPPURE un file interno. Qualsiasi cosa sia internal e non un topic viene attualmente scartata.
Sono riuscito a risolvere il problema modificando (in 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
in questo:
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
Spiegazione completa di Gemini
Hai trovato l’esatta prova schiacciante! Fornendo le due versioni di topic_link.rb, è chiarissimo esattamente cosa ha rotto questa funzionalità nella versione più recente di Discourse.
Il problema è causato interamente da un nuovo filtro di sicurezza aggiunto alla query del database in counts_for.
Il colpevole: il nuovo metodo apply_link_visibility_filters
Se guardi vicino alla fine del nuovo file topic_link.rb, troverai questo nuovo metodo:
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) <--- QUESTO È IL BUG
AND (#{link}.link_post_id IS NULL OR (#{target_posts}.id IS NOT NULL AND #{target_posts}.deleted_at IS NULL))
SQL
end
Perché questo rompe gli upload di file standard
Gli sviluppatori di Discourse hanno aggiunto questa regola SQL per impedire agli utenti di vedere i conteggi dei clic per i link che puntano a topic privati/nascosti a cui non hanno il permesso di accedere. Tuttavia, hanno accidentalmente coinvolto gli upload di file nel fuoco incrociato.
Ecco esattamente come il motore SQL valuta il tuo link di upload senza-allegato.funscript:
- #{link}.internal = false: Quando un upload viene salvato, Discourse lo segna come internal = true (perché il file è ospitato sul tuo forum). Quindi questo viene valutato come FALSE.
- #{target_topic}.id IS NOT NULL: Poiché si tratta di un upload di file e non di un link a un altro post del forum, non ha un topic di destinazione. L’ID del topic è NULL. Quindi anche questo viene valutato come FALSE.
- FALSE OR FALSE = FALSE.
Poiché entrambe le condizioni falliscono, la query SQL filtra completamente il tuo upload di file e il metodo counts_for restituisce un array vuoto al serializer.
(Nota: Il motivo per cui |allegato funziona ancora è perché il frontend gestisce le classi .attachment tramite un endpoint/meccanismo API completamente separato per il tracciamento dei download, bypassando completamente TopicLink!)
Come correggere il codice
Per correggere questo bug nel tuo ambiente locale, devi dire alla query SQL: “È accettabile se un link interno non ha un topic di destinazione, purché non ne dovesse avere uno fin dall’inizio (come un upload di file).”
Apri app/models/topic_link.rb nel tuo Dev Container, trova il metodo apply_link_visibility_filters (intorno alla riga 185) e modifica la riga rotta in questo modo:
Modifica questo:
AND (#{link}.internal = false OR #{target_topic}.id IS NOT NULL)
In questo:
AND (#{link}.internal = false OR #{link}.link_topic_id IS NULL OR #{target_topic}.id IS NOT NULL)
Perché questo lo risolve:
Aggiungendo #{link}.link_topic_id IS NULL, stai dicendo al database di consentire i link interni che sono puramente file/upload. Bloccerà comunque con successo i link che hanno un link_topic_id ma il target_topic è mancante/nascosto (il che soddisfa il requisito di sicurezza che gli sviluppatori di Discourse stavano cercando di raggiungere).