commit: fa2a6d5d6b24657ddbda4ef11d2e6dbcb59545d3
parent 29f4ab640b0269fc7751fca7c24cda5be912d1e5
Author: Claudio Maradonna <penguyman@stronzi.org>
Date:   Thu,  7 Apr 2022 18:25:02 +0200
feat: simple, but not stupid, uploader for IPFS
fix: format fix with credo
Diffstat:
5 files changed, 107 insertions(+), 2 deletions(-)
diff --git a/config/config.exs b/config/config.exs
@@ -82,6 +82,10 @@ config :ex_aws, :s3,
   # region: "us-east-1", # may be required for Amazon AWS
   scheme: "https://"
 
+config :pleroma, Pleroma.Uploaders.IPFS,
+  post_gateway_url: nil,
+  get_gateway_url: nil
+
 config :pleroma, :emoji,
   shortcode_globs: ["/emoji/custom/**/*.png"],
   pack_extensions: [".png", ".gif"],
diff --git a/config/description.exs b/config/description.exs
@@ -138,6 +138,30 @@ config :pleroma, :config_description, [
   },
   %{
     group: :pleroma,
+    key: Pleroma.Uploaders.IPFS,
+    type: :group,
+    description: "IPFS uploader-related settings",
+    children: [
+      %{
+        key: :get_gateway_url,
+        type: :string,
+        description: "GET Gateway URL",
+        suggestions: [
+          "get_gateway_url"
+        ]
+      },
+      %{
+        key: :post_gateway_url,
+        type: :string,
+        description: "POST Gateway URL",
+        suggestions: [
+          "post_gateway_url"
+        ]
+      }
+    ]
+  },
+  %{
+    group: :pleroma,
     key: Pleroma.Uploaders.S3,
     type: :group,
     description: "S3 uploader-related settings",
diff --git a/config/dev.exs b/config/dev.exs
@@ -58,6 +58,10 @@ config :pleroma, Pleroma.Web.ApiSpec.CastAndValidate, strict: true
 # https://dashbit.co/blog/speeding-up-re-compilation-of-elixir-projects
 config :phoenix, :plug_init_mode, :runtime
 
+config :pleroma, Pleroma.Uploaders.IPFS,
+  post_gateway_url: nil,
+  get_gateway_url: nil
+
 if File.exists?("./config/dev.secret.exs") do
   import_config "dev.secret.exs"
 else
diff --git a/lib/pleroma/upload.ex b/lib/pleroma/upload.ex
@@ -235,8 +235,14 @@ defmodule Pleroma.Upload do
           ""
         end
 
-    [base_url, path]
-    |> Path.join()
+    uploader = Config.get([Pleroma.Upload, :uploader])
+
+    if uploader == Pleroma.Uploaders.IPFS && String.contains?(base_url, "{CID}") do
+      String.replace(base_url, "{CID}", path)
+    else
+      [base_url, path]
+      |> Path.join()
+    end
   end
 
   defp url_from_spec(_upload, _base_url, {:url, url}), do: url
@@ -273,6 +279,9 @@ defmodule Pleroma.Upload do
           Path.join([upload_base_url, bucket_with_namespace])
         end
 
+      Pleroma.Uploaders.IPFS ->
+        Config.get([Pleroma.Uploaders.IPFS, :get_gateway_url])
+
       _ ->
         public_endpoint || upload_base_url || Pleroma.Web.Endpoint.url() <> "/media/"
     end
diff --git a/lib/pleroma/uploaders/ipfs.ex b/lib/pleroma/uploaders/ipfs.ex
@@ -0,0 +1,64 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Uploaders.IPFS do
+  @behaviour Pleroma.Uploaders.Uploader
+  require Logger
+
+  alias Pleroma.Config
+  alias Tesla.Multipart
+
+  @impl true
+  def get_file(file) do
+    b_url = Pleroma.Upload.base_url()
+
+    if String.contains?(b_url, "{CID}") do
+      {:ok, {:url, String.replace(b_url, "{CID}", URI.decode(file))}}
+    else
+      {:error, "IPFS Get URL doesn't contain '{CID}' placeholder"}
+    end
+  end
+
+  @impl true
+  def put_file(%Pleroma.Upload{} = upload) do
+    config = Config.get([__MODULE__])
+    post_base_url = Keyword.get(config, :post_gateway_url)
+
+    mp =
+      Multipart.new()
+      |> Multipart.add_content_type_param("charset=utf-8")
+      |> Multipart.add_file(upload.tempfile)
+
+    final_url = Path.join([post_base_url, "/api/v0/add"])
+
+    case Pleroma.HTTP.post(final_url, mp, [], params: ["cid-version": "1"]) do
+      {:ok, ret} ->
+        case Jason.decode(ret.body) do
+          {:ok, ret} ->
+            {:ok, {:file, ret["Hash"]}}
+
+          error ->
+            Logger.error("#{__MODULE__}: #{inspect(error)}")
+            {:error, "JSON decode failed"}
+        end
+
+      error ->
+        Logger.error("#{__MODULE__}: #{inspect(error)}")
+        {:error, "IPFS Gateway Upload failed"}
+    end
+  end
+
+  @impl true
+  def delete_file(file) do
+    config = Config.get([__MODULE__])
+    post_base_url = Keyword.get(config, :post_gateway_url)
+
+    final_url = Path.join([post_base_url, "/api/v0/files/rm"])
+
+    case Pleroma.HTTP.post(final_url, "", [], params: [arg: file]) do
+      {:ok, %{status_code: 204}} -> :ok
+      error -> {:error, inspect(error)}
+    end
+  end
+end