合并两个 Discourse 站点为一个

如果你有两个你希望合并成一个的 Discourse 站点,本指南就是为你准备的。

有一个名为 discourse_merger 的工具,可以将一个 Discourse 站点合并到另一个站点中。

先决条件

这不是一项简单的任务,应将其视为任何其他迁移到 Discourse 的操作。你不会在实时生产站点上运行 discourse_merger。你将在另一个环境中执行合并,在那里你可以审查输出,然后再将结果迁移到生产环境。

复制与合并

几乎所有内容都会从一个站点复制到另一个站点,但分类(Categories)和用户(Users)可以合并,以避免重复。

  • 如果两个站点上的用户具有相同的电子邮件地址,则用户将被合并。
  • 如果分类具有相同的名称,则分类将被合并。

如果你想对数据进行任何重新组织,请在合并前进行。

选择目标站点

选择哪个站点将作为数据的目标。这将是保留所有样式和设置的站点。另一个站点的用户、分类、主题、帖子、上传等将被复制/合并到目标站点中。

如何操作

备份两个站点*(包括文件)*并将它们复制到你将执行合并的环境中。它们可能来自不同版本的 Discourse,因此我们需要将它们更新到相同的版本。在执行合并时,我建议使用最新版本的 Discourse。

将目标站点恢复到合并环境。如果从命令行执行此操作:

bundle exec ruby script/discourse restore destination-2018-08-02-134227-v2018xxx.tar.gz

接下来我们将解压另一个站点。

cd /path/to/data
tar xvzf other-2018-08-02-134227-v2018xxx.tar.gz

输出将包括数据库转储和上传文件。

使用数据创建一个数据库:

psql
CREATE DATABASE "copyme" ENCODING = 'utf8';
\q
gunzip < /path/to/data/other-2018-08-02-134227-v2018xxx.tar.gz | psql -d copyme

如果你在官方 Docker 容器中运行导入(推荐),你需要重置 postgres 密码并将其提供给脚本,否则你可能会遇到 postgres 用户无法访问数据库的错误。

要更改密码:

sudo -u postgres psql
\password postgres
(输入新密码)
\q

现在是运行脚本的时候了。你需要设置一些环境变量:

DB_NAME:要合并到目标站点的数据库名称。
DB_HOST(可选) 要合并的数据库的主机名。如果它在本地,则留空。
DB_PASSpostgres 用户访问数据库的密码
UPLOADS_PATH:包含 “original” 和 “optimized” 目录的目录的绝对路径(被合并的站点)。例如:/path/to/data/uploads/default
SOURCE_BASE_URL:要合并的站点的基本 URL。例如:https://meta.discourse.org
SOURCE_CDN(可选) 要合并的站点的 CDN 的基本 URL。

在运行导入脚本之前,你可能需要先运行 bundle install 以避免错误。为此:

su discourse -c 'bundle config set --local with generic_import && bundle install'

在第一次运行时,你可能需要为导入所需的 gem 安装一些额外的依赖项。

捆绑完成后,运行导入。

su discourse -c 'DB_NAME=copyme DB_PASS=password SOURCE_BASE_URL=http://copy.othersite.com UPLOADS_PATH=/shared/import/data/uploads/default bundle exec ruby script/bulk_import/discourse_merger.rb'

完成后,在网络浏览器中查看输出。

你可以使用 remap 工具来更新来自旧论坛的链接。

bundle exec ruby script/discourse remap 'copy.othersite.com' 'hot.newsite.com'

还要重新烘焙所有包含上传的帖子:

rake posts:rebake_match["upload:"]

如果一切看起来正常,请备份结果并将其恢复到生产服务器。

bundle exec ruby script/discourse backup
45 个赞

它似乎可以工作,但在我运行备份时,我得到了

pg_dump: error: query failed: ERROR:  permission denied for table migration_mappings

这很奇怪。

编辑:已通过以下方式解决:

ALTER USER discourse WITH SUPERUSER;
1 个赞

最近有人用过这个吗?效果怎么样?

另外,有人知道是否可以自动将用户放入每个来源论坛的组中吗?(以便于他们获得查看他们来自的论坛主题的权限。)

这有点麻烦。我想我不得不注释掉一些东西。合并图像也存在问题。

我想我会将新网站的所有用户添加到某个组中,以便在合并时他们已经在该组中。这比在合并之后或作为合并的一部分进行要容易。

2 个赞

上次我这样做时,合并站点的上传全部丢失了。我从 tar tf backupfile.tar.gz 获取了上传列表,并将它们放入 allfiles.txtx 并将其复制到上传目录。此脚本(很可能需要修改才能在您那里工作)为每个文件创建了一个上传,然后重新烘焙帖子修复了所有(或大多数?)丢失的图像。

def process_uploads
  begin
    # 读取文件名列表
    filenames = File.readlines('/shared/uploads/allfiles.txt').map(&:strip)
    count = 0

    filenames.each do |filename|
      # 在文件名前面加上 /shared
      filename.gsub!(/\.\//,"")
      full_path = File.join('/shared/uploads/default/original/', filename)

      begin
        # 检查路径是否存在且为常规文件(非目录)
        count += 1
        
        if File.exist?(full_path) && File.file?(full_path)
          # 打开文件
          File.open(full_path, 'r') do |tempfile|
            # 使用指定参数创建上传
            u = UploadCreator.new(tempfile, 'imported', {}).create_for(-1)
            puts "#{count} -- #{u.id}: #{u.url}"
          end
        else
          puts "警告:路径未找到或不是常规文件:#{full_path}"
        end
      rescue => e
        puts "处理文件 #{full_path} 时出错:#{e.message}"
        # 即使当前文件失败,也继续处理下一个文件
        next
      end
    end
  rescue Errno::ENOENT
    puts "错误:找不到 files.txt"
  rescue => e
    puts "读取 files.txt 时出错:#{e.message}"
  end
end

# 执行处理
process_uploads;

我通过以下方式获取了错误的帖子:

 bad=Post.where("cooked like '%/images/transparent.png%'")

然后用这个来标记它们需要重新烘焙:

bad.update_all(baked_version: nil)

我没耐心了,所以用了

rake posts:rebake_uncooked_posts

来重新烘焙它们。

2 个赞

我想知道是否可以将我想合并的 Discourse 论坛转换为 XenForo(他们的导入器通常很出色),然后将其与我想合并的其他论坛合并(这些论坛本身将从 vBulletin 转换为 XenForo),最后再将新合并的 XenForo 论坛导入 Discourse 是否会更容易。