lib/services/s3_service.ex

defmodule ActiveStorage.Service.S3Service do
  def service_name do
    :amazon
  end

  def upload_headers(content_type, blob) do
    %{
      "Content-Type": content_type,
      # "Content-MD5": checksum,
      "Content-Disposition":
        "inline; filename=\"#{blob.id}.png\"; filename*=UTF-8''#{blob.id}.png"
    }
  end

  def presigned_url(blob) do
    bucket = System.fetch_env!("AWS_S3_BUCKET")

    ExAws.Config.new(:s3)
    |> ExAws.S3.presigned_url(:get, bucket, blob.id)
  end

  def private_url(blob) do
    bucket = System.fetch_env!("AWS_S3_BUCKET")

    # object_for(key).presigned_url :get, expires_in: expires_in.to_i,
    #  response_content_disposition: content_disposition_with(type: disposition, filename: filename),
    #  response_content_type: content_type
    case ExAws.Config.new(:s3) |> ExAws.S3.presigned_url(:get, bucket, blob.id) do
      {:ok, url} -> url
      _ -> nil
    end
  end

  def public_url(key) do
    bucket = System.fetch_env!("AWS_S3_BUCKET")

    # object_for(key).public_url
    case ExAws.Config.new(:s3)
         |> ExAws.S3.get_object(bucket, key) do
      {:ok, url} -> url
      _ -> nil
    end
  end

  # https://www.poeticoding.com/aws-s3-in-elixir-with-exaws/

  # def upload(key, io, %{checksum: nil, filename: nil, content_type: nil, disposition: nil}) do
  def upload(blob, io) do
    # stream(io)
    bucket = System.fetch_env!("AWS_S3_BUCKET")

    operation =
      ExAws.S3.put_object(
        bucket,
        blob.id,
        io
      )

    ExAws.request(operation)
  end

  def download(key) do
    _bucket = System.fetch_env!("AWS_S3_BUCKET")

    case object_for(key) do
      {:ok, %{body: body}} -> body
      _ -> nil
    end

    # ExAws.S3.download_file(
    #  bucket,
    #  key,
    #  "local_file.txt"
    # )
    # |> ExAws.request!()
  end

  def stream(filename) do
    bucket = System.fetch_env!("AWS_S3_BUCKET")
    ExAws.S3.Upload.stream_file(filename) |> ExAws.S3.upload(bucket, filename) |> ExAws.request!()
  end

  # def url(blob) do
  #  signed_blob_id = Chaskiq.Verifier.sign(blob.id)
  #  ActiveStorage.service_url(signed_blob_id)
  # end

  # def open(*args, **options, &block) do
  def open(blob, args, block) do
    # .open(*args, **options, &block)
    ActiveStorage.Downloader.new(__MODULE__)
    |> ActiveStorage.Downloader.open(blob, args, block)
  end

  def create_direct_upload(blob, %{
        byte_size: _byte_size,
        checksum: _checksum,
        content_type: content_type,
        filename: _filename
      }) do
    bucket = System.fetch_env!("AWS_S3_BUCKET")

    # TODO: refactor this to accept local service too

    {:ok, url} = ExAws.Config.new(:s3) |> ExAws.S3.presigned_url(:put, bucket, blob.id)

    headers = Jason.encode!(upload_headers(content_type, blob))

    %{
      url: url,
      service_url: ActiveStorage.service_url(blob),
      headers: headers,
      blob_id: blob.id,
      signed_blob_id: Chaskiq.Verifier.sign(blob.id)
    }
  end

  def exist?(key) do
    bucket = System.fetch_env!("AWS_S3_BUCKET")
    # instrument :exist, key: key do |payload|
    # answer = object_for(key)
    case ExAws.S3.head_object(bucket, key) |> ExAws.request() do
      {:ok, _} -> true
      {:error, _} -> false
    end

    # .exists?
    # payload[:exist] = answer
    # answer
    # end
  end

  def object_for(key) do
    bucket = System.fetch_env!("AWS_S3_BUCKET")

    ExAws.S3.get_object(bucket, key) |> ExAws.request()
  end
end

# #<ActiveStorage::Service::S3Service:0x00007fb8c88a6078
# @bucket=
#   #<Aws::S3::Bucket:0x00007fb8c888ee50
#    @arn=nil,
#    @client=#<Aws::S3::Client>,
#    @data=nil,
#    @name="hermessapp",
#    @resolved_region="us-east-1",
#    @waiter_block_warned=false>,
#  @client=#<Aws::S3::Resource:0x00007fb8c88a5fd8 @client=#<Aws::S3::Client>>,
#  @multipart_upload_threshold=104857600,
#  @name=:amazon,
#  @public=false,
#  @upload_options={}>