Fork of Pleroma with site-specific changes and feature branches https://git.pleroma.social/pleroma/pleroma
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

85 lines
2.7KB

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