[Credo][CI] Add readability as it’s fixed
[akkoma] / lib / pleroma / uploaders / uploader.ex
index b58fc6d713d1dfec501922242e7239e175ba4e2a..bf15389fcde3c46ed9ecf9f8a1c5f3ebcd514813 100644 (file)
@@ -1,20 +1,72 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
 defmodule Pleroma.Uploaders.Uploader do
   @moduledoc """
-  Defines the contract to put an uploaded file to any backend.
+  Defines the contract to put and get an uploaded file to any backend.
   """
 
+  @doc """
+  Instructs how to get the file from the backend.
+
+  Used by `Pleroma.Plugs.UploadedMedia`.
+  """
+  @type get_method :: {:static_dir, directory :: String.t()} | {:url, url :: String.t()}
+  @callback get_file(file :: String.t()) :: {:ok, get_method()}
+
   @doc """
   Put a file to the backend.
 
-  Returns `{:ok, String.t } | {:error, String.t} containing the path of the 
-  uploaded file, or error information if the file failed to be saved to the 
-  respective backend.
+  Returns:
+
+  * `:ok` which assumes `{:ok, upload.path}`
+  * `{:ok, spec}` where spec is:
+    * `{:file, filename :: String.t}` to handle reads with `get_file/1` (recommended)
+
+    This allows to correctly proxy or redirect requests to the backend, while allowing to migrate backends without breaking any URL.
+  * `{url, url :: String.t}` to bypass `get_file/2` and use the `url` directly in the activity.
+  * `{:error, String.t}` error information if the file failed to be saved to the backend.
+  * `:wait_callback` will wait for an http post request at `/api/pleroma/upload_callback/:upload_path` and call the uploader's `http_callback/3` method.
+
   """
-  @callback put_file(
-              name :: String.t(),
-              uuid :: String.t(),
-              file :: File.t(),
-              content_type :: String.t(),
-              should_dedupe :: Boolean.t()
-            ) :: {:ok, String.t()} | {:error, String.t()}
+  @type file_spec :: {:file | :url, String.t()}
+  @callback put_file(Pleroma.Upload.t()) ::
+              :ok | {:ok, file_spec()} | {:error, String.t()} | :wait_callback
+
+  @callback http_callback(Plug.Conn.t(), Map.t()) ::
+              {:ok, Plug.Conn.t()}
+              | {:ok, Plug.Conn.t(), file_spec()}
+              | {:error, Plug.Conn.t(), String.t()}
+  @optional_callbacks http_callback: 2
+
+  @spec put_file(module(), Pleroma.Upload.t()) :: {:ok, file_spec()} | {:error, String.t()}
+
+  def put_file(uploader, upload) do
+    case uploader.put_file(upload) do
+      :ok -> {:ok, {:file, upload.path}}
+      :wait_callback -> handle_callback(uploader, upload)
+      {:ok, _} = ok -> ok
+      {:error, _} = error -> error
+    end
+  end
+
+  defp handle_callback(uploader, upload) do
+    :global.register_name({__MODULE__, upload.path}, self())
+
+    receive do
+      {__MODULE__, pid, conn, params} ->
+        case uploader.http_callback(conn, params) do
+          {:ok, conn, ok} ->
+            send(pid, {__MODULE__, conn})
+            {:ok, ok}
+
+          {:error, conn, error} ->
+            send(pid, {__MODULE__, conn})
+            {:error, error}
+        end
+    after
+      30_000 -> {:error, "Uploader callback timeout"}
+    end
+  end
 end