Fusionar dos sitios de Discourse en uno

Si tienes dos sitios de Discourse que desearías que fueran uno, esta guía es para ti.

Hay una herramienta llamada discourse_merger que puede tomar un sitio de Discourse y fusionarlo con otro.

Prerrequisitos

Esta no es una tarea fácil, y debe tratarse como cualquier otra migración a Discourse. No ejecutarás discourse_merger en un sitio de producción activo. Realizarás la fusión en otro entorno donde puedas revisar el resultado antes de moverlo a producción.

Copia vs. Fusión

Casi todo se copiará de un sitio al otro, pero las Categorías y los Usuarios se pueden fusionar, lo que evitará la duplicación.

  • Los usuarios se fusionarán si un usuario en ambos sitios tiene la misma dirección de correo electrónico.
  • Las categorías se fusionarán si tienen el mismo nombre.

Si deseas realizar alguna reorganización de tus datos, hazlo antes de fusionar.

Elige el sitio de destino

Elige qué sitio será el destino de los datos. Este conservará todo su estilo y configuración. El otro sitio tendrá sus usuarios, categorías, temas, publicaciones, subidas, etc., copiados/fusionados en el sitio de destino.

Cómo hacerlo

Haz copias de seguridad de ambos sitios incluyendo los archivos y cópialas al entorno donde realizarás la fusión. Es posible que provengan de diferentes versiones de Discourse, por lo que necesitamos que estén en la misma versión. Yo elegiría usar la versión más reciente de Discourse mientras realizo la fusión.

Restaura el sitio de destino en el entorno de fusión. Si lo haces desde la línea de comandos:

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

A continuación, extraeremos el otro sitio.

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

El resultado incluirá el volcado de la base de datos y los archivos de subida.

Crea una base de datos con los datos:

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

Si estás ejecutando la importación en un contenedor Docker oficial (recomendado), deberás restablecer la contraseña de postgres para proporcionarla al script, de lo contrario, podrías encontrar un error de que el usuario postgres no puede acceder a la base de datos.

Para cambiar la contraseña:

sudo -u postgres psql
\password postgres
(introduce la nueva contraseña)
\q

Ahora es el momento de ejecutar el script. Algunas variables de entorno que configurarás son:

DB_NAME: nombre de la base de datos que se está fusionando en el sitio de destino.
DB_HOST: (opcional) nombre de host de la base de datos que se está fusionando. Déjalo en blanco si es local.
DB_PASS: contraseña para que el usuario postgres acceda a la base de datos
UPLOADS_PATH: ruta absoluta (del sitio que se fusiona) del directorio que contiene los directorios “original” y “optimized”. Ej: /path/to/data/uploads/default
SOURCE_BASE_URL: URL base del sitio que se está fusionando. Ej: https://meta.discourse.org
SOURCE_CDN: (opcional) URL base de la CDN del sitio que se está fusionando.

Es posible que debas ejecutar un bundle install antes de ejecutar el script de importación para evitar errores. Para hacerlo:

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

En la primera ejecución, es posible que debas instalar algunas dependencias adicionales para los gems requeridos en la importación.

Una vez que el bundle esté completo, ejecuta la importación.

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'

Cuando termine, revisa el resultado en un navegador web.

Puedes usar la herramienta remap para actualizar enlaces del foro antiguo.

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

También rebakea todas las publicaciones con subidas:

rake posts:rebake_match["upload:"]

Si todo tiene buen aspecto, haz una copia de seguridad del resultado y restáurala en tu servidor de producción.

bundle exec ruby script/discourse backup
45 Me gusta

Parece que funciona, pero cuando ejecuto una copia de seguridad, obtengo

pg_dump: error: la consulta falló: ERROR:  permiso denegado para la tabla migration_mappings

Eso es muy extraño.

EDITAR: resuelto con

ALTER USER discourse WITH SUPERUSER;
1 me gusta

¿Alguien ha usado esto recientemente? ¿Cómo fue?

Además, ¿alguien sabe si es posible poner automáticamente a los usuarios en un grupo para cada foro de origen? (Para facilitarles el otorgamiento de permisos para ver los temas de los foros de los que proceden).

Estuvo un poco complicado. Creo que tuve que comentar algunas cosas. También hubo un problema con las imágenes de la fusión.

Creo que agregaría a todos los usuarios del nuevo sitio a algún grupo para que estuvieran en ese grupo cuando los fusionaras. Esto sería más fácil que hacerlo después o como parte de la fusión.

2 Me gusta

La última vez que hice esto, las cargas del sitio fusionado faltaban por completo. Obtuve una lista de las cargas de tar tf backupfile.tar.gz y las puse en allfiles.txtx y las copié al directorio de cargas. Este script (que probablemente no funcionará para ti sin modificaciones) creó una carga para cada uno de esos archivos, por lo que luego volver a hornear las publicaciones arregló todas (¿o la mayoría?) de las imágenes faltantes.

def process_uploads
  begin
    # Lee la lista de nombres de archivo
    filenames = File.readlines('/shared/uploads/allfiles.txt').map(&:strip)
    count = 0

    filenames.each do |filename|
      # Agrega /shared al principio del nombre del archivo
      filename.gsub!(/\.\//,"")
      full_path = File.join('/shared/uploads/default/original/', filename)

      begin
        # Comprueba si la ruta existe y es un archivo regular (no un directorio)
        count += 1
        
        if File.exist?(full_path) && File.file?(full_path)
          # Abre el archivo
          File.open(full_path, 'r') do |tempfile|
            # Crea la carga usando los parámetros especificados
            u = UploadCreator.new(tempfile, 'imported', {}).create_for(-1)
            puts "#{count} -- #{u.id}: #{u.url}"
          end
        else
          puts "Advertencia: Ruta no encontrada o no es un archivo regular: #{full_path}"
        end
      rescue => e
        puts "Error al procesar el archivo #{full_path}: #{e.message}"
        # Continúa con el siguiente archivo incluso si el actual falla
        next
      end
    end
  rescue Errno::ENOENT
    puts "Error: No se pudo encontrar files.txt"
  rescue => e
    puts "Error al leer files.txt: #{e.message}"
  end
end

# Ejecuta el procesamiento
process_uploads;

Obtuve las publicaciones incorrectas así:

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

y luego esto para marcarlas como que necesitan ser horneadas de nuevo:

bad.update_all(baked_version: nil)

Estaba impaciente, así que usé

rake posts:rebake_uncooked_posts

para volver a hornearlas.

2 Me gusta

Me pregunto si no sería más fácil convertir el foro de Discourse que quiero fusionar a XenForo (sus importadores suelen ser excelentes) y luego fusionarlo con los otros foros que quiero que formen parte de la fusión (que a su vez se convertirán a XenForo desde vBulletin) y luego finalmente importar el nuevo foro de XenForo fusionado a Discourse.