Téléversements cassés avec installation de sous-dossier

Similaire à Uploaded avatars and Gravatar not working with subfolder installation

Tous les téléchargements sont cassés sur mon installation en sous-dossier. Les téléchargements arrivent dans le répertoire de téléchargements réel, mais lors du rendu des publications, toutes les images obtiennent src="".

Faire une publication…
https://i.imgur.com/ofOUY4e.png

Après la publication…
https://i.imgur.com/EBmnD6e.png

De manière assez étonnante, si je passe ensuite à un autre navigateur (Chrome maintenant), que j’ouvre le sujet (où l’image est toujours cassée), mais que je clique ensuite sur modifier, l’image se rend à nouveau dans l’aperçu de modification !

https://i.imgur.com/3rQirhc.png

Cela confirme qu’il télécharge avec succès sur le serveur, ce que j’ai vérifié :

root@cs6991:/var/discourse# ./launcher enter app
x86_64 arch detected.
root@cs6991-app:/var/www/discourse# ls 'public/~cs6991/forum'
backups  uploads
root@cs6991-app:/var/www/discourse# ls 'public/~cs6991/forum/uploads'
default
root@cs6991-app:/var/www/discourse# ls 'public/~cs6991/forum/uploads/default/original/1X/'
08335563eac3a393e60a902d4d38cffdfa6d967d.png  3eee67e6460792667bab4f2248ad4643be4feae3.png
29e403dabcfee32379629fb6d844354193e278ba.png  42ecfcb27b534acc9f3436fa7d291c2fca106e57.png

Mais cela ne semble tout simplement pas se rendre sur la page réelle.
Le même problème se produit avec d’autres téléchargements, tels que les avatars.

Quelques informations :

Sous-dossier : /~cs6991/forum

app.yml

## voici le modèle de conteneur Discourse autonome tout-en-un
##
## Après avoir apporté des modifications à ce fichier, vous DEVEZ reconstruire
## /var/discourse/launcher rebuild app
##
## SOYEZ TRÈS PRUDENT LORS DE LA MODIFICATION !
## LES FICHIERS YAML SONT EXTRÊMEMENT SENSIBLES AUX ERREURS D'ESPACEMENT OU D'ALIGNEMENT !
## visitez http://www.yamllint.com/ pour valider ce fichier si nécessaire

templates:
  - "templates/postgres.template.yml"
  - "templates/redis.template.yml"
  - "templates/web.template.yml"
  - "templates/web.ratelimited.template.yml"
## Décommentez ces deux lignes si vous souhaitez ajouter Lets Encrypt (https)
  #- "templates/web.ssl.template.yml"
  #- "templates/web.letsencrypt.ssl.template.yml"

## quels ports TCP/IP ce conteneur doit-il exposer ?
## Si vous souhaitez que Discourse partage un port avec un autre serveur web comme Apache ou nginx,
## voir https://meta.discourse.org/t/17247 pour plus de détails
expose:
  - "80:80"   # http
  - "443:443" # https

params:
  db_default_text_search_config: "pg_catalog.english"

  ## Définissez db_shared_buffers à un maximum de 25 % de la mémoire totale.
  ## sera défini automatiquement par bootstrap en fonction de la RAM détectée, ou vous pouvez le remplacer
  db_shared_buffers: "128MB"

  ## peut améliorer les performances de tri, mais augmente l'utilisation de la mémoire par connexion
  #db_work_mem: "40MB"

  ## Quelle révision Git ce conteneur doit-il utiliser ? (défaut : tests-passed)
  #version: tests-passed

env:
  LC_ALL: en_US.UTF-8
  LANG: en_US.UTF-8
  LANGUAGE: en_US.UTF-8
  # DISCOURSE_DEFAULT_LOCALE: en

  ## Combien de requêtes web simultanées sont prises en charge ? Dépend de la mémoire et des cœurs CPU.
  ## sera défini automatiquement par bootstrap en fonction des CPU détectés, ou vous pouvez le remplacer
  UNICORN_WORKERS: 2

  ## TODO : Le nom de domaine auquel cette instance Discourse répondra
  ## Obligatoire. Discourse ne fonctionnera pas avec un simple numéro IP.
  DISCOURSE_HOSTNAME: 'cgi.cse.unsw.edu.au'

  ## Décommentez si vous souhaitez que le conteneur soit démarré avec le même
  ## nom d'hôte (-h option) que celui spécifié ci-dessus (défaut "$hostname-$config")
  #DOCKER_USE_HOSTNAME: true

  ## TODO : Liste des e-mails séparés par des virgules qui seront faits administrateurs et développeurs
  ## lors de la première inscription, exemple 'user1@example.com,user2@example.com'
  DISCOURSE_DEVELOPER_EMAILS: '<<REDACTED>>'

  ## TODO : Le serveur de messagerie SMTP utilisé pour valider les nouveaux comptes et envoyer des notifications
  # ADRESSE SMTP, nom d'utilisateur et mot de passe sont requis
  # ATTENTION le caractère '#' dans le mot de passe SMTP peut causer des problèmes !
  DISCOURSE_SMTP_ADDRESS: email-smtp.ap-southeast-2.amazonaws.com
  DISCOURSE_SMTP_PORT: 587
  DISCOURSE_SMTP_USER_NAME: <<REDACTED>>
  DISCOURSE_SMTP_PASSWORD: <<REDACTED>>
  #DISCOURSE_SMTP_ENABLE_START_TLS: true           # (facultatif, défaut true)
  #DISCOURSE_SMTP_DOMAIN: discourse.example.com    # (requis par certains fournisseurs)
  DISCOURSE_NOTIFICATION_EMAIL: discourse@cs6991.email    # (adresse d'envoi des notifications)

  ## Si vous avez ajouté le modèle Lets Encrypt, décommentez ci-dessous pour obtenir un certificat SSL gratuit
  #LETSENCRYPT_ACCOUNT_EMAIL: me@example.com

  ## L'adresse CDN http ou https pour cette instance Discourse (configurée pour tirer)
  ## voir https://meta.discourse.org/t/14857 pour plus de détails
  #DISCOURSE_CDN_URL: https://discourse-cdn.example.com

  ## La clé d'adresse IP Maxmind pour la recherche d'adresses IP de géolocalisation
  ## voir https://meta.discourse.org/t/-/137387/23 pour plus de détails
  #DISCOURSE_MAXMIND_LICENSE_KEY: 1234567890123456

  DISCOURSE_RELATIVE_URL_ROOT: '/~cs6991/forum'

## Le conteneur Docker est sans état ; toutes les données sont stockées dans /shared
volumes:
  - volume:
      host: /var/discourse/shared/standalone
      guest: /shared
  - volume:
      host: /var/discourse/shared/standalone/log/var-log
      guest: /var/log

## Les plugins vont ici
## voir https://meta.discourse.org/t/19157 pour plus de détails
hooks:
  after_code:
    - exec:
        cd: $home/plugins
        cmd:
          - git clone https://github.com/discourse/docker_manager.git

## Toutes les commandes personnalisées à exécuter après la construction
run:
  - exec: echo "Début des commandes personnalisées"
  ## Si vous souhaitez définir l'adresse e-mail 'De' pour votre première inscription, décommentez et modifiez :
  ## Après avoir reçu le premier e-mail d'inscription, re-commentez la ligne. Elle ne doit s'exécuter qu'une seule fois.
  #- exec: rails r "SiteSetting.notification_email='info@unconfigured.discourse.org'"
  - exec:
      cd: $home
      cmd:
        - mkdir -p public/~cs6991/forum
        - cd public/~cs6991/forum && ln -s ../../uploads && ln -s ../../backups
  - replace:
     global: true
     filename: /etc/nginx/conf.d/discourse.conf
     from: proxy_pass http://discourse;
     to: |
        rewrite ^/(.*)$ /~cs6991/forum/$1 break;
        proxy_pass http://discourse;
  - replace:
     filename: /etc/nginx/conf.d/discourse.conf
     from: etag off;
     to: |
        etag off;
        location /~cs6991/forum {
           rewrite ^/~cs6991/forum/?(.*)$ /$1;
        }
  - replace:
       filename: /etc/nginx/conf.d/discourse.conf
       from: $proxy_add_x_forwarded_for
       to: $http_your_original_ip_header
       global: true
  - exec: echo "Fin des commandes personnalisées"

Tout le reste, d’après ce que je peux dire, semble fonctionner correctement - seule le rendu des téléchargements agit de manière assez étrange.

J’ai vérifié ce comportement sur une construction complètement nouvelle - c’est-à-dire rm -rf /var/discourse, en supprimant complètement docker, et en suivant les instructions d’installation cloud + sous-dossier.

S’il y a d’autres investigations que je peux entreprendre, je suis heureux de les réaliser. (désolé pour les liens imgur - je ne suis pas encore autorisé à intégrer plus de 2 images ici !)

Cordialement !

1 « J'aime »

Informations supplémentaires – il semble que la source soit déjà supprimée avant le rendu, car elle est manquante dans la base de données de production :

# sudo -u postgres psql discourse
discourse=# select * from posts where id=13;
 id | user_id | topic_id | post_number |                             raw                             |                                                  cooked                                                  |        created_at         |        updated_at         | reply_to_post_number | reply_count | quote_count |         deleted_at         | off_topic_count | like_count | incoming_link_count | bookmark_count | score | reads | post_type | sort_order | last_editor_id | hidden | hidden_reason_id | notify_moderators_count | spam_count | illegal_count | inappropriate_count |      last_version_at       | user_deleted | reply_to_user_id | percent_rank | notify_user_count | like_score | deleted_by_id | edit_reason | word_count | version | cook_method | wiki |          baked_at          | baked_version | hidden_at | self_edits | reply_quoted | via_email | raw_email | public_version | action_code | locked_by_id | image_upload_id
----+---------+----------+-------------+-------------------------------------------------------------+----------------------------------------------------------------------------------------------------------+---------------------------+---------------------------+----------------------+-------------+-------------+----------------------------+-----------------+------------+---------------------+----------------+-------+-------+-----------+------------+----------------+--------+------------------+-------------------------+------------+---------------+---------------------+----------------------------+--------------+------------------+--------------+-------------------+------------+---------------+-------------+------------+---------+-------------+------+----------------------------+---------------+-----------+------------+--------------+-----------+-----------+----------------+-------------+--------------+-----------------
 13 |       1 |        7 |           2 | ![ferris|690x459](upload://5YA5Y9vjz0iQmn2DErtUBrHCKng.png) | <p><img src="" alt="ferris" data-base62-sha1="5YA5Y9vjz0iQmn2DErtUBrHCKng" width="690" height="459"></p> | 2022-09-01 19:30:38.97281 | 2022-09-01 19:30:38.97281 |                      |           0 |           0 | 2022-09-01 19:47:34.612042 |               0 |          0 |                   0 |              0 |   0.2 |     1 |         1 |          2 |              1 | f      |                  |                       0 |          0 |             0 |                   0 | 2022-09-01 19:30:38.993775 | f            |                  |          0.5 |                 0 |          0 |             1 |             |          5 |       1 |           1 | f    | 2022-09-01 19:30:38.972751 |             2 |           |          0 | f            | f         |           |              1 |             |              |

Aussi depuis l’onglet réseau, créant une réponse avec une image :

raw	"Mon image est insérée ici :\n\n![ferris|690x459](upload://5YA5Y9vjz0iQmn2DErtUBrHCKng.png)\n\n\nL'image est au-dessus."
unlist_topic	"false"
category	"4"
topic_id	"7"
is_warning	"false"
archetype	"regular"
typing_duration_msecs	"7500"
composer_open_duration_msecs	"14116"
featured_link	""
shared_draft	"false"
draft_key	"topic_7"
image_sizes[https://cgi.cse.unsw.edu.au/~cs6991/forum/uploads/default/original/1X/29e403dabcfee32379629fb6d844354193e278ba.png][width]	"1200"
image_sizes[https://cgi.cse.unsw.edu.au/~cs6991/forum/uploads/default/original/1X/29e403dabcfee32379629fb6d844354193e278ba.png][height]	"800"
nested_post	"true"

ou la chaîne de requête brute si vous préférez :

raw=Mon+image+est+insérée+ici+%3A%0A%0A!%5Bferris%7C690x459%5D(upload%3A%2F%2F5YA5Y9vjz0iQmn2DErtUBrHCKng.png)%0A%0A%0AL%27image+est+au-dessus.%26unlist_topic=false%26category=4%26topic_id=7%26is_warning=false%26archetype=regular%26typing_duration_msecs=7500%26composer_open_duration_msecs=14116%26featured_link=%26shared_draft=false%26draft_key=topic_7%26image_sizes%5Bhttps%3A%2F%2Fcgi.cse.unsw.edu.au%2F~cs6991%2Fforum%2Fuploads%2Fdefault%2Foriginal%2F1X%2F29e403dabcfee32379629fb6d844354193e278ba.png%5D%5Bwidth%5D=1200%26image_sizes%5Bhttps%3A%2F%2Fcgi.cse.unsw.edu.au%2F~cs6991%2Fforum%2Fuploads%2Fdefault%2Foriginal%2F1X%2F29e403dabcfee32379629fb6d844354193e278ba.png%5D%5Bheight%5D=800%26nested_post=true

la réponse semble faire écho au message, et vous pouvez voir que la source est déjà dépouillée à ce stade :

{
  "action": "create_post",
  "post": {
    "id": 16,
    "name": null,
    "username": "z.kologlu",
    "avatar_template": "/~cs6991/forum/letter_avatar_proxy/v4/letter/z/b9bd4f/{size}.png",
    "created_at": "2022-09-02T05:37:25.680Z",
    "cooked": "\u003cp\u003eMon image est insérée ici :\u003c/p\u003e\\n\u003cp\u003e\u003cimg src=\\\"\\\" alt=\\\"ferris\\\" data-base62-sha1=\\\"5YA5Y9vjz0iQmn2DErtUBrHCKng\\\" width=\\\"690\\\" height=\\\"459\\\"\u003e\u003c/p\u003e\\n\u003cp\u003eL'image est au-dessus.\u003c/p\u003e",
    "post_number": 5,
    "post_type": 1,
    "updated_at": "2022-09-02T05:37:25.680Z",
    "reply_count": 0,
    "reply_to_post_number": null,
    "quote_count": 0,
    "incoming_link_count": 0,
    "reads": 0,
    "readers_count": 0,
    "score": 0,
    "yours": true,
    "topic_id": 7,
    "topic_slug": "welcome-to-discourse",
    "display_username": null,
    "primary_group_name": null,
    "flair_name": null,
    "flair_url": null,
    "flair_bg_color": null,
    "flair_color": null,
    "version": 1,
    "can_edit": true,
    "can_delete": true,
    "can_recover": false,
    "can_wiki": true,
    "user_title": null,
    "bookmarked": false,
    "raw": "Mon image est insérée ici :\n\n![ferris|690x459](upload://5YA5Y9vjz0iQmn2DErtUBrHCKng.png)\n\n\nL'image est au-dessus.",
    "actions_summary": [
      {
        "id": 3,
        "can_act": true
      },
      {
        "id": 4,
        "can_act": true
      },
      {
        "id": 8,
        "can_act": true
      },
      {
        "id": 7,
        "can_act": true
      }
    ],
    "moderator": false,
    "admin": true,
    "staff": true,
    "user_id": 1,
    "draft_sequence": 12,
    "hidden": false,
    "trust_level": 1,
    "deleted_at": null,
    "user_deleted": false,
    "edit_reason": null,
    "can_view_edit_history": true,
    "wiki": false,
    "reviewable_id": null,
    "reviewable_score_count": 0,
    "reviewable_score_pending_count": 0
  },
  "success": true
}

J’ai suivi ce problème plus loin jusqu’à cette fonction : discourse/app/models/post.rb at main · discourse/discourse · GitHub

Fonction locale modifiée :

  def cook(raw, opts = {})
    Rails.logger.info("Cooking post with raw: #{raw}")
    # Pour certains posts, par exemple ceux importés via RSS, nous prenons en charge le HTML brut. Dans ce
    # cas, nous pouvons sauter le pipeline de rendu.
    return raw if cook_method == Post.cook_methods[:raw_html]

    options = opts.dup
    options[:cook_method] = cook_method

    post_user = self.user
    options[:user_id] = post_user.id if post_user
    options[:omit_nofollow] = true if omit_nofollow?

    if self.with_secure_media?
      each_upload_url do |url|
        uri = URI.parse(url)
        if FileHelper.is_supported_media?(File.basename(uri.path))
          raw = raw.sub(
            url, Rails.application.routes.url_for(
              controller: "uploads", action: "show_secure", path: uri.path[1..-1], host: Discourse.current_hostname
            )
          )
        end
      end
    end

    cooked = post_analyzer.cook(raw, options)

    Rails.logger.info("Cooked into: #{cooked}")

    new_cooked = Plugin::Filter.apply(:after_post_cook, self, cooked)

    if post_type == Post.types[:regular]
      if new_cooked != cooked && new_cooked.blank?
        Rails.logger.debug("Plugin is blanking out post: #{self.url}\nraw: #{raw}")
      elsif new_cooked.blank?
        Rails.logger.debug("Blank post detected post: #{self.url}\nraw: #{raw}")
      end
    end

    Rails.logger.info("New cooked into: #{new_cooked}")

    new_cooked
  end

Sortie :

Completed 200 OK in 335ms (Views: 0.4ms | ActiveRecord: 0.0ms | Allocations: 78316)
done
done
Cooking post with raw: ![ferris|690x459](upload://5YA5Y9vjz0iQmn2DErtUBrHCKng.png)
Started POST "/~cs6991/forum/presence/update" for 127.0.0.1 at 2022-09-02 05:55:33 +0000
Processing by PresenceController#update as */*
  Parameters: {"client_id"=>"16308337827949548cb8b156301a493b", "leave_channels"=>["/discourse-presence/reply/7"]}
Completed 200 OK in 19ms (Views: 0.2ms | ActiveRecord: 0.0ms | Allocations: 6182)
done
Cooked into: <p><img src="" alt="ferris" data-base62-sha1="5YA5Y9vjz0iQmn2DErtUBrHCKng" width="690" height="459"></p>
New cooked into: <p><img src="" alt="ferris" data-base62-sha1="5YA5Y9vjz0iQmn2DErtUBrHCKng" width="690" height="459"></p>
done

J’ai réussi à isoler le téléchargement des publications jusqu’à cette ligne exacte :

https://github.com/discourse/discourse/blob/main/app/assets/javascripts/pretty-text/addon/sanitizer.js#L23

en particulier,

  // relative urls
  if (/^\\/[\\w\\.\\-]+/i.test(href)) {
    return href;
  }

ne fonctionne pas car l’URL de mon forum étant servie depuis un répertoire web d’utilisateur Apache (sur lequel je n’ai aucun contrôle), elle commence par un ~, ce qui casse cette regex.
J’ai confirmé que la modification de la classe de caractères pour inclure ~ (comme [\\w\\.\\-~]) corrige le téléchargement des publications, mais les téléchargements d’avatars, douloureusement, ne fonctionnent toujours pas !

1 « J'aime »

…et l’autre regex cassée :

même problème – a besoin d’un ~ dans la classe de caractères
ce qui corrige mes avatars

1 « J'aime »

Si Core n’est pas très enthousiaste à l’idée d’une PR, vous pourriez probablement résoudre ce problème de manière permanente avec un plugin personnalisé pour votre site spécifique.

1 « J'aime »