Migrate an SMF2 forum to Discourse

우리는 시도해 보았지만 다음과 같은 문제를 겪고 있습니다: 모든 게시글은 가져와졌지만, 주제 제목이 표시되지 않고 외부 이미지가 렌더링되지 않습니다. 현재 SMF2 포럼은: https://forum.mundofotografico.com.br 입니다. 여기 Discourse로 이식을 시도하고 있습니다: https://discourse.fotografos.online - 모든 주제와 적절한 설명이 가져와지지 않았고, 이미지가 로드되지 않습니다… 도움을 부탁드립니다! @marcozambi @miligraf @FireAllianceNX @pfaffman

SMF 마이그레이션 과정을 막 시작했고, 현재 테스트 인스턴스로 시간당 약 1,000개의 게시물을 가져오고 있습니다. MySQL 성능 스크립트에서 MySQL이 ‘ALTER USER’ 명령을 어떤 이유로든 받아들이지 않았을 뿐, 지금까지는 모든 것이 잘 진행되고 있습니다. 수동으로 'CREATE USER’를 실행한 후에는 문제가 해결되었습니다.

삭제된 사용자에 대한 코멘트를 읽었지만, 삭제된 사용자를 모두 커버할 수 있는 새로운 사용자/가짜 이메일 주소를 쉽게 생성할 수 없습니다(제 포럼은 20년 이상 운영되어 왔으며, 현재 삭제된 사용자의 수가 실제 사용자의 수보다 많을 것입니다). 삭제된 사용자는 약 4,000~5,000명일 것으로 추정됩니다. 모든 사용자가 게시물을 작성한 것은 아니지만, 상당수가 작성했을 가능성이 높으므로 ‘누락된’ 사용자가 수백 명에 달할 것입니다.

게시물은 'system’에 속하도록 가져와지고 있는데, 이는 이상적이지 않습니다. 다음 방법이 작동할지 궁금했습니다.

  1. 가져오기 전에 'Deleted User’와 같은 더미 사용자를 생성합니다.
  2. 'Deleted User’의 사용자 번호를 확인합니다.
  3. smf2.rb 파일의 “user_id: user_id_from_imported_user_id(message[:id_member]) || -1,” 줄을 수정하여 '-1’을 'Deleted User’의 사용자 번호로 대체합니다(system 사용자는 -1인 것으로 알고 있습니다?).

이렇게 하면 작동할까요? 또한 smf2.rb 파일에서 비슷한 변경이 필요한 다른 곳이 있을까요?

안녕하세요. '삭제’라고 하셨을 때, SMF 데이터베이스에서 실제로 삭제된 상태인지, 아니면 여전히 데이터베이스에 남아있되 사용자명과 이메일이 표시되고 정지 상태로 표시되어 있는 것을 의미하시나요? 해당 '삭제’된 사용자의 게시물이 현재 SMF에서 어떻게 표시되고 있나요?

저도 현재 Drupal에서 Discourse로의 대규모 마이그레이션을 진행 중인데, 오랜 역사를 가진 포럼에서 수많은 정지된 사용자를 함께 이전해야 하는 상황입니다. 저는 Discourse에서도 동일한 정지된 사용자명과 관련 이메일 주소를 유지하고 싶었기 때문에, Discourse용 Drupal 가져오기(importer) 스크립트에 해당 기능을 추가해야 했습니다. 기본적으로 스크립트는 모든 사용자를 정상적인 활성 사용자로 가져오며, 공개적으로 표시되는 게시물이 있다면 원본 포럼과 마찬가지로 그것도 함께 가져옵니다. 그리고 프로세스의 가장 마지막 단계에서, 다른 가져오기 스크립트에서 가져온 기능을 추가하여 Drupal 데이터베이스를 순회하고 사용자가 정지 상태로 표시되어 있다면 Discourse 계정에도 정지 처리를 하도록 했습니다. 해당 코드에 대한 내용은 제 게시글 히스토리에서 확인하실 수 있습니다.

1개의 좋아요

안녕하세요. 실제로는 사용자가 삭제되어 smf_member 테이블에 더 이상 해당 사용자의 기록이 남아 있지 않습니다. SMF에는 사용자를 일시 정지하는 기능이 없습니다. 사용자를 금지할 수는 있지만, 사용자가 사망했거나 더 이상 해당 취미나 포럼에 관심이 없어진 계정에 대해 그렇게 하는 것은 적절하지 않은 것 같습니다. 데이터 보호 관점에서도 그렇게 하는 것은 적절하지 않습니다.

SMF 게시글에는 각 기록에 대해 두 개의 필드가 저장됩니다… 삭제된 사용자의 경우 0으로 설정되는 사용자 멤버 번호와 게시자의 사용자 이름을 포함하는 게시자 이름입니다. 따라서 어떤 사용자가 메시지를 게시했는지 확인할 수는 있지만, 해당 사용자의 세부 정보(이메일, 전체 이름 등)는 더 이상 사용할 수 없습니다. 게시글은 표시될 때 ‘게스트’ 마커가 붙습니다.

멤버 ID가 0인 메시지를 게시한 모든 사용자를 위해 새로운 사용자 계정을 생성하고, 해당 계정에 더미 이메일 주소를 할당한 후, 사용자를 일시 정지 상태로 표시할 수 있을 것 같습니다. 고유하지만 식별 가능한 형식의 더미 이메일 계정을 사용하면, 이메일 형식을 기반으로 계정을 일시 정지 상태로 표시할 수 있습니다. 하지만 몇 가지 경우에는 약간 이상하게 느껴질 수 있습니다… 10~15년 전에 사망한 것으로 알고 있는 사람들을 위해 계정을 생성하는 것은 말이죠!

다만, 이 문제에 대해 생각할 시간은 있습니다… 마이그레이션이 부분적으로 작동했지만, 이제 첨부 파일이 첨부되지 않은 이유, 포럼 내 링크가 수정되지 않은 이유, 그리고 마이그레이션된 사용자의 비밀번호가 작동하지 않는 이유를 파악해야 합니다. 다른 문제도 있을 수 있지만, 먼저 이러한 문제를 수정하는 데 집중하고, 그 후 추가로 어떤 문제가 발생하는지 확인해 보겠습니다.

Postgres를 말씀하시는 건가요? 이게 무슨 말씀인지 잘 모르겠습니다.

제가 하려는 방법은 사용자 ID가 0인 경우 ID 대신 사용자 이름을 사용하는 것입니다. 그런 다음 find_username_by_import_id 함수가 사용자를 찾지 못하면, 이메일 주소를 fake_email로 설정하고(이는 base.rb에 있는 가짜 이메일 주소를 생성하는 함수입니다) 기존에 가지고 있는 사용자 이름으로 사용자를 생성합니다. 그리고 야심이 있다면 스크립트 마지막에 이메일 주소에 @email.invalid이 포함된 모든 사용자를 정지(suspend)할 수 있습니다. 그들은 비활성 상태이므로, 정지하지 않아도 큰 문제가 되지 않을 것 같습니다.

다른 방법으로는 게시글 처리를 시작하기 전에 삭제된 사용자 목록을 생성하는 쿼리를 실행하여 그들을 먼저 생성하는 방법이 있지만, 그렇게 하면 더 복잡해집니다.

deleted user라는 사용자를 생성하고 system 대신 모든 해당 게시글을 그 사용자에게 소유하도록 하고 싶다면, 그렇게 하고 -1을 deleted user의 사용자 번호로 대체하면 됩니다. 일반 사용자로 생성하거나, 사용자 ID를 -2로 설정하는 등 더 복잡한 작업을 수행할 수도 있습니다.

일부 시스템에서는 첨부파일이 게시글 본문에 있는 경우가 있고, 다른 경우에서는 첨부파일 레코드가 데이터베이스에 저장되어 있기 때문에 이런 문제가 발생합니다.

수입(import)을 실행한 후에 Migrated password hashes support 플러그인을 설치했나요? (최소 몇몇 상황에서는 수입 실행에 간섭을 줄 수 있습니다.) SMF2가 smf가 하는 방식과 동일한 방식으로 비밀번호를 해싱하나요?

스크립트 이름을 잘못 불러서 죄송합니다. 첫 번째 게시물에서 언급된 MySQL 스크립트를 말씀하시는 거죠.

– file: ~/smf2/script_for_mysql_tuning.sql
ALTER USER ‘user’@‘%’ IDENTIFIED WITH mysql_native_password BY ‘pass’;

사용자 관련, 특히 fake_email에 대한 조언에 감사드립니다. 제 첫 번째 과제는 가져오기(import) 스크립트를 수정할 수 있을 정도로 루비(Ruby)를 충분히 배우는 것입니다!

SMF2 첨부파일은 데이터베이스의 레코드입니다. 좀 더 깊이 파고들어 보니 일부는 가져와졌지만, 수만 개 중 겨우 몇백 개뿐인 것 같습니다. 왜 그런지 알아볼 수 있도록 계속 조사해 보겠습니다.

아, 아마 제가 놓친 게 바로 그거겠네요! SMF2가 SMF1과 동일한 해싱(기억이 맞다면 소금(Salt)이 추가된 MD5)을 사용한다고 확신하므로, 이 플러그인이 문제를 해결해 주기를 바랍니다. 사용자가 로그인하는 것에 대해 너무 걱정하기 전에 더 많은 가져오기 실행을 해야 합니다.

또 다른 질문이 떠올랐습니다. 시스템 초기화를 하여 다시 가져오기를 수행할 수 있는 방법이 있을까요? 시작하기 전에 백업을 해야 했는데 깜빡하고 안 했네요 :anguished:

아, MySQL 설정만 하면 된다는 말씀이시군요. 알겠습니다.

다른 언어를 조금이라도 아신다면, 그렇게 어렵지 않게 진행하실 수 있을 겁니다.
저도 Ruby를 배우기 전에 여러 개의 임포터(importer)를 작성해 봤거든요. :slight_smile:

Discourse 데이터베이스를 삭제하고 새로 만드는 방법 중 하나는 다음과 같습니다.

sv stop unicorn;DISABLE_DATABASE_ENVIRONMENT_CHECK=1 IMPORT=1 rake db:drop db:create db:migrate; sv start unicorn

백업을 미리 만들어 두시면 약간 더 빠를 수 있습니다. 아마도요.

또 다른 팁은, 사용자 계정을 처리하는 방법을 파악한 후에 사용자 임포트가 끝나면 스크립트를 중지하고 그때 백업을 만드는 것입니다. 이렇게 하면 모든 사용자를 다시 임포트할 필요 없이 게시물 임포트 부분을 디버깅할 수 있습니다.

저는 몇 가지를 알고 있습니다. 1976년에 인텔 4004에서 이진 기계어로 제 첫 번째 프로그램을 작성했습니다. 이제 smf2.rb를 조금씩 이해하기 시작했는데, 저에게 낯선 코드 구조 중 일부가 파악되고 있습니다.

데이터베이스 삭제/생성 메서드에 대해 감사합니다. 처음부터 다시 시작해서 제 데이터용 가져오기(importer)에 점진적인 변경을 가해볼 수 있을지 확인해 보겠습니다.

1개의 좋아요

삭제된 사용자를 위해 가상의 이메일 주소로 더미 계정을 생성하도록 가져오기(importer)를 수정하는 데 성공했습니다. 이제 더미 계정이 해당 사용자의 원래 게시물들을 소유하게 되었고, 이는 좋은 시작입니다.

다음으로 첨부 파일을 이해하려고 노력하고 있습니다. 지금까지 가져온 게시물들 중 어디에도 첨부 파일이 보이지 않기 때문입니다(원래 몇 개는 있어야 하는데).

Discourse 웹 페이지를 통해 정상적으로 메시지를 생성하면 posts 테이블에 레코드(id=4346)가 하나, uploads 테이블에 레코드(ids=403과 404)가 두 개, upload_references 테이블에 레코드(403/Draft/4, 403/Post/4346, 404/Draft/4, 404/Post/4346)가 네 개 생성됩니다. 또한 게시물 4346의 image_upload_id 필드에는 403이 표시되고, posts/cooked 필드에는 두 개의 업로드를 참조하는 HTML이 포함되어 있습니다.

가져온 게시물들의 경우, 가져온 각 SMF 메시지에 대해 posts 테이블에 레코드가 하나씩 생성되고, 가져온 SMF 메시지와 연관된 각 첨부 파일에 대해 uploads 테이블에 레코드가 하나씩 생성됩니다. uploads 테이블의 레코드들은 올바른 이미지를 포함하는 디스크 파일을 참조하므로, 이 부분은 정상적으로 작동하고 있습니다. 그러나 업로드된 이미지에 대한 upload_references 레코드가 생성되지 않고, posts 테이블의 image_upload_id 필드에도 업로드 ID가 포함되어 있지 않습니다.

upload_references 레코드 생성 및 posts 테이블의 image_upload_id와 cooked 필드 값을 채워 넣어야 한다고 가정하고 있습니다. 하지만 먼저 가져오기(importer)가 업로드를 게시물과 연관시키기 위해 다른 방식을 사용하거나(또는 사용하려고 하거나) 하고 있는지 확인하고 싶었습니다.

raw에 호스트/업로드에 대한 참조를 추가해야 할 것 같습니다. 링크를 생성해 주는 함수가 있는데, 이름이 기억이 나지 않습니다. 업로드 모델(uploads model) 안에 있을 것 같은데, 모델이 무엇인지 모르겠다면 다른 임포트 스크립트에서 찾는 것이 더 쉬울 수도 있습니다.

포럼의 특이한 상황에 맞게 가져오기 스크립트를 조정하는 데 진전을 이루고 있었지만, 며칠 전 갑자기 멈추게 되었습니다. Discourse 베타 최신 업데이트 이후로 가져오기 컨테이너를 빌드할 수 없게 되었습니다. 다음과 같은 오류가 발생합니다.

> FAILED
> --------------------
> Pups::ExecError: cd /var/www/discourse && apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y libmariadb-dev failed with return #<Process::Status: pid 439 exit 100>
> Location of failure: /usr/local/lib/ruby/gems/3.1.0/gems/pups-1.1.1/lib/pups/exec_command.rb:117:in `spawn'
> exec failed with the params {"cd"=>"$home", "cmd"=>["echo \"gem 'mysql2'\" >> Gemfile", "apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y libmariadb-dev", "su discourse -c 'bundle config unset deployment'", "su discourse -c 'bundle install --no-deployment --path vendor/bundle --jobs 4 --without test development'"]}
> bootstrap failed with exit code 100
> ** FAILED TO BOOTSTRAP ** please scroll up and look for earlier error messages, there may be more than one.

yarn 키 만료에 대한 게시물을 확인했고, 그 문제를 수정했습니다. 그 문제는 libmariadb-dev 패키지가 설치되지 않는 원인이었지만, 해당 패키지를 수동으로 설치했고 정상적으로 작동했습니다. 그러나 MariaDB 패키지를 수동으로 설치한 후에도 mysql 가져오기 템플릿이 활성화된 상태로 가져오기를 다시 빌드하면 여전히 작동하지 않습니다.

이전 서버/설치와 관련된 잠재적인 문제를 피하기 위해 새로운 서버를 구축하고 Discourse를 새로 설치하여 시작했습니다. 하지만 새로운 서버에서도 이전 서버와 동일한 오류가 발생합니다.

다음에 무엇을 시도해야 할지 아이디어가 없으니, 어떤 제안이든 환영합니다!

Apt-get update fails inside container yarn repo not signed - #5 by pfaffman 을 참조하세요. mysql 템플릿을 수정해야 합니다.

1개의 좋아요

정확한 방향을 알려주셔서 감사합니다. 이제 제가 어디에서 실수를 했는지 알겠어요!

1개의 좋아요

SMF 2(정확히는 v2.0.15) 포럼의 테스트 마이그레이션을 시작했으며, 드러난 첫 번째 문제 중 하나는 카테고리 이름에 앰퍼샌드(&)가 포함된 경우 발생하는 문제입니다:

같은 문자가 포함된 스레드 제목은 문제가 없는 것 같습니다:
image

현재까지 앰퍼샌드만이 문제적인 문자인 것으로 보이며, 예를 들어 독일어 음부(umlaut)는 문제없이 처리됩니다:

이 스레드에서 보고된 문제들(삭제된 사용자, 첨부 파일, 포럼 내부 링크)에도 영향을 받고 있을 가능성이 있지만, 아직 가져오기가 진행 중이므로 완료되면 업데이트하겠습니다.

이와 관련하여, 가져오기 속도가 정말 이렇게 느려야 하는지 의문이 듭니다. 현재 AMD Ryzen 5 3600 프로세서와 64GB RAM이 장착된 Hetzner 서버(Ubuntu 22.04)에서 분당 약 1750개 항목(초기에는 2000개 항목에 가까웠음)의 속도로 가져오기를 진행 중이며, 이로 인해 전체 마이그레이션에 약 3일이 소요될 것으로 예상됩니다.

꽤 좋은 속도네요.

보고된 문제들이 여전히 존재할 것 같지만, 포럼에 고유한 기능들이 의외로 많아서 그렇습니다. 수정하고 싶은 부분이 있고 예산도 있으시다면, 기꺼이 도와드리겠습니다.

1개의 좋아요

감사합니다! 상황이 좀 더 구체화되면 다시 연락드리겠습니다.

현재 저희(테이블톱 역할극 게임과 관련 취미를 중심으로 한 소규모 비영리 단체)는 평가 단계에 있습니다. 새로운 소프트웨어가 필요하다는 데에는 공감대가 형성되어 있으며, 지금까지는 Discourse가 가장 선호되는 옵션입니다. 하지만 해야 할 일들(마이그레이션, 새 테마, 필요 시 새 플러그인/테마 컴포넌트)을 모두 정리해서 예산에 맞출 수 있는지 확인해야 합니다.

1개의 좋아요

이 단계에 특별한 이유가 있나요?

OP를 주의 깊게 다시 읽어본 결과, 그 smf2.rb 파일은 변경되거나 패치되지 않는 것 같습니다.

또한 smf2.rb가 호스트에 (예를 들어) /var/discourse/smf2에 위치해 있고, 2단계에서 볼륨 마운트를 그에 맞게 설정했다면, 어차피 게스트 내부의 /shared/smf2에 마운트됩니다.

따라서 이를 별도의 단계로 복사해야 하는 필요성을 이해하지 못하겠습니다. 하지만 이 부분을 이해하는 것이 스크립트가 첨부 파일의 95%를 올바르게 찾지 못하는 이유를 파악하는 열쇠가 될 수 있습니다…

1개의 좋아요

복사할 필요가 없다는(부재)에 대해 당신이 옳은 것 같습니다. 아마도 저자가 이를 변경했고, 그 변경 사항이 나중에 코어(CORE)로 이동되었을 수 있습니다.

첨부 파일의 5%를 찾고 0%를 찾지 않는다면, 스크립트에 문제가 있는 것으로 보입니다.

일부 시스템에서는 파일을 첨부하는 두 가지 방식이 있습니다. 하나는 파일 내용을 게시물 원문에 직접 언급하는 방식(이 경우 게시물의 원문에 포함되며 해당 방식으로 처리됨)이고, 다른 하나는 메타데이터나 테이블에 첨부된 형태로 저장하는 방식입니다. 제 추측으로는 데이터베이스에 저장된 첨부 파일의 95%가 한 방식으로 처리되고, 스크립트는 다른 방식으로 처리하고 있는 것 같습니다.

하지만 두 가지 방식을 모두 시도하려는 것처럼 보이기도 합니다. 첨부 파일이 있어야 할 게시물을 찾아 데이터베이스와 파일 시스템을 확인하여 파일이 어떻게 저장되어 있는지 살펴보고, 그 다음 코드를 확인하여 왜 파일을 가져오지 못하는지 파악해야 합니다.

1개의 좋아요

@pfaffman 감사합니다. 정말 안심이 됩니다! 아마도 그런 상황일 거라고 짐작은 했지만, 이 가져오기(import) 작업에서 이렇게 많은 문제를 겪고 있어서 제 지식과 실력이 모든 면에서 의심을 받고 있었습니다!

팁 감사합니다, @pfaffman. 모든 도움이 감사드립니다. 이것은 지난 해 작업했던 동일한 포럼에 대한 오래 지연된 재시도입니다. 무보수(pro-bono) 작업이지만, 시간이 허락하신다면 다시 도움을 요청드릴 수 있을 것 같습니다.

매우 오래된 SMF2 포럼이며, 첨부 파일은 두 가지 형태로 존재합니다:

  • <id>_<대소문자 규칙이 명확하지 않은_파일명_jpg>_<32자_아마도_어떤_것의_MD5해시>
  • <id>_<40자_아마도_어떤_것의_SHA1해시>

아직 실패 패턴이 있는지 파악하고 있습니다.

제 추측(코드를 보지 않은 상태에서!)은 그중 하나가 스크립트가 실제로 수행하는 방식이고, 다른 하나는 그렇지 않다는 것입니다. (아마도 하나는 구식 방식이므로, 새로운 게시글은 다른 메서드를 사용하지만, 기존에 다른 메서드를 사용하던 게시글들을 되돌려서 수정하지는 않았을 것입니다.)

그러므로 convert_message_body와 첨부 파일 테이블의 필드를 확인해 보세요. 아마도 어떤 이유로 인해 MD5에서 SHA1로, 또는 그 반대로 변경했을 수 있습니다.

앞서 말했듯이, 이 중 한 버전이 테이블에 링크되어 있는 대신 업로드에 대한 참조를 포함하고 있을 수 있습니다. 그렇다면 gsub를 사용하여 해당 패턴을 찾고, 일치하면 키를 가져와 테이블에서 조회한 후 그 파일로 create_upload를 실행하는 방식이 될 것입니다. . .

이 정도면 시작하는 데 도움이 될 것이라 생각합니다. 하지만 여기서 제가 드릴 수 있는 도움보다 더 많은 도움이 필요하시다면 저에게 연락해 주세요.