Embeds: carga atascada o tema no creado y sin logs, ¡qué hacer!

A lo largo de unos días, noté que mucha gente ha buscado y publicado soluciones sobre los embeds de comentarios. Yo soy una de esas personas. Espero que esta publicación ayude a otros en la misma situación.

Soy nuevo en Discourse, así que cualquiera que desee complementar la información que estoy proporcionando con experiencia profunda, siéntase libre de hacerlo.

Una cosa que puedo decir después de revisar muchas publicaciones sobre el tema es que la fuente de los problemas puede ser muy variada. Para aquellos en mi situación, ¡aquí hay una solución!

Problema

  1. Te encuentras con un embed que dice “Cargando discusión…”
  2. Los temas de Discourse no se están creando automáticamente.

Solución

Intenta agregar tu dominio a la lista de hosts internos permitidos.

Es una configuración del sitio que se encuentra en el área de administración. Puedes encontrarla en esta ruta de tu sitio de Discourse:

/admin/site_settings/category/all_results

Un enlace directo a la configuración a la que me refiero sería:

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

Para aquellos que miran la consola de Rails, busquen:

SiteSetting.allowed_internal_hosts

La configuración es una lista de nombres de dominio separados por una barra vertical (|).

Contexto

Mi instancia de Discourse es pública, pero mi DNS interno resuelve algunos dominios localmente. Esto puede suceder en configuraciones que usan Docker, Kubernetes o cualquier entorno con DNS interno.

Siendo nuevo en Discourse, debo decir que lo que ahora parece obvio, realmente no lo era al principio.

Aquellos de nosotros que no estamos familiarizados con los internos de Discourse no sabemos que en 2017 se implementó la protección SSRF o incluso los detalles de esa protección. Solo en retrospectiva ese anuncio aclara la conexión.

Es una característica bien implementada, pero fue un verdadero laberinto por una razón muy simple.

Lo que debes saber

Discourse no creará un tema para tu embed si el dominio se resuelve a una IP local.

No grites todavía, amigos. Esto es algo bueno. Puedes leer sobre SSRF para averiguar por qué y también agradecer a los desarrolladores de Discourse por tomarlo en serio.

El problema es que Discourse no proporciona comentarios para informarnos por qué no está creando los temas y por qué se queda atascado en “Cargando discusión…”

Lectura Adicional

Pero, ¿qué es exactamente una IP local? Para cualquiera que esté interesado, puede encontrar la respuesta directamente en el código de Discourse, aquí hay un enlace directo al archivo en GitHub.

Por ejemplo, si tu instancia de Discourse en super-forum[punto]com vive en una red que también alberga cool-blog[punto]net, tu DNS interno podría resolver cool-blog[punto]net como una IP local, que Discourse rechazará a menos que esté en la lista blanca.

Con suerte, esta publicación le ahorrará a alguien más unas horas de rascarse la cabeza y tal vez incluso algunos cabellos.

2 Me gusta

Al volver al trabajo hoy, algunas cosas me llamaron la atención en las páginas de administración. Aquí hay algunas ideas sobre lo que se podría mejorar.

Configuración de Incrustación — /admin/customize/embedding/settings

allowed_internal_hosts es una configuración crucial para que la incrustación funcione de manera confiable en entornos no públicos. Debería enumerarse explícitamente como una configuración relacionada en esta sección; no hay duda de su importancia.

Hosts de Incrustación — /admin/customize/embedding

El fragmento de configuración proporcionado es más que útil, ya que incluye mucha información valiosa. Creo que podemos usarlo para brindar orientación adicional.

Primer párrafo

*Actual: *

Pegue el siguiente código HTML en su sitio para crear e incrustar temas de Discourse. Reemplace EMBED_URL con la URL canónica de la página en la que lo está incrustando.

Alternativa:

Pegue el siguiente HTML donde desee que aparezcan los comentarios en su página.
La discourseEmbedUrl es la URL de su página, la que se vinculará desde Discourse. Cuando su página se cargue por primera vez, Discourse intentará encontrar o crear un tema para esa URL y vincularlo a su contenido.

Segundo párrafo

Actual:

Si desea personalizar el estilo, descomente y reemplace CLASS_NAME con una clase CSS definida en el CSS incrustado de su tema.

Alternativa:

Use la propiedad className para agregar clases personalizadas a la etiqueta <html> dentro del iframe incrustado. Para estilizarlo, vaya a /admin/customize/themes, haga clic en el botón Editar de su tema, luego en el botón Editar código y marque Mostrar avanzado. Agregue su CSS personalizado a la sección CSS incrustado.

Tercer párrafo

Actual:

Reemplace DISCOURSE_USERNAME con el nombre de usuario de Discourse del autor que debería crear el tema. Discourse buscará automáticamente al usuario por el atributo content de las etiquetas <meta> con el atributo name establecido en discourse-username o author. El parámetro discourseUserName ha sido obsoleto y se eliminará en Discourse 3.2.

Alternativa:

Nota: El tema es creado por un usuario real de Discourse, no por un nombre para mostrar o una cadena de autor. Debe ser una cuenta válida y existente. Hay tres formas de determinar qué usuario se utiliza:

  1. Fallo de respaldo predeterminado — establecido en /admin/customize/embedding/posts_and_topics
  2. Anulación por host — establecido en /admin/customize/embedding/
  3. Control por URL — agregue una etiqueta <meta name="discourse-username" content="USERNAME"> a su página con un USERNAME de Discourse existente

Solo funcionará el nombre de usuario de un usuario existente de Discourse. Discourse recurrirá al valor predeterminado a nivel de host o global si no se encuentra el usuario de la etiqueta meta. El método de etiqueta <meta> ilustrado aquí permite el control programático por URL sobre qué usuario de Discourse se utiliza para crear el tema. Por ejemplo, puede mapear autores de publicaciones de blog en su sitio a cuentas de usuario de Discourse coincidentes.

La sección Fragmento de Configuración

La sección colapsable “Fragmento de Configuración” es fácil de pasar por alto. Visualmente, se asemeja a un encabezado, y la sutil flecha no es intuitiva. A diferencia del enlace “Más información”, que tiene color y llama la atención, este se siente oculto a plena vista.

Sé que esto puede ser debatible; algunos pueden sentir que la interfaz de usuario es limpia y suficiente. Pero personalmente me perdí esta sección demasiadas veces antes de darme cuenta de que era clicable. Eso me dice que la usabilidad podría mejorarse, aunque sea ligeramente. Una señal visual más clara o un valor predeterminado no colapsado podrían ser de gran ayuda, especialmente para los recién llegados que dependen de ejemplos.

El Fragmento

Actual:

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

Alternativa:

<div id="discourse-comments"></div>
<!-- Opcional: especificar qué cuenta de usuario de Discourse crea el tema -->
<!-- Si se omite, Discourse recurre al usuario predeterminado por host o global -->
<meta name="discourse-username" content="DISCOURSE_USERNAME" />

<script type="text/javascript">
  DiscourseEmbed = {
    discourseUrl: 'https://discourse.mydomain.com/', // Se requiere barra final
    discourseEmbedUrl: window.location.href, // O una cadena de URL canónica codificada
    // 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>

Opciones de DiscourseEmbed — descripciones cortas

  • discourseUrlRequerido
    La URL completa de su instancia de Discourse. Debe terminar con una barra final, por ejemplo, https://discourse.mydomain.com/

  • discourseEmbedUrlRequerido
    La URL completa de la página actual donde se están incrustando los comentarios. Así es como Discourse identifica y vincula los temas a su contenido.

  • classNameOpcional
    Agrega clases CSS personalizadas al elemento <html> dentro del iframe. Defina estilos en la sección “CSS incrustado” de su tema de Discourse.

  • discourseReferrerPolicyOpcional
    Predeterminado a no-referrer-when-downgrade. Ver: Referrer-Policy

  • topicIdOpcional
    Si se establece, Discourse utilizará este tema directamente. De lo contrario, buscará un tema que coincida con discourseEmbedUrl, o creará uno si no existe.

Documentación contextual de DiscourseEmbed

La discourseEmbedUrl debe ser accesible para su servidor Discourse. Cuando se carga el iframe, Discourse recupera la página en esa URL para crear o localizar el tema. Esto funciona sin problemas para sitios web alojados en plataformas públicas.

Sin embargo, si está desarrollando localmente, la incrustación puede parecer atascada en “Cargando discusión…” o no aparecer en absoluto. Esto se debe a que las URL de desarrollo local (como localhost) pueden ser bloqueadas por la protección SSRF de Discourse, o la opción force_https, o un host incrustable faltante.

Si está creando nuevas funciones para un sitio existente, una solución alternativa es apuntar discourseEmbedUrl a la URL de producción. Cuando embed_any_origin está habilitado, Discourse permitirá que la incrustación funcione incluso si el iframe se sirve desde un origen diferente. Los comentarios se cargarán si existen, o se mostrará un botón “Continuar discusión”.

Alternativamente, si su dominio local (por ejemplo, localhost) se agrega en Hosts de Incrustación, es posible que no necesite embed_any_origin en absoluto. Pero aún necesita agregar localhost como un host incrustable.

:warning: Una advertencia: si la configuración force_https está habilitada y su sitio de desarrollo no utiliza TLS, la incrustación fallará. En este caso, desactive force_https durante el desarrollo o considere iniciar una instancia de Discourse separada para pruebas.

Nota: Si discourseEmbedUrl es accesible públicamente y la incrustación todavía muestra “Cargando discusión…” sin crear un tema, su dominio puede estar bloqueado por la protección SSRF de Discourse.

Esto a menudo sucede cuando su instancia de Discourse se ejecuta en un entorno con resolución DNS local, como Docker, Kubernetes o una LAN con un servidor DNS interno. En estos casos, Discourse puede resolver el dominio de su sitio a una dirección IP local (por ejemplo, 127.0.0.1 o 192.168.x.x) y tratarlo como inseguro.

Para permitir el acceso, agregue su dominio a la configuración del sitio allowed_internal_hosts. Esto marca explícitamente su dominio como seguro para recuperar, omitiendo el filtrado SSRF.

La lista completa de rangos de IP bloqueados está disponible en el código fuente de Discourse.

Hosts Internos Permitidos — [Descripción de la Configuración del Sitio]((/admin/customize/embedding/settings`)

Actual:

Una lista de hosts internos que Discourse puede rastrear de forma segura para oneboxing y otros propósitos

Alternativa:

Permite a Discourse rastrear hosts que se resuelven a IPs internas. Necesario si su sitio se ejecuta detrás de DNS local (por ejemplo, Docker, LAN, Kubernetes). Requerido para incrustaciones de comentarios, creación de temas y oneboxing cuando la protección SSRF de lo contrario bloquearía el acceso.

Consideraciones

Algunas de estas sugerencias podrían ser más adecuadas como enlaces a la documentación oficial. De hecho, esta publicación por sí sola podría cumplir esa función, ya que debería indexarse como cualquier otra. Otras podrían justificar una solicitud de extracción adecuada, que quizás termine haciendo, pero no hoy.

Dicho esto, puede haber imprecisiones técnicas en lo que he escrito. La mayor parte proviene de la experiencia práctica, pero podría haber malinterpretado un comportamiento, haber sido engañado por el almacenamiento en caché (oh, el almacenamiento en caché…) o simplemente haber pasado algo por alto. En ese sentido, me remito a los veteranos experimentados de Discourse.