move to 2.5.5
[anni] / lib / pleroma / uploaders / uploader.ex
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
4
5 defmodule Pleroma.Uploaders.Uploader do
6   import Pleroma.Web.Gettext
7
8   @mix_env Mix.env()
9
10   @moduledoc """
11   Defines the contract to put and get an uploaded file to any backend.
12   """
13
14   @doc """
15   Instructs how to get the file from the backend.
16
17   Used by `Pleroma.Web.Plugs.UploadedMedia`.
18   """
19   @type get_method :: {:static_dir, directory :: String.t()} | {:url, url :: String.t()}
20   @callback get_file(file :: String.t()) :: {:ok, get_method()}
21
22   @doc """
23   Put a file to the backend.
24
25   Returns:
26
27   * `:ok` which assumes `{:ok, upload.path}`
28   * `{:ok, spec}` where spec is:
29     * `{:file, filename :: String.t}` to handle reads with `get_file/1` (recommended)
30
31     This allows to correctly proxy or redirect requests to the backend, while allowing to migrate backends without breaking any URL.
32   * `{url, url :: String.t}` to bypass `get_file/2` and use the `url` directly in the activity.
33   * `{:error, String.t}` error information if the file failed to be saved to the backend.
34   * `: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.
35
36   """
37   @type file_spec :: {:file | :url, String.t()}
38   @callback put_file(upload :: struct()) ::
39               :ok | {:ok, file_spec()} | {:error, String.t()} | :wait_callback
40
41   @callback delete_file(file :: String.t()) :: :ok | {:error, String.t()}
42
43   @callback http_callback(Plug.Conn.t(), Map.t()) ::
44               {:ok, Plug.Conn.t()}
45               | {:ok, Plug.Conn.t(), file_spec()}
46               | {:error, Plug.Conn.t(), String.t()}
47   @optional_callbacks http_callback: 2
48
49   @spec put_file(module(), upload :: struct()) :: {:ok, file_spec()} | {:error, String.t()}
50   def put_file(uploader, upload) do
51     case uploader.put_file(upload) do
52       :ok -> {:ok, {:file, upload.path}}
53       :wait_callback -> handle_callback(uploader, upload)
54       {:ok, _} = ok -> ok
55       {:error, _} = error -> error
56     end
57   end
58
59   defp handle_callback(uploader, upload) do
60     :global.register_name({__MODULE__, upload.path}, self())
61
62     receive do
63       {__MODULE__, pid, conn, params} ->
64         case uploader.http_callback(conn, params) do
65           {:ok, conn, ok} ->
66             send(pid, {__MODULE__, conn})
67             {:ok, ok}
68
69           {:error, conn, error} ->
70             send(pid, {__MODULE__, conn})
71             {:error, error}
72         end
73     after
74       callback_timeout() -> {:error, dgettext("errors", "Uploader callback timeout")}
75     end
76   end
77
78   defp callback_timeout do
79     case @mix_env do
80       :test -> 1_000
81       _ -> 30_000
82     end
83   end
84 end