mirror of
https://github.com/discourse/discourse.git
synced 2024-11-23 22:26:26 +08:00
131 lines
3.0 KiB
Ruby
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.starts_with?(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
|