Files
danbooru/app/logical/storage_manager.rb
evazion 15799f8af7 Fix #4260: Unable to replace cdn.donmai.us images?
Bug: Replacing posts hosted on cdn.donmai.us didn't work.

Cause: Original files on cdn.donmai.us are hosted under /var/www/danbooru/original/, but replacements
were trying to store them directly under /var/www/danbooru, which failed with a permission error.
We were trying to store them in the wrong directory because we didn't respect the `original_subdir`
option when generating file paths.
2020-06-09 15:57:43 -05:00

122 lines
3.4 KiB
Ruby

class StorageManager
class Error < StandardError; end
DEFAULT_BASE_DIR = "#{Rails.root}/public/data"
attr_reader :base_url, :base_dir, :hierarchical, :tagged_filenames, :original_subdir
def initialize(base_url: default_base_url, base_dir: DEFAULT_BASE_DIR, hierarchical: false, tagged_filenames: Danbooru.config.enable_seo_post_urls, original_subdir: "")
@base_url = base_url.chomp("/")
@base_dir = base_dir
@hierarchical = hierarchical
@tagged_filenames = tagged_filenames
@original_subdir = original_subdir
end
def default_base_url
"#{CurrentUser.root_url}/data"
end
# Store the given file at the given path. If a file already exists at that
# location it should be overwritten atomically. Either the file is fully
# written, or an error is raised and the original file is left unchanged. The
# file should never be in a partially written state.
def store(io, path)
raise NotImplementedError, "store not implemented"
end
# Delete the file at the given path. If the file doesn't exist, no error
# should be raised.
def delete(path)
raise NotImplementedError, "delete not implemented"
end
# Return a readonly copy of the file located at the given path.
def open(path)
raise NotImplementedError, "open not implemented"
end
def store_file(io, post, type)
store(io, file_path(post.md5, post.file_ext, type))
end
def delete_file(post_id, md5, file_ext, type)
delete(file_path(md5, file_ext, type))
end
def open_file(post, type)
self.open(file_path(post.md5, post.file_ext, type))
end
def file_url(post, type, tagged_filenames: false)
subdir = subdir_for(post.md5)
file = file_name(post.md5, post.file_ext, type)
seo_tags = seo_tags(post) if tagged_filenames
if type == :preview && !post.has_preview?
"#{root_url}/images/download-preview.png"
elsif type == :preview
"#{base_url}/preview/#{subdir}#{file}"
elsif type == :crop
"#{base_url}/crop/#{subdir}#{file}"
elsif type == :large && post.has_large?
"#{base_url}/sample/#{subdir}#{seo_tags}#{file}"
else
"#{base_url}/#{original_subdir}#{subdir}#{seo_tags}#{post.md5}.#{post.file_ext}"
end
end
def root_url
origin = Addressable::URI.parse(base_url).origin
origin = "" if origin == "null" # base_url was relative
origin
end
def file_path(post_or_md5, file_ext, type)
md5 = post_or_md5.is_a?(String) ? post_or_md5 : post_or_md5.md5
subdir = subdir_for(md5)
file = file_name(md5, file_ext, type)
case type
when :preview
"#{base_dir}/preview/#{subdir}#{file}"
when :crop
"#{base_dir}/crop/#{subdir}#{file}"
when :large
"#{base_dir}/sample/#{subdir}#{file}"
when :original
"#{base_dir}/#{original_subdir}#{subdir}#{file}"
end
end
def file_name(md5, file_ext, type)
large_file_ext = (file_ext == "zip") ? "webm" : "jpg"
case type
when :preview
"#{md5}.jpg"
when :crop
"#{md5}.jpg"
when :large
"sample-#{md5}.#{large_file_ext}"
when :original
"#{md5}.#{file_ext}"
end
end
def subdir_for(md5)
if hierarchical
"#{md5[0..1]}/#{md5[2..3]}/"
else
""
end
end
def seo_tags(post)
return "" if !tagged_filenames
tags = post.presenter.humanized_essential_tag_string.gsub(/[^a-z0-9]+/, "_").gsub(/(?:^_+)|(?:_+$)/, "").gsub(/_{2,}/, "_")
"__#{tags}__"
end
end