在不同 Discourse 站点间同步/跨发主题

我目前在一个站点上有一个主题,我也想在另一个站点上拥有它。虽然我可以超链接它,但我真正想要的是能够在一个站点上编辑它,并在两个站点上都反映出更改。这可以避免我有一个版本的主题远远过时,而是让两个主题始终保持最新。它还为我提供了一种分散网站信息的方法。

关于功能的一些想法

  • 作为起点,一个站点将继续作为主题的托管/所有者,而另一个(或多个)站点将基本上镜像它。更进一步,我想知道如果原始主题被删除,镜像主题是否可以被继承。
  • 主题应保留在镜像站点上隐藏、关闭等功能。
  • 回复应同步——每个站点都有不同的用户群,所以我无法想象同步回复如何工作。

我知道实现这样的功能绝非易事,但我想知道这是否曾经被研究过,以及已经存在哪些结果/实验?

2 个赞

在多个站点上拥有重复内容是搜索引擎优化(SEO)的大忌。这不太可能得到支持。

用例或目的是什么?你基本上是想让其他站点成为主站点的备份吗?

2 个赞

您能提供更多关于这方面的信息吗?从我的角度来看,这会减少重复内容。

备份不是目标,而是为那些可以出现在多个网站上的主题创建一个单一的真相来源。

举个具体的例子,我正在考虑更换我的签证。我的私人 Discourse 中有一个主题,基本上是一个需要完成的事项清单。但是,我的朋友们也可能觉得这很有用,所以我会在我们共享的 Discourse 上创建相同的主题。问题是,我需要单独同步这两个主题的信息,而不是只更新一个主题。这意味着我经常会有一个主题缺少关键信息。

我猜这甚至可以通过一个指向另一个网站的 api 密钥来实现?也许像编辑器中的一个按钮/部分,可以创建 api 密钥和目标主题的 URL 列表。当你更改源主题时,你可以点击类似“将更改推送到此主题的克隆”的按钮。所有这些都只会将更新推送到其他实例上的主题。

1 个赞

将信息放在一个大家都能看到的地方。链接就是这样做的。

但您有一些秘密信息,想在别处提供。那是另一回事。通过插件完全可以做到。这是那种“权宜之计需要花费实际问题十倍精力”的问题。(例如,我经常花费数小时来自动化一个只需要发生一次的任务。)

但您需要将其放在一个只有发布者才能访问的地方。或者将其设为全站通用?

同样,它们需要是每个用户独有的,并且仅在序列化器中为当前用户(或者也许将其存储在用户配置文件中?)。您还需要某种数据结构来将 API 密钥映射到不同的站点。这似乎是我认为可以在 2-5 小时内完成的事情。

因此,您需要在某处存储要拥有此主题的其他站点的 URL。如何创建该帖子也可能很复杂;最简单的方法是手动创建它,并在源站点中包含该帖子的 URL。您可能可以在某种 BBCode 或类似的东西的原始帖子中存储它。这将允许您创建一个组件,为每个帖子创建按钮和链接,然后您将拥有 Rails 代码来排队一个作业,该作业将尝试将其发布到其他站点。但接收站点不需要任何代码——您可以使用 api 来推送对帖子的编辑。

这似乎是我认为我可以在 5-10 小时内完成的事情,但很可能需要两倍的时间。如果这对您来说很有趣,那将是一个很棒的项目。

4 个赞

这也可以通过一个外部小程序来完成,该小程序可以监听源网站编辑时发送的 webhook,然后使用 API 在镜像网站上发布。

4 个赞

我又在思考这个问题了。

插件可以为源文档的 URL 添加一个自定义主题字段。 (我想,如果主文档需要隐藏,还需要提供远程用户名和 API 密钥字段,但这部分可以稍后处理。或者,它们可以放在用户自定义字段中。生成密钥的人需要确保 API 密钥只有读取权限)。

创建主题时,你可以输入类似“remote: https://meta.discourse.org/t/synchronising-crossposting-topics-across-different-discourse-sites/263269”的内容,主题创建后,Discourse 会拉取远程主题的原始文本,将其插入到 raw 中作为编辑,并使用远程 URL 实例化 topic_custom_field,可能还在顶部添加“复制自 url”。

这样,你就将远程主题复制到了本地,并有了记录。

之后可以有一个“检查源”按钮,它会拉取远程主题,并将远程主题的 updated_at 甚至 raw 保存到其他自定义字段中(也可以通过一个后台任务定期执行此操作,节省用户界面操作)。然后,你可以有一个更新按钮,将现有 raw 替换为远程主题,并作为一次编辑。

如果主站点是公开的,那么这部分就非常容易了。为私有站点添加 API 密钥以进行拉取会使事情复杂化,管理多个站点的一组 API 密钥会使事情更加复杂。如果需要替换原始源,你可以使用 remap rake 任务来完成,或者在需要时添加编辑自定义字段(包含远程 URL)的功能。

这部分是免费的,因为这个解决方案允许二级站点从主站点拉取数据。

是的。并且可以有一个链接回源站点,这样人们就可以去源站点查看这些评论,或者甚至通过 Embed comments from Discourse in your single page app 来嵌入它们。

如果你有任何预算,请随时与我联系。

1 个赞

嘿 Jay,

我考虑这件事已经有一段时间了,直到最近才取得了一些进展。不幸的是,Discourse 到 Discourse 的同步对我来说价值不大(只有 handful of topics),但更重要的是,我希望能将其他平台上的 markdown 文件进行通用同步。

我们公司主要的用例是将 Gitlab 项目中的 readmes 和 wikis 放在 Discourse 中(用于反馈和搜索),同时保持 gitlab 文件作为单一事实来源。由于我缺乏 Ruby 知识,我编写了一个 Python 脚本,虽然实现上有些过度,但功能上是令人满意的。第一个像样的版本也实现了一些你概述的功能。部分功能包括:

  • 包含指向原始来源(gitlab 中的文件)的链接
  • 包含指向特定修订版本(gitlab commit #)的链接
  • 处理图像和图像 URL
    • 从 gitlab 仓库下载图像
    • 将它们上传到 discourse
    • 将原始图像 URL 替换为上传的短 URL
  • 添加一个名为“synced_with_gitlab”的标签,以便于查找所有这些内容

它与 github 的工作方式也差不多。两者在 markdown 的风格上大同小异,这使得它非常流畅。

我很想开源它,但需要看看法律部门怎么说。此外,它仍然是一个有点 hacky 的 Python 混乱。我的初衷是将其转换为一个 Ruby 插件,但需要看看我是否能找到必要的时间。

3 个赞

确实如此。我正在重新尝试这个项目,目前正在考虑创建一个数据库,其中包含:

  1. 每个平台一个表(Discourse 表、Gitlab 表等),以应对可能的细微差别
  2. 每个平台表都支持通过 API 密钥进行 webhook 和轮询
  3. 数据库或 API 密钥加密——目前我正在考虑最好加密整个数据库,并通过脚本和密码短语与其进行交互

Discourse 表将如下所示:

类型 间隔(分钟) 上次运行(分钟) 源域 源帖子ID 源用户 源密钥 目标域 目标帖子ID 目标用户 目标密钥
Webhook - 120 meta.discourse.com 1280952 - - discourse.mysite.com 120 Tris xyz12345
Polling 60 40 meta.discourse.com 1280953 Tris20 12345xyz discourse.mysite.com 121 Tris xyz12345
Polling 60 35 meta.discourse.com 1750968 Tris20 12345xyz discourse.mysite.com 221 Tris xyz12345
Polling 60 40 meta.discourse.com 1123292 Tris20 12345xyz discourse.mysite.com 131 Tris xyz12345
Webhook - 4800 meta.discourse.com 1283678 - - discourse.mysite.com 129 Tris xyz12345

这听起来疯狂且过度设计了吗?我的一部分想法是构建一个合适的解决方案,这意味着要同时考虑这两种可能性——webhook 和轮询。

另外,我将非常感谢有关如何保护数据库内容的建议。目前的想法是用一个密码短语加密数据库,该密码短语必须在启动脚本时作为参数提供,例如:

discourse-sync run password123

仅作为一点启发,webhook 可以非常快速:


这是两个不同实例上的两个主题。左侧的主题是源主题,右侧的主题是目标主题。

有一个 Rails 方法可以加密单个字段。这就是我在仪表板中所做的。

请参阅 Active Record Encryption — Ruby on Rails Guides

同时拥有轮询和 Webhook 似乎是多余的。我认为我会选择一种方法。

1 个赞

我认为 ActivityPub 插件将在不久的将来实现 Discourse 到 Discourse 的联合,如果那也是一种可能性的话?

3 个赞

就我而言,这个想法我也已经出现过很多次了。我不确定我们这里是否已经有专门讨论这个话题的帖子,如果没有的话,我们应该有一个!

1 个赞

理论上同意,但不幸的是,企业环境使事情复杂化:

  1. 由于 IT 政策,公司内部无法使用 webhook(我们由 CDCK 托管在公司外部,并且无法在没有繁琐流程的情况下允许端口转发到外部世界)——因此必须使用 API 版本。
  2. Webhook 对其他人来说很方便、很棒,而且完全合理,因此也应该支持它们 :slight_smile:

所以我在很久以前就实现了这个粗略的版本,效果很好但不可扩展。这是我设计上的一个糟糕之处:我曾尝试使用一个带有 Markdown 表格的主题作为输入。当条目超过 30 个时,它就变得一团糟。

我所看到的 discourse 到 discourse 用例的一部分是:文档(Meta)的单一事实来源同步到其他实例中的相应帖子。这意味着如果团队更改了 Core 并更新了用户文档 Meta,我将在我的实例中拥有该文档的最新版本,供所有用户查找。

对于 V2,我计划使用上面提到的 SQLite 数据库作为输入,并且可能这次用 Rust 而不是 Python 编写。

下面是一个粗略的草图。

graph TB
    A[terminal] -- ~\u003ediscourse-sync run $PASSWORD --\u003e B[Rust Script]
    B -- SQLCipher:Decrypt DB using Password --\u003e C[( sqlite: sources-and-targets')]
    C -- 'Discourse' Table Data --\u003e B
    B -.-\u003e D{Decision on Type}
    D -- Webhook --\u003e E[Listen for Webhook Info]
    D -- Polling --\u003e F[Polling API]
    E --\u003e G[Receive New Information]
    F --\u003e G
    G --\u003e H[Parse and Process Data]
    H --\u003e I[POST\n tgt_domain, tgt_usr, tgt_key, post_id]
    I --'raw' and images--\u003e J[ Target Post ]

    subgraph Rust Script Operations
    B
    D
E
F
G
H
I
    end

非常乐意收到关于此的反馈和建议 :slightly_smiling_face:

1 个赞

@angus 可能对您有帮助,我认为我们已经通过 activity pub 插件解决了其中很多问题。

6 个赞

是的,ActivityPub 插件现在确实支持此功能。我们非常接近在内部使用它来同步 meta 和内部实例之间的文档,事实上,这在我下周的待办事项列表中。

7 个赞

这是否适用于这两种情况?例如,源是公开的,但目标是私有的,这仍然有效吗?

尽管 activitypub 插件看起来很棒,但我担心它可能无法满足私有实例的需求。

它确实适用于私有实例,在 AP 插件的当前版本中,私有实例可以关注公共 Discourse 实例中的类别,因此可以接收来自这些实例的已发布活动。(但私有实例中的内容不会被发布,所以它是一个单向同步,仅从公共到私有。)

3 个赞

所以听起来 ActivityPub 可以这样工作:

公共 –\u003e 公共 :white_check_mark:
公共 –\u003e 私有 :white_check_mark:

私有 –\u003e 公共 :x:
私有 –\u003e 私有 :x:

这在许多情况下确实很有用,例如广播 Meta 的文档。不幸的是,这还不能满足我的一个用例,即从私有实例发布到另一个私有实例。通过研究,我是否可以认为 ActivityPub 插件将来不太可能支持这些用例?在我看来,ActivityPub 的设计初衷是面向公共到公共的。

2 个赞

@Tris20 有趣!感谢您分享您对此的看法和一些细节。

您所描述的正是 ActivityPub 旨在解决(或其中之一)的问题。我不想过多地打击您的积极性,但说实话,您尝试以您描述的方式完成此任务将面临各种挑战。我不会详尽地列出您需要克服的每一个挑战,但为了让您有所了解,ActivityPub 插件已经有近 700 个 rspec 测试,经过一年的开发,它最近才刚刚支持完全的主题到主题同步。

我看不出有任何固有的限制,无法通过 ActivityPub 支持私有到公开和私有到私有发布。问题在于确保在处理私有实例时满足访问和安全方面的考虑。

如果我可以提出一个建议。也许可以考虑如何在 ActivityPub 插件(以及 ActivityPub 标准)已完成的这方面工作的基础上进行构建。实际上,有一个解决方案可以解决 Discourse 到 Discourse 的内容同步问题,并且目前正在运行。它尚未涵盖您的用例,但它解决了您为满足需求而需要解决的大部分问题。

也许您可以考虑一下私有到私有同步在插件中如何工作,即如何处理访问和安全问题?然后,也许您和我可以一起努力,将它作为一个功能添加进来。也许您会达到一个点,觉得在插件(或 ActivityPub 标准)的背景下确实无法实现您想要实现的目标,但您为达到该点所做的工作将与您为独立解决方案所需的工作基本相同,因此不会白费。

ActivityPub 领域有很多聪明人,我不会惊讶于这种类型的问题(即私有发布)是否已经被深入考虑过。您可能会在 SocialHub 上找到一些关于它的先例,这是主要的 ActivityPub 社区论坛(当然是 Discourse),现在正在使用 Discourse ActivityPub 插件 :slight_smile:

7 个赞