require 'ipfs/client' class ActiveStorage::DirectUploadsController private def blob_args params.require(:blob).permit(:key, :filename, :byte_size, :checksum, :content_type, :metadata).to_h.symbolize_keys end end class ActiveStorage::Blob class << self def create_before_direct_upload!(key: nil, filename:, byte_size:, checksum:, content_type: nil, metadata: nil) create! key: key, filename: filename, byte_size: byte_size, checksum: checksum, content_type: content_type, metadata: metadata end end end module ActiveStorage class Service::IpfsService < Service attr_reader :client def initialize(api_endpoint:, gateway_endpoint:) @client = Ipfs::Client.new api_endpoint, gateway_endpoint end # File is uploaded to Ipfs and a hash # is returned which is used to retrieve the file # Change the key of the blob to that of the hash def upload(key, io, checksum: nil, **) instrument :upload, key: key, checksum: checksum do data = @client.add io.path find_blob(key).update(key: data['Hash']) # TODO: Ensure integrity of checksum end end def download(key, &block) puts key if block_given? instrument :streaming_download, key: key do @client.download key, &block end else instrument :download, key: key do @client.download key end end end def download_chunk(key, range) instrument :download_chunk, key: key, range: range do @client.cat key, range.begin, range.size end end def url(key, content_type: nil, filename: nil, expires_in: nil, disposition: nil) @client.build_file_url key, filename.to_s end def exists?(key) instrument :exist, key: key do |payload| answer = @client.file_exists?(key) payload[:exist] = answer answer end end def url_for_direct_upload(key, expires_in:, content_type:, content_length:, checksum:) instrument :url_for_direct_upload, key: key do "#{@client.api_endpoint}/api/v0/add" end end private def find_blob(key) Blob.find_by_key key end end end