-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
431 additions
and
60 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
require 'rails_helper' | ||
require 'aws-sdk-s3' | ||
|
||
RSpec.describe DigitalOceanSpacesService do | ||
let(:bucket_name) { 'linkarooie' } | ||
let(:service) { described_class.new(bucket_name) } | ||
let(:s3_bucket) { instance_double(Aws::S3::Bucket) } | ||
let(:s3_object) { instance_double(Aws::S3::Object) } | ||
let(:file_content) { 'image_content' } | ||
let(:content_type) { 'image/jpeg' } | ||
let(:key) { 'avatars/default_avatar.jpg' } | ||
|
||
before do | ||
# Mock S3 Client initialization and bucket | ||
allow(S3_CLIENT).to receive(:bucket).with(bucket_name).and_return(s3_bucket) | ||
allow(s3_bucket).to receive(:object).with(key).and_return(s3_object) | ||
end | ||
|
||
describe '#upload_file' do | ||
it 'uploads the file to DigitalOcean Spaces and returns the public URL' do | ||
# Mock successful upload | ||
allow(s3_object).to receive(:put).and_return(true) | ||
allow(s3_object).to receive(:public_url).and_return("https://#{bucket_name}.syd1.digitaloceanspaces.com/#{key}") | ||
|
||
result = service.upload_file(key, file_content, content_type) | ||
|
||
# Verify that the correct methods are called | ||
expect(s3_bucket).to have_received(:object).with(key) | ||
expect(s3_object).to have_received(:put).with(body: file_content, acl: 'public-read', content_type: content_type) | ||
# Ensure the public URL is returned | ||
expect(result).to eq("https://#{bucket_name}.syd1.digitaloceanspaces.com/#{key}") | ||
end | ||
|
||
it 'returns nil if the upload fails' do | ||
# Mock an upload failure | ||
allow(s3_object).to receive(:put).and_raise(Aws::S3::Errors::ServiceError.new(nil, 'Upload failed')) | ||
|
||
result = service.upload_file(key, file_content, content_type) | ||
|
||
# Verify that nil is returned on failure | ||
expect(result).to be_nil | ||
end | ||
end | ||
|
||
describe '#delete_file' do | ||
it 'deletes the file from DigitalOcean Spaces' do | ||
# Mock successful deletion | ||
allow(s3_object).to receive(:delete).and_return(true) | ||
|
||
result = service.delete_file(key) | ||
|
||
# Verify that the correct delete method is called | ||
expect(s3_bucket).to have_received(:object).with(key) | ||
expect(s3_object).to have_received(:delete) | ||
# Ensure the result is nil since the method doesn't return anything on success | ||
expect(result).to be_nil | ||
end | ||
|
||
it 'returns nil and logs an error if deletion fails' do | ||
# Mock a deletion failure | ||
allow(s3_object).to receive(:delete).and_raise(Aws::S3::Errors::ServiceError.new(nil, 'Delete failed')) | ||
|
||
result = service.delete_file(key) | ||
|
||
# Verify that nil is returned on failure | ||
expect(result).to be_nil | ||
end | ||
end | ||
|
||
describe '#download_file' do | ||
it 'downloads the file from DigitalOcean Spaces' do | ||
# Mock successful download | ||
allow(s3_object).to receive(:get).and_return(double(body: double(read: 'file_content'))) | ||
|
||
result = service.download_file(key) | ||
|
||
# Verify that the file content is returned | ||
expect(result).to eq('file_content') | ||
end | ||
|
||
it 'returns nil if the download fails' do | ||
# Mock a download failure | ||
allow(s3_object).to receive(:get).and_raise(Aws::S3::Errors::ServiceError.new(nil, 'Download failed')) | ||
|
||
result = service.download_file(key) | ||
|
||
# Verify that nil is returned on failure | ||
expect(result).to be_nil | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
# lib/tasks/migrate_images_to_spaces.rake | ||
|
||
namespace :images do | ||
desc 'Migrate user avatars and banners to DigitalOcean Spaces (use dry_run=true for testing)' | ||
task :migrate_to_spaces, [:dry_run] => :environment do |t, args| | ||
dry_run = args[:dry_run] == 'true' | ||
puts "\n#{'DRY RUN: ' if dry_run}Starting image migration to DigitalOcean Spaces" | ||
puts "=========================================================\n\n" | ||
|
||
unless ENV['SPACES_BUCKET_IMAGES'] | ||
puts "Error: SPACES_BUCKET_IMAGES environment variable is not set." | ||
exit | ||
end | ||
|
||
def upload_to_spaces(file_path, key, dry_run) | ||
if dry_run | ||
puts " Would upload: #{file_path}" | ||
puts " To: #{key}" | ||
"https://#{ENV['SPACES_BUCKET_IMAGES']}.syd1.digitaloceanspaces.com/#{key}" | ||
else | ||
bucket = S3_CLIENT.bucket(ENV['SPACES_BUCKET_IMAGES']) | ||
obj = bucket.object(key) | ||
|
||
begin | ||
File.open(file_path, 'rb') do |file| | ||
obj.put(body: file, acl: 'public-read') | ||
end | ||
puts " Uploaded: #{key}" | ||
"https://#{ENV['SPACES_BUCKET_IMAGES']}.syd1.digitaloceanspaces.com/#{key}" | ||
rescue Aws::S3::Errors::ServiceError => e | ||
puts " Failed to upload #{key}: #{e.message}" | ||
nil | ||
end | ||
end | ||
end | ||
|
||
def migrate_image(user, attribute, folder, dry_run) | ||
local_path = user.send("#{attribute}_local_path") || user.send(attribute) | ||
if local_path.present? | ||
file_path = local_path.start_with?('/') ? Rails.root.join('public', local_path.sub(/\A\//, '')) : Rails.root.join('public', local_path) | ||
if File.exist?(file_path) | ||
key = "#{folder}/#{user.id}_#{File.basename(file_path)}" | ||
spaces_url = upload_to_spaces(file_path, key, dry_run) | ||
if spaces_url | ||
if dry_run | ||
puts " Would update #{attribute} URL to: #{spaces_url}" | ||
else | ||
user.update(attribute => spaces_url) | ||
puts " Updated #{attribute} URL to: #{spaces_url}" | ||
end | ||
end | ||
else | ||
puts " File not found: #{file_path}" | ||
end | ||
else | ||
puts " No #{attribute} found" | ||
end | ||
end | ||
|
||
puts "Migrating user images:" | ||
User.find_each do |user| | ||
puts "\nProcessing user: #{user.email}" | ||
puts " Avatar:" | ||
migrate_image(user, :avatar, 'avatars', dry_run) | ||
puts " Banner:" | ||
migrate_image(user, :banner, 'banners', dry_run) | ||
end | ||
|
||
def handle_default_image(image_type, dry_run) | ||
puts "\nProcessing default #{image_type}:" | ||
fallback_constant = "User::FALLBACK_#{image_type.upcase}_URL" | ||
fallback_url = User.const_get(fallback_constant) | ||
file_path = Rails.root.join('public', fallback_url.sub(/\A\//, '')) | ||
|
||
if File.exist?(file_path) | ||
key = "defaults/default_#{image_type}#{File.extname(file_path)}" | ||
spaces_url = upload_to_spaces(file_path, key, dry_run) | ||
if spaces_url | ||
affected_users = User.where(image_type => fallback_url) | ||
if dry_run | ||
puts " Would update #{affected_users.count} users with default #{image_type}" | ||
puts " New URL would be: #{spaces_url}" | ||
else | ||
affected_users.update_all(image_type => spaces_url) | ||
puts " Updated #{affected_users.count} users' default #{image_type}" | ||
puts " New URL is: #{spaces_url}" | ||
end | ||
end | ||
else | ||
puts " Default #{image_type} file not found: #{file_path}" | ||
end | ||
end | ||
|
||
handle_default_image('avatar', dry_run) | ||
handle_default_image('banner', dry_run) | ||
|
||
puts "\n#{'DRY RUN: ' if dry_run}Image migration completed!" | ||
puts "=========================================================\n\n" | ||
end | ||
end |
Oops, something went wrong.