누락된 업로드를 데이터베이스에 다시 추가하기

사이트에서 데이터베이스의 uploads 테이블에서 여러 업로드 기록이 삭제되었지만 파일 시스템에는 여전히 존재하는 것으로 보입니다. 이로 인해 해당 링크들이 깨진 상태가 되었습니다. 저는 DB에 기록을 생성하기 위해 새 주제에 파일을 다시 업로드하는 방식으로 몇 개를 수정했습니다(당시에는 파일 자체도 사라진 줄 알았지만, @angus가 제가 파일을 찾지 못한 이유를 친절하게 알려주었습니다. 언젠가 물어볼게요: 왜 이 모든 에셋에 sha1 그리고 base62 이름이 둘 다 있나요?) 그런 다음 해당 업로드를 포함하는 게시글을 다음과 같이 다시 생성(rebake)했습니다:

def rebake_posts_with_uploads(topic_id, post_number)
  p = Post.find_by(topic_id: topic_id, post_number: post_number)
  exit unless p
  re = /upload:\/\/(.+?)\)/
  shas= p.raw.scan(re)

  shas.each do |sha|
    posts = Post.where("raw like '%#{sha[0]}%'")
    next unless posts
    puts "Found #{posts.count - 1} #{sha[0]}"
    posts.each do |post|
      next if post.id == p.id
      puts "rebake #{post.id}--#{BASE_URL}/t/-/#{post.topic_id}/#{post.post_number}"
      post.rebake!
    end
  end
end

파일 시스템에는 파일이 있지만 데이터베이스에는 없는 것으로 보이므로, 제 새로운 계획은 다음과 같은 작업을 수행하는 것입니다:

/shared/uploads/default/original/1x의 모든 파일에 대해 |file|
  uploads 테이블에 파일이 없다면
     업로드 기록 생성 
     해당 업로드 기록을 포함하는 각 게시글에 대해
       다시 생성(rebake)

이렇게 하는 것이 맞을까요? uploads.rake를 보고 있지만, 이미 이런 작업을 수행하는 내용은 보이지 않습니다. 이것은 다음과 같은 것과 약간 반대되는 개념입니다:

다만 FileUtils.rm(file_path) 대신 Upload.create를 수행하는 것이라고 생각합니다.

이 방법이 정말 멍청해 보이거나 훨씬 나은 해결책이 있다면, 이 작은 토끼굴에 들어가기 전에 듣고 싶습니다.

감사합니다.

이런 일이 어떻게 일어났는지 모르겠습니다. 커스텀 플러그인의 탓으로 돌리기를 바랐지만, 안타깝게도 그런 경우가 아닙니다. 다른 토론과 관련이 있을 수 있는데, 거기서 누군가가 이렇게 말했습니다:

음, 여기에서 업로드 관련해서 좀 불안정한 적이 많았고, 이건 우리 탓이죠.. @sam, 빠르게 조언해 줄 수 있는 사람을 추천해 줄 수 있을까요?

이 방법은 작동할 수 있지만, 로컬에서 테스트할 때 주의해야 합니다. 또한 2x / 3x 디렉터리도 확인해 보세요. 여기저기 업로드된 파일이 널려 있습니다.

이런 식으로 시도해 보고 있습니다:

def add_missing_files_to_uploads
  public_directory = Rails.root.join("public").to_s
  db = RailsMultisite::ConnectionManagement.current_db
  uploads_directory = File.join(public_directory, 'uploads', db).to_s
  # 업로드 및 최적화된 이미지
  missing = 0
  matched = 0
  Dir.glob("#{uploads_directory}/**/*.pdf").each do |file_path|
    sha1 = Upload.generate_digest(file_path)
    url = file_path.split(public_directory, 2)[1]
    if (Upload.where(sha1: sha1).empty? &&
        Upload.where(url: url).empty?)
      puts "MISSING #{file_path}" if DEBUG
      missing += 1
    else
      matched += 1
    end
  end
  puts "MISS: #{missing}. Match #{matched}"
end

이 정도면 꽤 근접한가요? 첫 번째 테스트는 실패한 것 같지만, 제가 뭔가 잘못했을 수도 있습니다.

Hi @pfaffman. 이 주제를 읽고 있었는데, 여기서 제가 겪고 있는 문제에 대한 유망한 해결책처럼 보여서요.
결국 좋은 결과를 얻으셨나요?

미안하지만, 기억이 나지 않네.

샘과 내 생각엔 잘 작동할 것 같아.

나는 방금 시뮬레이터 문제를 해결했는데, 게시물을 하나 만들고 그 안에 모든 이미지의 링크를 삽입한 다음, transparent.gif를 포함하는 모든 게시물을 다시 베이킹(rebaking)하는 방식으로 해결했어.

이것은 좀 더 우아한 해결책처럼 보여.

데이터베이스만 백업하고 시도해 보는 걸 추천해. 나는 지금 일종의 휴가 중이지만, 예산이 있다면 내가 할 수 있는 일이 있는지 살펴볼 수 있어.

나도 ams3에 중요한 클라이언트가 있어. 아직 AWS로 이전하지는 않았지만, 곧 진행될 것 같아. Digital Ocean에서 일했던 친구가 ams3를 추천했는데, 당시에는 최고의 데이터센터였고 대부분 유휴 상태였기 때문이었어(이건 꽤 오래전 일이지). 하지만 내가 바랐던 대로 잘 풀리지는 않았어.

요즘에는 이게 최선의 방법인지 궁금합니다.

오래된 rake:s3_migrate를 사용한 잘못된 S3 마이그레이션 이후, 파일시스템에는 있지만 데이터베이스에는 없는 업로드 파일을 복구하려고 합니다.

사실 좋은 방법은 없습니다. :crying_cat:

하지만, 네. 여전히 그 아이디어가 맞습니다. 그때 이후로 Discourse에 변화가 있었던 것 같지는 않습니다. 보통은 예상대로 작동하는지 확인하기 위해 몇 개는 수동으로 처리합니다. 또한, 먼저 백업을 만들어 두는 것이 좋습니다. 그리고 혹시 백업을 복원해야 하는 상황이 발생했을 때, 작업을 하던 동안 작성된 게시물을 버리지 않도록 사이트를 읽기 전용 모드로 설정해 두는 것도 좋습니다.

포럼에서 쉽게 전달하기 어려운 훨씬 더 많은 정보가 없다면, 이것이 정말로 최선의 방법인지, 아니면 더 간단한 방법이 있는지 단정할 수 없습니다. 예를 들어, URL의 경로를 gsub로만 교체할 수도 있습니다. 예산이 있다면 저에게 연락하거나 Marketplace 채널에서 질문해 보세요.

오케이, Claude와 많은 칭찬으로 문제를 해결했습니다. 비슷한 또는 동일한 문제를 겪고 있는 다른 분들을 도우려고 제가 한 작업을 공유합니다.

이것이 가장 지능적이고 최적의 방법인지는 확실하지 않지만, 제게는 효과가 있었던 방법입니다.

전문가가 아니라 항상 배우고 있는 초보자이므로 주의하시고 이 점을 염두에 두시기 바랍니다.

문제 (S3 → 로컬 파일 시스템)

AWS S3에서 로컬 FS로 마이그레이션한 후, 많은 이미지들이 transparent.png로 표시되었습니다. 파일은 항상 디스크에 존재했지만 Discourse가 이를 해결하지 못했습니다.

근본 원인은 끊어진 체인이었습니다:

  1. upload:// 짧은 URL(base62 인코딩된 SHA1)을 가진 게시물.
  2. SHA1 → 로컬 파일 경로를 매핑하는 데이터베이스의 uploads.
  3. SHA1 해시로 이름이 지정된 파일을 저장하는 파일 시스템.

마이그레이션은 파일을 디스크에 올바르게 이동했지만, uploads DB 기록이 존재하지 않았습니다. 일치하는 기록이 없으면 Discourse는 transparent.png로 대체합니다.

해결책 (기록 생성 및 재베이크)

# 컨테이너 진입
./launcher enter app
rails c

고아 파일에서 누락된 업로드 기록 생성:

dir = Rails.root.join("public", "uploads", "default", "original")
created = 0

Dir.glob(dir.join("**", "*")).select { |f| File.file?(f) }.each do |path|
  sha = File.basename(path, File.extname(path))
  next if Upload.find_by(sha1: sha)

  ext = File.extname(path).delete(".")
  relative = path.sub("#{Rails.root}/public", "")

  u = Upload.new
  u.sha1 = sha
  u.url = relative
  u.original_filename = File.basename(path)
  u.filesize = File.size(path)
  u.extension = ext
  u.user_id = -1
  u.save!(validate: false)

  created += 1
  puts "Created upload #{u.id}: #{sha}"
end

puts "Total created: #{created}"

복원된 업로드를 참조하는 게시물 재베이크:

fixed_posts = 0

Upload.where(user_id: -1).find_each do |u|
  short = u.short_url
  next unless short

  Post.where("raw LIKE '%upload://%'").find_each do |p|
    urls = p.raw.scan(/upload:\/\/[^\s\]\)]+/)
    urls.each do |url|
      decoded = Upload.sha1_from_short_url(url)
      if decoded == u.sha1
        p.rebake!
        fixed_posts += 1
        puts "Rebaked post #{p.id}"
        break
      end
    end
  end
end

puts "Total rebaked: #{fixed_posts}"

누락된 최적화 파일 재생성:

원본 파일을 수정한 후, 최적화 파일(1X, 2X 등)을 채워 넣어야 합니다.

Rake는 discourse 컨테이너에서는 작동하지만 rails 콘솔에서는 작동하지 않습니다.

rake uploads:regenerate_missing_optimized

[선택 사항] 여전히 최적화 파일이 누락된 경우

rake uploads:regenerate_missing_optimized가 모든 파일 문제를 해결하지 못하고 여전히 파일이 누락된 경우:

# 컨테이너 진입
./launcher enter app
rails c
missing = 0
OptimizedImage.find_each do |oi|
  path = "#{Rails.root}/public#{oi.url}"
  unless File.exist?(path)
    missing += 1
    oi.delete
  end
end
puts "Deleted #{missing} broken optimized records"

그 후 rails를 종료하고 다시 실행합니다:

rake uploads:regenerate_missing_optimized

안전한 롤백 (만약을 대비하여)

생성된 모든 기록은 user_id: -1을 사용하며, delete_all은 콜백을 건너뛰므로 파일 시스템의 파일에는 영향을 미치지 않습니다. 되돌리려면:

Upload.where(user_id: -1).delete_all

실수로 destroy_all을 사용하여 파일이 톰스톤으로 이동하는 콜백이 트리거되었습니다.

테스트용으로 사용했던 개별 항목을 복구하고 제 접근 방식을 재구성했습니다.