diaspora/app/services/migration_service.rb

142 lines
3.9 KiB
Ruby

# frozen_string_literal: true
class MigrationService
attr_reader :archive_path, :new_user_name, :opts
delegate :errors, :warnings, to: :archive_validator
def initialize(archive_path, new_user_name, opts={})
@archive_path = archive_path
@new_user_name = new_user_name
@opts = opts
end
def validate
return unless archive_file_exists?
archive_validator.validate
raise ArchiveValidationFailed, errors.join("\n") if errors.any?
raise MigrationAlreadyExists if AccountMigration.where(old_person: old_person).any?
raise SelfMigrationNotAllowed if self_import?
end
def perform!
find_or_create_user
import_archive
run_migration
ensure
remove_intermediate_file
end
def self_import?
source_diaspora_id = archive_validator.archive_author_diaspora_id
target_diaspora_id = "#{new_user_name}#{User.diaspora_id_host}"
source_diaspora_id == target_diaspora_id
end
# when old person can't be resolved we still import data but we don't create&perform AccountMigration instance
def only_import?
old_person.nil?
end
private
def find_or_create_user
archive_importer.find_or_create_user(username: new_user_name, password: SecureRandom.hex)
end
def import_archive
archive_importer.import(opts)
end
def run_migration
account_migration.save
account_migration.perform!
end
def account_migration
@account_migration ||= AccountMigration.new(
old_person: old_person,
new_person: archive_importer.user.person,
old_private_key: archive_importer.serialized_private_key,
old_person_diaspora_id: archive_importer.archive_author_diaspora_id,
archive_contacts: archive_importer.contacts
)
end
def old_person
@old_person ||= Person.by_account_identifier(archive_validator.archive_author_diaspora_id)
end
def archive_importer
@archive_importer ||= ArchiveImporter.new(archive_validator.archive_hash)
end
def archive_validator
@archive_validator ||= ArchiveValidator.new(archive_file)
end
def archive_file
return uncompressed_zip if zip_file?
return uncompressed_gz if gzip_file?
File.new(archive_path, "r")
end
def archive_file_exists?
File.exist?(archive_path)
end
def zip_file?
filetype = MIME::Types.type_for(archive_path).first.content_type
filetype.eql?("application/zip")
end
def gzip_file?
filetype = MIME::Types.type_for(archive_path).first.content_type
filetype.eql?("application/gzip")
end
def uncompressed_gz
target_dir = File.dirname(archive_path) + Pathname::SEPARATOR_LIST
unzipped_archive_file = "#{File.join(target_dir, SecureRandom.hex)}.json" # never override an existing file
Zlib::GzipReader.open(archive_path) {|gz|
File.open(unzipped_archive_file, "w") do |output_stream|
IO.copy_stream(gz, output_stream)
end
@intermediate_file = unzipped_archive_file
}
File.new(unzipped_archive_file, "r")
end
def uncompressed_zip
target_dir = File.dirname(archive_path) + Pathname::SEPARATOR_LIST
zip_stream = Zip::InputStream.open(archive_path)
while entry = zip_stream.get_next_entry # rubocop:disable Lint/AssignmentInCondition
next unless entry.name.end_with?(".json")
target_file = "#{File.join(target_dir, SecureRandom.hex)}.json" # never override an existing file
entry.extract(target_file)
@intermediate_file = target_file
return File.new(target_file, "r")
end
end
def remove_intermediate_file
# If an unzip operation created an unzipped file, remove it after migration
return if @intermediate_file.nil?
return unless File.exist?(@intermediate_file)
File.delete(@intermediate_file)
end
class ArchiveValidationFailed < RuntimeError
end
class MigrationAlreadyExists < RuntimeError
end
class SelfMigrationNotAllowed < RuntimeError
end
end