J’ai trouvé et résolu ce problème moi-même après qu’un client l’ait signalé. J’ai utilisé une IA pour générer le rapport de bug car j’étais paresseux et qu’elle fait un meilleur travail que moi — et elle a découvert un bug fondamental en le faisant, que j’ai vérifié manuellement.
Ce n’est pas grave, mais il pourrait être utile de vérifier si ce schéma provoque des problèmes dans d’autres endroits également.
Tout a commencé quand quelqu’un a coché enable_direct_s3_uploads sans réellement configurer et activer S3, ce qui a cassé tous les téléversements, renvoyant des erreurs 500 en coulisses. J’ai pensé que la valeur 500 méritait d’être investiguée.
Résumé
Lorsque enable_direct_s3_uploads est activé alors que le magasin de stockage actif est local, la tentative de téléversement peut provoquer :
NoMethodError (undefined method 'signed_request_for_temporary_upload' for an instance of FileStore::LocalStore)
app/services/external_upload_manager.rb:34:in 'ExternalUploadManager.create_direct_upload'
lib/external_upload_helpers.rb:56:in 'ExternalUploadHelpers#generate_presigned_put'
app/controllers/application_controller.rb:443:in 'block in ApplicationController#with_resolved_locale'
app/controllers/application_controller.rb:443:in 'ApplicationController#with_resolved_locale'
Cela semble être causé par une combinaison d’un état de configuration invalide et d’un rappel before_action écrasé.
Configuration
L’état problématique est effectivement :
SiteSetting.enable_direct_s3_uploads == true
SiteSetting.enable_s3_uploads == false
Discourse.store.is_a?(FileStore::LocalStore) == true
Pour le stockage local, Discourse.store n’implémente pas :
signed_request_for_temporary_upload
ce qui est attendu, car cette opération n’a de sens que pour un magasin externe/S3.
Comportement attendu
Si les téléversements directs S3 sont activés alors que le magasin actif est local, la requête devrait être rejetée proprement par external_store_check.
Idéalement, la combinaison de paramètres de site invalide devrait également être prévenue ou validée.
Comportement réel
La requête atteint ExternalUploadManager.create_direct_upload, qui appelle :
store.signed_request_for_temporary_upload(...)
sur un FileStore::LocalStore, ce qui entraîne une NoMethodError.
Cause possible
ExternalUploadHelpers enregistre le rappel suivant :
before_action :external_store_check,
only: %i[
generate_presigned_put
complete_external_upload
create_multipart
batch_presign_multipart_parts
complete_multipart
abort_multipart
]
Cela devrait empêcher la requête d’atteindre le code de téléversement externe lorsque le magasin est local.
Cependant, UploadsController enregistre plus tard un autre rappel en utilisant la même méthode de filtre :
before_action :external_store_check,
only: %i[_show_secure_deprecated show_secure]
Rails traite l’enregistrement répété du même rappel comme une redéfinition, de sorte que le dernier semble remplacer les conditions only: antérieures.

En conséquence, external_store_check n’est plus exécuté pour generate_presigned_put, permettant à un magasin local d’atteindre le chemin de code de téléversement direct.
Reproduction
- Configurer une instance Discourse avec un stockage de téléversement local.
- S’assurer que :
SiteSetting.enable_s3_uploads = false
SiteSetting.enable_direct_s3_uploads = true
- Tenter de téléverser un fichier.
- Observer la
NoMethodErrorprovenant deFileStore::LocalStore.
Une vérification de l’état via la console peut être effectuée avec :
[
SiteSetting.enable_direct_s3_uploads,
SiteSetting.enable_s3_uploads,
Discourse.store.class,
Discourse.store.external?
]
Correction suggérée
Les enregistrements de rappel ne devraient pas s’écraser mutuellement.
Par exemple, inclure les actions de téléversement sécurisé dans le rappel external_store_check existant :
before_action :external_store_check,
only: %i[
generate_presigned_put
complete_external_upload
create_multipart
batch_presign_multipart_parts
complete_multipart
abort_multipart
_show_secure_deprecated
show_secure
]
Alternativement, utiliser une méthode de rappel distincte pour les actions de téléversement sécurisé.
Il peut également être judicieux d’ajouter une validation afin que enable_direct_s3_uploads ne puisse pas être activé alors que les téléversements S3/externes sont désactivés.
Solution de contournement
Pour les sites utilisant un stockage de téléversement local :
SiteSetting.enable_direct_s3_uploads = false
empêche l’erreur.