diff --git a/app/logical/storage_manager.rb b/app/logical/storage_manager.rb index 4970dec26..b12d1f674 100644 --- a/app/logical/storage_manager.rb +++ b/app/logical/storage_manager.rb @@ -17,9 +17,9 @@ class StorageManager # @param base_url [String] the base URL where images are stored (ex: "https://cdn.donmai.us/") # @param base_dir [String] the base directory where images are stored (ex: "/var/www/danbooru/public/images") # @param tagged_filenames [Boolean] whether image URLs can include tags - def initialize(base_url:, base_dir:, tagged_filenames: Danbooru.config.enable_seo_post_urls) - @base_url = base_url.chomp("/") - @base_dir = base_dir + def initialize(base_url: nil, base_dir: nil, tagged_filenames: Danbooru.config.enable_seo_post_urls) + @base_url = base_url.to_s.chomp("/") + @base_dir = base_dir.to_s @tagged_filenames = tagged_filenames end diff --git a/app/logical/storage_manager/mirror.rb b/app/logical/storage_manager/mirror.rb new file mode 100644 index 000000000..b7dbcc24c --- /dev/null +++ b/app/logical/storage_manager/mirror.rb @@ -0,0 +1,29 @@ +# A StorageManager that mirrors files across multiple storage backends. +class StorageManager::Mirror < StorageManager + attr_reader :services + + def initialize(services, **options) + @services = services + super(**options) + end + + def store(io, dest_path) + services.each do |service| + service.store(io, dest_path) + end + end + + def delete(path) + services.each do |service| + service.delete(path) + end + end + + def open(path) + services.first.open(path) + end + + def file_url(path) + services.first.file_url(path) + end +end diff --git a/test/unit/storage_manager_test.rb b/test/unit/storage_manager_test.rb index 1b8c6fd95..ad054563b 100644 --- a/test/unit/storage_manager_test.rb +++ b/test/unit/storage_manager_test.rb @@ -38,4 +38,48 @@ class StorageManagerTest < ActiveSupport::TestCase end end end + + context "StorageManager::Mirror" do + setup do + @temp_dir1 = Dir.mktmpdir("danbooru-temp1-") + @temp_dir2 = Dir.mktmpdir("danbooru-temp2-") + + @storage_manager = StorageManager::Mirror.new([ + StorageManager::Local.new(base_dir: @temp_dir1, base_url: "/data"), + StorageManager::Local.new(base_dir: @temp_dir2, base_url: "/data") + ]) + end + + teardown do + FileUtils.rm_rf(@temp_dir1) + FileUtils.rm_rf(@temp_dir2) + end + + context "#store method" do + should "store the file on both backends" do + @storage_manager.store(StringIO.new("data"), "test.txt") + + assert("data", File.read("#{@temp_dir1}/test.txt")) + assert("data", File.read("#{@temp_dir2}/test.txt")) + end + end + + context "#delete method" do + should "delete the file from both backends" do + @storage_manager.store(StringIO.new("data"), "test.txt") + @storage_manager.delete("test.txt") + + assert_not(File.exist?("#{@temp_dir1}/test.txt")) + assert_not(File.exist?("#{@temp_dir2}/test.txt")) + end + end + + context "#open method" do + should "open the file from the first backend" do + @storage_manager.store(StringIO.new("data"), "test.txt") + + assert_equal("data", @storage_manager.open("test.txt").read) + end + end + end end