Generate image URLs relative to the site's canonical URL instead of relative to the domain of the current request. This means that all subdomains of Danbooru - safebooru.donmai.us, shima.donmai.us, saitou.donmai.us, and kagamihara.donmai.us - will use image URLs from https://danbooru.donmai.us, instead of from the current domain. The main reason we did this before was so that we could generate either http:// or https:// image URLs, depending on whether the current request was HTTP or HTTPS, back when we tried to support both at the same time. Now we support only HTTPS in production, so there's no need for this. It was also pretty hacky, since it required storing the URL of the current request in a per-request global variable in `CurrentUser`. This also improves caching slightly, since users of safebooru.donmai.us will receive cached images from danbooru.donmai.us. Downstream boorus should make sure that the `canonical_url` and `storage_manager` config options are set correctly. If you don't support https:// in development, you should make sure to set the canonical_url option to http:// instead of https://.
116 lines
3.2 KiB
Ruby
116 lines
3.2 KiB
Ruby
class StorageManager
|
|
class Error < StandardError; end
|
|
|
|
attr_reader :base_url, :base_dir, :hierarchical, :tagged_filenames, :original_subdir
|
|
|
|
def initialize(base_url:, 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
|
|
|
|
# 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
|