mirror of
				https://github.com/scinote-eln/scinote-web.git
				synced 2025-10-31 00:19:20 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			103 lines
		
	
	
	
		
			3.3 KiB
		
	
	
	
		
			Ruby
		
	
	
	
	
	
			
		
		
	
	
			103 lines
		
	
	
	
		
			3.3 KiB
		
	
	
	
		
			Ruby
		
	
	
	
	
	
| # frozen_string_literal: true
 | |
| 
 | |
| class ConvertToActiveStorage < ActiveRecord::Migration[5.2]
 | |
|   require 'open-uri'
 | |
| 
 | |
|   ID_PARTITION_LIMIT = 1_000_000_000
 | |
|   DIGEST = OpenSSL::Digest.const_get('SHA1').new
 | |
|   MODELS = [Asset, TempFile, TinyMceAsset, User, ZipExport].freeze
 | |
| 
 | |
|   def up
 | |
|     ActiveRecord::Base.connection.raw_connection.prepare('active_storage_blob_statement', <<-SQL)
 | |
|       INSERT INTO active_storage_blobs (
 | |
|         key, filename, content_type, metadata, byte_size, checksum, created_at
 | |
|       ) VALUES ($1, $2, $3, '{}', $4, $5, $6)
 | |
|       RETURNING id;
 | |
|     SQL
 | |
| 
 | |
|     ActiveRecord::Base.connection.raw_connection.prepare('active_storage_attachment_statement', <<-SQL)
 | |
|       INSERT INTO active_storage_attachments (
 | |
|         name, record_type, record_id, blob_id, created_at
 | |
|       ) VALUES ($1, $2, $3, $4, $5)
 | |
|     SQL
 | |
| 
 | |
|     transaction do
 | |
|       MODELS.each do |model|
 | |
|         next unless ActiveRecord::Base.connection.table_exists?(model.table_name)
 | |
| 
 | |
|         attachments = model.column_names.map do |c|
 | |
|           $1 if c =~ /(.+)_file_name$/
 | |
|         end.compact
 | |
| 
 | |
|         next if attachments.blank?
 | |
| 
 | |
|         model.find_each.each do |instance|
 | |
|           attachments.each do |attachment|
 | |
|             next if instance.__send__("#{attachment}_file_name").blank?
 | |
| 
 | |
|             res = ActiveRecord::Base.connection.raw_connection.exec_prepared(
 | |
|               'active_storage_blob_statement', [
 | |
|                 key(instance, attachment),
 | |
|                 instance.__send__("#{attachment}_file_name"),
 | |
|                 instance.__send__("#{attachment}_content_type"),
 | |
|                 instance.__send__("#{attachment}_file_size") || 0,
 | |
|                 checksum(attachment),
 | |
|                 instance.updated_at.iso8601
 | |
|               ]
 | |
|             )
 | |
| 
 | |
|             ActiveRecord::Base.connection.raw_connection.exec_prepared(
 | |
|               'active_storage_attachment_statement', [
 | |
|                 attachment,
 | |
|                 model.name,
 | |
|                 instance.id,
 | |
|                 res[0]['id'],
 | |
|                 instance.updated_at.iso8601
 | |
|               ]
 | |
|             )
 | |
|           end
 | |
|         end
 | |
|       end
 | |
|     end
 | |
|   end
 | |
| 
 | |
|   def down
 | |
|     raise ActiveRecord::IrreversibleMigration
 | |
|   end
 | |
| 
 | |
|   private
 | |
| 
 | |
|   ID_PARTITION_LIMIT = 1_000_000_000
 | |
|   DIGEST = OpenSSL::Digest.const_get('SHA1').new
 | |
| 
 | |
|   def id_partition(id)
 | |
|     if id < ID_PARTITION_LIMIT
 | |
|       format('%09d', id).scan(/\d{3}/).join('/')
 | |
|     else
 | |
|       format('%012d', id).scan(/\d{3}/).join('/')
 | |
|     end
 | |
|   end
 | |
| 
 | |
|   def hash_data(instance, attachment)
 | |
|     "#{instance.class.to_s.underscore.pluralize}/#{attachment.pluralize}/#{instance.id}/original"
 | |
|   end
 | |
| 
 | |
|   def interpolate(pattern, instance, attachment)
 | |
|     path = pattern
 | |
|     path = path.gsub(':class', instance.class.to_s.underscore.pluralize)
 | |
|     path = path.gsub(':attachment', attachment.pluralize)
 | |
|     path = path.gsub(':id_partition', id_partition(instance.id))
 | |
|     path = path.gsub(':hash', OpenSSL::HMAC.hexdigest(DIGEST,
 | |
|                                                       ENV['PAPERCLIP_HASH_SECRET'],
 | |
|                                                       hash_data(instance, attachment)))
 | |
|     path.gsub(':filename', instance.__send__("#{attachment}_file_name"))
 | |
|   end
 | |
| 
 | |
|   def key(instance, attachment)
 | |
|     interpolate(':class/:attachment/:id_partition/:hash/original/:filename', instance, attachment)
 | |
|   end
 | |
| 
 | |
|   def checksum(_attachment)
 | |
|     'dummy'
 | |
|   end
 | |
| end
 |