Embeds: التحميل عالق أو الموضوع لا يُنشأ ولا توجد سجلات، ما الحل؟

على مدار بضعة أيام، لاحظت أن الكثير من الأشخاص يبحثون وينشرون حلولًا بخصوص تضمينات التعليقات. أنا واحد من هؤلاء الأشخاص. آمل أن يساعد هذا المنشور الآخرين في نفس الموقف.

أنا جديد على Discourse، لذا فإن أي شخص يرغب في إكمال المعلومات التي أقدمها بخبرة متعمقة، فلا تتردد في القيام بذلك.

شيء واحد يمكنني قوله بعد مراجعة العديد من المشاركات حول هذا الموضوع هو أن مصدر المشكلات يمكن أن يكون واسع النطاق. بالنسبة لأولئك الموجودين في موقفي، إليك حل!

المشكلة

  1. تجد نفسك مع تضمين يقول “جارٍ تحميل المناقشة…”
  2. لا يتم إنشاء مواضيع Discourse تلقائيًا

الحل

حاول إضافة نطاقك إلى قائمة المضيفين الداخليين المسموح بها.

إنها إعدادات موقع موجودة في منطقة المسؤول. يمكنك العثور عليها في هذا المسار لموقع Discourse الخاص بك:

/admin/site_settings/category/all_results

رابط مباشر للإعداد الذي أشير إليه سيكون:

/admin/site_settings/category/all_results?filter=allowed_internal_hosts

بالنسبة لأولئك الذين ينظرون إلى وحدة تحكم Rails، ابحث عن:

SiteSetting.allowed_internal_hosts

الإعداد هو قائمة بأسماء النطاقات مفصولة بعلامة (|).

السياق

مثيل Discourse الخاص بي عام، ولكن نظام DNS الداخلي الخاص بي يحل بعض النطاقات محليًا. يمكن أن يحدث هذا في الإعدادات التي تستخدم Docker أو Kubernetes أو أي بيئة بها نظام DNS داخلي.

كوني جديدًا على Discourse، يجب أن أقول إن ما يبدو واضحًا الآن لم يكن واضحًا في البداية.

أولئك منا غير المطلعين على التفاصيل الداخلية لـ Discourse غير مدركين أنه في عام 2017 تم تطبيق حماية SSRF أو حتى تفاصيل تلك الحماية. فقط في المستقبل يتضح الارتباط من هذا الإعلان.

إنها ميزة مطبقة بشكل جيد، ولكنها كانت بمثابة ورطة كبيرة لسبب بسيط للغاية.

ما يجب أن تعرفه

لن يقوم Discourse بإنشاء موضوع لتضمينك إذا كان النطاق يحل إلى عنوان IP محلي.

لا تصرخ بعد أيها الناس. هذا شيء جيد. يمكنك القراءة عن SSRF لمعرفة السبب وشكر مطوري Discourse على أخذ الأمر على محمل الجد.

المشكلة هي أن Discourse لا يوفر ردود فعل لإعلامنا لماذا لا يقوم بإنشاء المواضيع ولماذا عالق عند “جارٍ تحميل المناقشة…”

قراءات إضافية

ولكن، ما هو عنوان IP المحلي بالضبط؟ لأي شخص مهتم، يمكنك العثور على الإجابة مباشرة في كود Discourse، إليك رابط مباشر للملف على GitHub.

على سبيل المثال، إذا كان مثيل Discourse الخاص بك على super-forum[dot]com موجودًا على شبكة تستضيف أيضًا cool-blog[dot]net، فقد يحل نظام DNS الداخلي الخاص بك cool-blog[dot]net كعنوان IP محلي - والذي سيرفضه Discourse ما لم يكن مدرجًا في القائمة المسموح بها.

نأمل أن يوفر هذا المنشور على شخص آخر بضع ساعات من التفكير - وربما حتى بضع شعرات.

إعجابَين (2)

عندما عدت إلى العمل اليوم، لفت انتباهي بعض الأشياء في صفحات المسؤول. إليك بعض الأفكار حول ما يمكن تحسينه.

إعدادات التضمين — /admin/customize/embedding/settings

allowed_internal_hosts هو إعداد حاسم لكي يعمل التضمين بشكل موثوق في البيئات غير العامة. يجب إدراجه صراحة كإعداد ذي صلة في هذا القسم - لا يوجد شك في أهميته.

مضيفو التضمين — /admin/customize/embedding

مقتطف التكوين المقدم أكثر من مفيد لأنه يتضمن الكثير من المعلومات المفيدة. أعتقد أنه يمكننا استخدام ذلك لتقديم إرشادات إضافية.

الفقرة الأولى

الحالي:

الصق كود HTML التالي في موقعك لإنشاء وتضمين مواضيع Discourse. استبدل EMBED_URL بعنوان URL الأساسي للصفحة التي تقوم بتضمينها عليه.

بديل:

الصق HTML التالي حيث تريد ظهور التعليقات على صفحتك.
discourseEmbedUrl هو عنوان URL لصفحتك - الصفحة التي سيتم ربطها من Discourse. عند تحميل صفحتك لأول مرة، سيحاول Discourse العثور على موضوع لهذا العنوان URL أو إنشائه وربطه بمحتواك.

الفقرة الثانية

الحالي:

إذا كنت ترغب في تخصيص النمط، قم بإلغاء التعليق واستبدل CLASS_NAME بفئة CSS محددة في CSS المضمن لموضوعك.

بديل:

استخدم خاصية className لإضافة فئات CSS مخصصة إلى علامة <html> داخل إطار iframe المضمن. لتخصيص النمط، انتقل إلى /admin/customize/themes، وانقر فوق زر Edit لموضوعك، ثم زر Edit Code، وتحقق من Show Advanced. أضف CSS المخصص الخاص بك إلى قسم Embedded CSS.

الفقرة الثالثة

الحالي:

استبدل DISCOURSE_USERNAME باسم مستخدم Discourse للمؤلف الذي يجب أن ينشئ الموضوع. سيقوم Discourse تلقائيًا بالبحث عن المستخدم بواسطة السمة content لعلامات <meta> التي تحتوي على السمة name مضبوطة على discourse-username أو author. تم إهمال المعلمة discourseUserName وسيتم إزالتها في Discourse 3.2.

بديل:

ملاحظة: يتم إنشاء الموضوع بواسطة مستخدم Discourse حقيقي - وليس اسم عرض أو سلسلة مؤلف. يجب أن يكون حسابًا صالحًا وموجودًا. هناك ثلاث طرق لتحديد المستخدم الذي سيتم استخدامه:

  1. الاحتياطي الافتراضي - تم تعيينه في /admin/customize/embedding/posts_and_topics
  2. تجاوز لكل مضيف - تم تعيينه ضمن /admin/customize/embedding/
  3. التحكم لكل عنوان URL - أضف علامة <meta name="discourse-username" content="USERNAME"> إلى صفحتك مع اسم مستخدم Discourse موجود USERNAME

اسم المستخدم لمستخدم Discourse موجود فقط هو الذي سيعمل. سيعود Discourse إلى الافتراضي على مستوى المضيف أو الافتراضي العام إذا لم يتم العثور على مستخدم علامة meta. تسمح طريقة علامة <meta> الموضحة هنا بالتحكم البرمجي لكل عنوان URL في مستخدم Discourse الذي سيتم استخدامه لإنشاء الموضوع. على سبيل المثال، يمكنك تعيين مؤلفي منشورات المدونة على موقعك إلى حسابات مستخدمي Discourse المطابقة.

قسم مقتطف التكوين

من السهل تفويت قسم “مقتطف التكوين” القابل للطي. بصريًا، يشبه عنوانًا، والسهم الخفي ليس بديهيًا. على عكس رابط “Learn More”، الذي يتم تلوينه وجذب الانتباه، يبدو هذا مخفيًا في وضح النهار.

أعلم أن هذا قد يكون قابلاً للنقاش - قد يشعر البعض بأن واجهة المستخدم نظيفة وكافية. لكنني شخصياً فاتني هذا القسم مرات عديدة قبل أن أدرك أنه قابل للنقر. هذا يخبرني أن الإمكانية يمكن تحسينها، حتى لو كان ذلك بشكل طفيف. قد يكون التلميح البصري الأكثر وضوحًا أو افتراضيًا غير مطوي مفيدًا، خاصة للمستخدمين الجدد الذين يعتمدون على الأمثلة.

المقتطف

الحالي:

<div id='discourse-comments'></div>
  <meta name='discourse-username' content='DISCOURSE_USERNAME'>

  <script type="text/javascript">
    DiscourseEmbed = {
      discourseUrl: 'https://discourse.your-site.com/',
      discourseEmbedUrl: 'EMBED_URL',
      // className: 'CLASS_NAME',
    };

    (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>

بديل:

<div id="discourse-comments"></div>
<!-- اختياري: تحديد حساب مستخدم Discourse الذي ينشئ الموضوع -->
<!-- إذا تم حذفه، سيعود Discourse إلى المستخدم الافتراضي لكل مضيف أو العام -->
<meta name="discourse-username" content="DISCOURSE_USERNAME" />

<script type="text/javascript">
  DiscourseEmbed = {
    discourseUrl: 'https://discourse.mydomain.com/', // مطلوب الشرطة المائلة اللاحقة
    discourseEmbedUrl: window.location.href, // أو سلسلة عنوان URL أساسي ثابت
    // className: 'my-iframe-theme another-class',
    // discourseReferrerPolicy: 'strict-origin-when-cross-origin',
    // topicId: '1234',
  };

  (function() {
    const d = document.createElement('script');
    d.type = 'text/javascript';
    d.async = true;
    d.src = `${DiscourseEmbed.discourseUrl}javascripts/embed.js`;
    document.head.appendChild(d);
  })();
</script>

خيارات DiscourseEmbed - أوصاف موجزة

  • discourseUrl - مطلوب
    عنوان URL الكامل لمثيل Discourse الخاص بك. يجب أن ينتهي بشرطة مائلة لاحقة، على سبيل المثال https://discourse.mydomain.com/

  • discourseEmbedUrl - مطلوب
    عنوان URL الكامل للصفحة الحالية التي يتم تضمين التعليقات فيها. هذه هي الطريقة التي يحدد بها Discourse المواضيع ويربطها بمحتواك.

  • className - اختياري
    يضيف فئات CSS مخصصة إلى عنصر <html> داخل إطار iframe. قم بتعريف الأنماط في قسم “Embedded CSS” لموضوع Discourse الخاص بك.

  • discourseReferrerPolicy - اختياري
    الافتراضي هو no-referrer-when-downgrade. انظر: Referrer-Policy

  • topicId - اختياري
    إذا تم تعيينه، سيستخدم Discourse هذا الموضوع مباشرة. وإلا، فسيبحث عن موضوع يطابق discourseEmbedUrl، أو ينشئ واحدًا إذا لم يكن موجودًا.

توثيق سياقي لـ DiscourseEmbed

يجب أن يكون discourseEmbedUrl قابلاً للوصول إليه من خادم Discourse الخاص بك. عند تحميل إطار iframe، يقوم Discourse بجلب الصفحة على عنوان URL هذا لإنشاء الموضوع أو تحديد موقعه. يعمل هذا بسلاسة مع مواقع الويب المستضافة على منصات عامة.

ومع ذلك، إذا كنت تقوم بالتطوير محليًا، فقد يظهر التضمين عالقًا عند “Loading Discussion…” أو لا يظهر على الإطلاق. هذا لأن عناوين URL للتطوير المحلي (مثل localhost) قد يتم حظرها بواسطة حماية SSRF الخاصة بـ Discourse، أو خيار force_https، أو مضيف مضمن مفقود /admin/customize/embedding.

إذا كنت تبني ميزات جديدة لموقع موجود، فإن أحد الحلول هو توجيه discourseEmbedUrl إلى عنوان URL للإنتاج. عندما يتم تمكين embed_any_origin، سيسمح Discourse للتضمين بالعمل حتى لو تم تقديم إطار iframe من أصل مختلف. سيتم تحميل التعليقات إذا كانت موجودة، أو سيتم عرض زر “Continue Discussion”.

بدلاً من ذلك، إذا تم إضافة نطاقك المحلي (مثل localhost) ضمن مضيفي التضمين، فقد لا تحتاج إلى embed_any_origin على الإطلاق. ولكن لا يزال يتعين عليك إضافة localhost كمضيف مضمن /admin/customize/embedding.

:warning: تحذير واحد: إذا تم تمكين إعداد force_https، ولم يكن موقع التطوير الخاص بك يستخدم TLS، فسيفشل التضمين. في هذه الحالة، قم إما بتعطيل force_https أثناء التطوير أو فكر في تشغيل مثيل Discourse منفصل للاختبار.

ملاحظة: إذا كان discourseEmbedUrl متاحًا للجمهور ولا يزال التضمين يعرض “Loading Discussion…” دون إنشاء موضوع، فقد يتم حظر نطاقك بواسطة حماية SSRF الخاصة بـ Discourse.

يحدث هذا غالبًا عندما يعمل مثيل Discourse الخاص بك في بيئة ذات تحليل DNS محلي - مثل Docker أو Kubernetes أو شبكة محلية مع خادم DNS داخلي. في هذه الحالات، قد يحل Discourse عنوان IP المحلي لنطاق موقعك (مثل 127.0.0.1 أو 192.168.x.x) ويعامله على أنه غير آمن.

للسماح بالوصول، أضف نطاقك إلى إعداد الموقع allowed_internal_hosts. هذا يحدد صراحة نطاقك على أنه آمن لجلب البيانات، متجاوزًا تصفية SSRF.

القائمة الكاملة لنطاقات IP المحظورة متاحة في كود مصدر Discourse.

المضيفون الداخليون المسموح بهم - [وصف إعداد الموقع]((/admin/customize/embedding/settings`)

الحالي:

قائمة بالمضيفين الداخليين التي يمكن لـ Discourse الزحف إليها بأمان لأغراض oneboxing وأغراض أخرى

بديل:

يسمح لـ Discourse بالزحف إلى المضيفين الذين يحلون إلى عناوين IP الداخلية. مطلوب إذا كان موقعك يعمل خلف DNS محلي (مثل Docker أو LAN أو Kubernetes). مطلوب لتضمينات التعليقات وإنشاء المواضيع و oneboxing عندما تمنع حماية SSRF الوصول بخلاف ذلك.

اعتبارات

قد تكون بعض هذه الاقتراحات مناسبة بشكل أفضل كروابط للتوثيق الرسمي. في الواقع، قد يكون هذا المنشور وحده كافياً، حيث يجب فهرسته مثل أي منشور آخر. قد تستدعي اقتراحات أخرى طلب سحب (pull request) مناسب - والذي قد أقوم به في النهاية، ولكن ليس اليوم.

ومع ذلك، قد تكون هناك أخطاء تقنية فيما كتبته. معظمها يأتي من الخبرة العملية، ولكن ربما أسأت تفسير سلوك ما، أو تم خداعي بواسطة التخزين المؤقت (آه، التخزين المؤقت…)، أو ببساطة أغفلت شيئًا ما. في هذا الصدد، أنا أعتمد على قدامى محاربي Discourse المتمرسين.