discourse/lib/upload_recovery.rb

131 lines
3.0 KiB
Ruby

class UploadRecovery
def initialize(dry_run: false)
@dry_run = dry_run
end
def recover(posts = Post)
posts.where("raw LIKE '%upload:\/\/%'").find_each do |post|
begin
analyzer = PostAnalyzer.new(post.raw, post.topic_id)
analyzer.cooked_stripped.css("img").each do |img|
if dom_class = img["class"]
if (Post.white_listed_image_classes & dom_class.split).count > 0
next
end
end
if img["data-orig-src"]
if @dry_run
puts "#{post.full_url} #{img["data-orig-src"]}"
else
recover_post_upload(post, img["data-orig-src"])
end
end
end
rescue => e
raise e unless @dry_run
puts "#{post.full_url} #{e.class}: #{e.message}"
end
end
end
private
def recover_post_upload(post, short_url)
sha1 = Upload.sha1_from_short_url(short_url)
return unless sha1.present?
attributes = {
post: post,
sha1: sha1
}
if Discourse.store.external?
recover_from_s3(attributes)
else
recover_from_local(attributes)
end
end
def recover_from_local(post:, sha1:)
public_path = Rails.root.join("public")
@paths ||= begin
Dir.glob(File.join(
public_path,
'uploads',
'tombstone',
RailsMultisite::ConnectionManagement.current_db,
'original',
'**',
'*.*'
)).concat(Dir.glob(File.join(
public_path,
'uploads',
RailsMultisite::ConnectionManagement.current_db,
'original',
'**',
'*.*'
)))
end
@paths.each do |path|
if path =~ /#{sha1}/
begin
file = File.open(path, "r")
create_upload(file, File.basename(path), post)
ensure
file&.close
end
end
end
end
def recover_from_s3(post:, sha1:)
@object_keys ||= begin
s3_helper = Discourse.store.s3_helper
s3_helper.list("original").map(&:key).concat(
s3_helper.list("#{FileStore::S3Store::TOMBSTONE_PREFIX}original").map(&:key)
)
end
@object_keys.each do |key|
if key =~ /#{sha1}/
tombstone_prefix = FileStore::S3Store::TOMBSTONE_PREFIX
if key.include?(tombstone_prefix)
old_key = key
key = key.sub(tombstone_prefix, "")
Discourse.store.s3_helper.copy(
old_key,
key,
options: { acl: "public-read" }
)
end
url = "https:#{SiteSetting.Upload.absolute_base_url}/#{key}"
begin
tmp = FileHelper.download(
url,
max_file_size: SiteSetting.max_image_size_kb.kilobytes,
tmp_file_name: "recover_from_s3"
)
create_upload(tmp, File.basename(key), post) if tmp
ensure
tmp&.close
end
end
end
end
def create_upload(file, filename, post)
upload = UploadCreator.new(file, filename).create_for(post.user_id)
post.rebake! if upload.persisted?
end
end