埋め込みが機能しない

こんにちは、埋め込みガイド に従って、Web サイトに Discourse コメントを埋め込もうとしているのですが、行き詰まってしまいました :frowning:

症状

Firefox と Chrome の両方で試しました。どちらの場合も、「Loading Discussion…」という Discourse の iframe が読み込まれますが、そこで停止し、開発者コンソールに繰り返し JavaScript エラーが表示されます。

Firefox では、X-Frame-Options に関するエラーが発生します:

Invalid X-Frame-Options header was found when loading "https://discourse.29th.local/embed/comments?embed_url=https%3A%2F%2Fpersonnel.29th.local%2F%23enlistments%2F11927": "ALLOWALL" is not a valid directive.

その後、embed-application.js:7 で DOMException エラーが発生します:

Uncaught DOMException: An invalid or illegal string was specified

これらの 2 つのエラーは約 30 秒ごとに繰り返されます。ネットワークタブには失敗したリクエストはありません。

Chrome では X-Frame-Options エラーは発生しませんが、数秒後に「ターゲットオリジンが受信ウィンドウのオリジンと一致しない」というエラーが発生します:

Failed to execute 'postMessage' on 'DOMWindow': The target origin provided ('https://discourse.29th.local') does not match the recipient window's origin ('https://personnel.29th.local').

メタ(meta)ではこのエラーに関するトピックを多数見かけ、すべてのトラブルシューティング手順を試しましたが、効果はありませんでした。

私の環境設定

Mac での Discourse 設定ガイド に従いましたが、少し例外があります。Postgres、Redis、Mailcatcher をラップトップにグローバルにインストールする代わりに、これらを Docker コンテナで実行し、ポートを外部に公開しています。Discourse は、これらが Docker コンテナではなくベアメタル上で実行されているとは知りません。Rails/Discourse はグローバルにインストールされており、Docker コンテナ内では実行されていません。

完全に別のことですが、カスタム Web アプリケーションは Docker Compose スタック内で実行されています。そのスタックの一部には nginx サーバーが含まれており、personnel.29th.local を適切なアップストリームコンテナへ、discourse.29th.localhost.docker.internal:3000(Docker コンテナからホストの localhost に到達するために使用できるマジックホスト名)へルーティングします。

(後述しますが、nginx レイヤーを除外しても同じエラーが発生しました)

ここで考えられる落とし穴は、私の Web アプリが JavaScript のシングルページアプリケーション(SPA)であることです。Discourse コメントが埋め込まれているページは https://personnel.29th.local/#enlistments/1234 で、サーバーサイドレンダリングは行われていません。これが問題であれば、クローラーに関連するエラーが発生すると予想されます。その場合は、Discourse が私のアプリをクローリングするのではなく、単にリンクを貼るだけでも構いません。しかし、表示されているエラーはクローリングの失敗とは関係ないように見えます。

トラブルシューティング

管理 > カスタマイズ > 埋め込み で埋め込み可能なホストを personnel.29th.local に設定しました。最初は、サンプルの埋め込みコードの discourseUrlhttp://localhost:3000/ として表示されていたため、rails console を起動して以下を実行しました:

SiteSetting.force_hostname = "discourse.29th.local"
SiteSetting.port = 443

さらに、管理ダッシュボードで「HTTPS を強制する」をオンにしました。これでサンプルの埋め込みコード内の URL が修正されました。

また、設定の cors origins セクションに https://personnel.29th.local を CORS ドメインとして追加しました。

現在は、以下のコマンドで Discourse を起動しています:

DISCOURSE_DEV_HOSTS=discourse.29th.local,host.docker.internal DISCOURSE_ENABLE_CORS=true bundle exec rails server

さらに、設定ダッシュボードでコンテンツセキュリティポリシー(CSP)を無効にする試みもしました。

https://discourse.29th.local/logs/ を確認しましたが、エラーや Sidekiq に関する記述は見当たりませんでした。

Sidekiq についてですが、管理ダッシュボードに更新に関するメッセージが表示されています:

A check for updates has not been performed. Ensure sidekiq is running.

そこで、rails console で Sidekiq.redis { |r| puts r.flushall } を実行し、OK が返ってきたため、rails サーバーを再起動しましたが、メッセージも全体の状況も変わりませんでした。Redis キャッシュをいろいろ探しましたが、このページに関する記述は見つかりませんでした。

また、nginx レイヤーを除外して状況を単純化しようと試みました:SiteSetting.force_hostnameSiteSetting.portnil に戻し、HTTPS 強制をオフにし、Web アプリと Discourse に localhost でアクセスし、Web アプリを Discourse の埋め込み可能なホストと CORS ホスト名(http://localhost:8080)に追加しましたが、ホスト名が異なるだけで同じエラーが発生しました:

Failed to execute 'postMessage' on 'DOMWindow': The target origin provided ('http://localhost:3000') does not match the recipient window's origin ('http://localhost:8080').

使用しているバージョンは 2.6.0.beta6 ( 60bc38e6a8 ) です。これは、Discourse for Mac 設定ガイドに従って数週間前に master ブランチをクローンし、今日 git pull origin master を実行して取得したものです。

また、tmp ディレクトリを削除してサーバーを再起動しました。

散歩に行き、枕に向かって叫び、机の下で泣いてみました。

これで網羅できているはずです。誰か助けてください!

お困りとのこと、お察しします。

Discourse は SPA をクローリングするほどの賢さを持っていませんので、これが最も怪しい原因だと考えられます。静的コンテンツを含むサイトでもう一度再現を試みていただけますか?

あらゆるカスタム環境をサポートすることは不可能ですので、まずは構成を簡素化して動作を確認し、その後で段階的に機能を追加していくことをお勧めします。

「いいね!」 3

ご返信ありがとうございます。心配ご無用です、きっと価値あるものになると思います!

シンプルにするために、サーバー側でレンダリングする(Rails)サイトを使用し、nginx レイヤーは完全に除外しました。アプリはポート 3001 で、Discourse はポート 3000 で実行しています。

埋め込みコードは以下のようになっています:

<script type="text/javascript">
      DiscourseEmbed = { discourseUrl: 'http://localhost:3000/',
                         discourseEmbedUrl: 'http://localhost:3001/enlistments/1' };
    
      (function() {
        var d = document.createElement('script'); d.type = 'text/javascript'; d.async = true;
        d.src = DiscourseEmbed.discourseUrl + 'javascripts/embed.js';
        (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(d);
      })();
</script>

埋め込み可能なホストには localhost:3001 を、管理 > カスタマイズ > 埋め込み に追加し、ホスト名には http://localhost:3001管理 > 設定 > cors に追加しました。

エラーは同じですが、ホスト名が更新されています:

Failed to execute 'postMessage' on 'DOMWindow': The target origin provided ('http://localhost:3000') does not match the recipient window's origin ('http://localhost:3001').

スタックはこれ以上シンプルなものはないと思います :thinking: つまり、何らかの設定問題だと思われますよね?何かアイデアはありますか?

いくつかの追加のデバッグ結果をお伝えします:

手動でトピックを作成し、JavaScript スニペット内の discourseEmbedUrl: 'http://localhost:3001/enlistments/<%= @enlistment.id %>'topicId: 14 に置き換えてみました。するとコメントが読み込まれました。これは CORS や X-Frame- の問題ではなく、(a) スクレイピングに関連する何か、そして (b) 埋め込み時のエラーハンドリングの問題である可能性を示唆しています。

スクレイピングの問題を調査するため、以前アクセスしたことがない新しいページに移動しました(そのため、そのページに対するスクレイピングは試行されていません)。アプリの Rails コンソールを観察しながらページをロードしました。ログに /enlistments/6 が 1 回表示されました。JavaScript コンソールでエラーメッセージが表示されるまで待機しましたが、その時点で Discourse がスクレイピングを試みるはずだったにもかかわらず、アプリの Rails コンソールにはそれ以上のアクセス試行のログは表示されませんでした。

Discourse の /logs エンドポイントにもエラーはなく、Discourse の Rails ログからも何も確認できませんでした。

Discourse が私のウェブサイトにアクセスできないのではないかと思い、Discourse アプリの Rails コンソールにログインして以下を実行しました:

± |master U:3 ?:2 ✗| → rails c
Loading development environment (Rails 6.0.3.3)
[1] pry(main)> require "net/http"
=> false
[2] pry(main)> url = URI.parse("http://localhost:3001/enlistments/6")
=> #<URI::HTTP http://localhost:3001/enlistments/6>
[3] pry(main)> req = Net::HTTP.new(url.host, url.port)
=> #<Net::HTTP localhost:3001 open=false>
[4] pry(main)> res = req.request_head(url.path)
4=> #<Net::HTTPOK 200 OK readbody=true>
[5] pry(main)>

この操作をしている間、アプリの Rails サーバーのアクセスログが表示されました。これにより、Discourse が私のアプリに到達できることが確認できました。

今は Sidekiq やジョブスケジューリングに問題があるのではないかと思っています🤷‍♂️ しかし、そのデバッグ方法がわかりません。Sidekiq を使ったことがないためです。

Redis データを再度確認しました(Redis に対応したデータベース GUI の TablePlus を使用)。keydefault:logster-env-96404aef1da0c422fc32e3bb82d85fbc のような値で、value が以下のような約 3 行のデータが表示されました:

[
  {
    "hostname": "myhostname",
    "process_id": 7188,
    "application_version": "60bc38e6a8914a10341a32ff9909e69faa65ffef",
    "params": {
      "embed_url": "http: //localhost:3001/enlistments/11927"
    },
    "HTTP_HOST": "localhost:3000",
    "REQUEST_URI": "/embed/comments?embed_url=http%3A%2F%2Flocalhost%3A3001%2Fenlistments%2F11927",
    "REQUEST_METHOD": "GET",
    "HTTP_USER_AGENT": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.60 Safari/537.36",
    "HTTP_ACCEPT": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
    "HTTP_REFERER": "http://localhost:3000/embed/comments?embed_url=http%3A%2F%2Flocalhost%3A3001%2Fenlistments%2F11927",
    "time": 1606253787041
  }
]

typeLIST で、ttl-1 です。これはジョブが開始されていることを意味するのでしょうか?

/sidekiq を調べてみましたが、このジョブに関する言及や RetrieveTopic という名前のキューは見当たりませんでした :frowning:

確かに範囲は狭まってきましたが、何か思い当たる節があればお手伝いいただきたいです!

@evitrout さん、設定を基本構成に簡素化した今、さらにトラブルシューティングを進めるためのアイデアはありますか?

ほぼ間違いなく設定の問題です。localhost:3000 の Discourse が異なるホスト名を持っていると考えている可能性があります。コンソールで以下を確認できます:

Discourse.base_url

もう一つ確認すべき点は、/sidekiq にある sidekiq のログです。

@eviltrout TopicRetriever ライブラリにデバッグ用の行を追加し、invalid_url?false(つまり有効である)であることを確認しました。Discourse.base_url は確かに http://localhost:3000 に設定されています。RetrieveTopic ジョブがどこかで静かに失敗していると考えており、その原因を特定しようとしています。/logs にはエラーログは存在せず、/sidekiq にもトピックの取得に関する参照やログは一切ありません。

申し訳ありませんが、現時点では思いつきません。コードは現在本番環境で動作しているため、環境、プラグイン、またはセットアップに関する問題があると考えられます。

「いいね!」 1

こんにちは、調査ありがとうございます。私にも全く同じ問題が発生していると思います(既存のトピックでは埋め込みが機能しますが、トピック作成時には失敗します)。これは本番環境では問題なく動作するのですが、開発マシンでは動作しません。

私の環境は Docker スタックで構成されており、Discourse と Sidekiq の両方からすべてが可視化されていることを確認しました。現時点では、Discourse が URL を解析する際(ポスト内のリンクのプレビューを Onebox が取得しようとする際にも失敗します)、外部サービスに依存しており、それがローカルインスタンスを認識できないことが原因ではないかと考え始めています。そのようなことはあり得るでしょうか?

@wilson29thid さん、その後何か手がかりは見つかりましたか?

本番環境のマシンで同じ問題が発生しました。開発環境のマシンではありません。どちらを使っても本質的に変わらないような気がします。

「いいね!」 1

いいえ、残念ながら私はこの問題は解決できず、最終的にはDiscourseのAPIを使って手動でトピックを作成することになりました😔

「いいね!」 1

それ、私も実際にやってみるかもしれませんね。良い点は、記事へのコメントが始まる前に、作成されるスレッドの数を制御できることです。