diff options
Diffstat (limited to 'lib/pleroma/uploaders')
-rw-r--r-- | lib/pleroma/uploaders/local.ex | 55 | ||||
-rw-r--r-- | lib/pleroma/uploaders/mdii.ex | 17 | ||||
-rw-r--r-- | lib/pleroma/uploaders/s3.ex | 56 | ||||
-rw-r--r-- | lib/pleroma/uploaders/swift/swift.ex | 2 | ||||
-rw-r--r-- | lib/pleroma/uploaders/swift/uploader.ex | 13 | ||||
-rw-r--r-- | lib/pleroma/uploaders/uploader.ex | 42 |
6 files changed, 102 insertions, 83 deletions
diff --git a/lib/pleroma/uploaders/local.ex b/lib/pleroma/uploaders/local.ex index d96481c8d..434a6b515 100644 --- a/lib/pleroma/uploaders/local.ex +++ b/lib/pleroma/uploaders/local.ex @@ -3,49 +3,32 @@ defmodule Pleroma.Uploaders.Local do alias Pleroma.Web - def put_file(name, uuid, tmpfile, _content_type, should_dedupe) do - upload_folder = get_upload_path(uuid, should_dedupe) - url_path = get_url(name, uuid, should_dedupe) - - File.mkdir_p!(upload_folder) + def get_file(_) do + {:ok, {:static_dir, upload_path()}} + end - result_file = Path.join(upload_folder, name) + def put_file(upload) do + {local_path, file} = + case Enum.reverse(String.split(upload.path, "/", trim: true)) do + [file] -> + {upload_path(), file} - if File.exists?(result_file) do - File.rm!(tmpfile) - else - File.cp!(tmpfile, result_file) - end + [file | folders] -> + path = Path.join([upload_path()] ++ Enum.reverse(folders)) + File.mkdir_p!(path) + {path, file} + end - {:ok, url_path} - end + result_file = Path.join(local_path, file) - def upload_path do - settings = Application.get_env(:pleroma, Pleroma.Uploaders.Local) - Keyword.fetch!(settings, :uploads) - end - - defp get_upload_path(uuid, should_dedupe) do - if should_dedupe do - upload_path() - else - Path.join(upload_path(), uuid) + unless File.exists?(result_file) do + File.cp!(upload.tempfile, result_file) end - end - defp get_url(name, uuid, should_dedupe) do - if should_dedupe do - url_for(:cow_uri.urlencode(name)) - else - url_for(Path.join(uuid, :cow_uri.urlencode(name))) - end + :ok end - defp url_for(file) do - settings = Application.get_env(:pleroma, Pleroma.Uploaders.Local) - - Keyword.get(settings, :uploads_url) - |> String.replace("{{file}}", file) - |> String.replace("{{base_url}}", Web.base_url()) + def upload_path do + Pleroma.Config.get!([__MODULE__, :uploads]) end end diff --git a/lib/pleroma/uploaders/mdii.ex b/lib/pleroma/uploaders/mdii.ex index a9d52b0dc..35d36d3e4 100644 --- a/lib/pleroma/uploaders/mdii.ex +++ b/lib/pleroma/uploaders/mdii.ex @@ -5,22 +5,27 @@ defmodule Pleroma.Uploaders.MDII do @httpoison Application.get_env(:pleroma, :httpoison) - def put_file(name, uuid, path, content_type, should_dedupe) do + # MDII-hosted images are never passed through the MediaPlug; only local media. + # Delegate to Pleroma.Uploaders.Local + def get_file(file) do + Pleroma.Uploaders.Local.get_file(file) + end + + def put_file(upload) do cgi = Pleroma.Config.get([Pleroma.Uploaders.MDII, :cgi]) files = Pleroma.Config.get([Pleroma.Uploaders.MDII, :files]) - {:ok, file_data} = File.read(path) + {:ok, file_data} = File.read(upload.tempfile) - extension = String.split(name, ".") |> List.last() + extension = String.split(upload.name, ".") |> List.last() query = "#{cgi}?#{extension}" with {:ok, %{status_code: 200, body: body}} <- @httpoison.post(query, file_data) do - File.rm!(path) remote_file_name = String.split(body) |> List.first() public_url = "#{files}/#{remote_file_name}.#{extension}" - {:ok, public_url} + {:ok, {:url, public_url}} else - _ -> Pleroma.Uploaders.Local.put_file(name, uuid, path, content_type, should_dedupe) + _ -> Pleroma.Uploaders.Local.put_file(upload) end end end diff --git a/lib/pleroma/uploaders/s3.ex b/lib/pleroma/uploaders/s3.ex index 40a836460..19832a7ec 100644 --- a/lib/pleroma/uploaders/s3.ex +++ b/lib/pleroma/uploaders/s3.ex @@ -1,40 +1,46 @@ defmodule Pleroma.Uploaders.S3 do - alias Pleroma.Web.MediaProxy - @behaviour Pleroma.Uploaders.Uploader + require Logger + + # The file name is re-encoded with S3's constraints here to comply with previous links with less strict filenames + def get_file(file) do + config = Pleroma.Config.get([__MODULE__]) + + {:ok, + {:url, + Path.join([ + Keyword.fetch!(config, :public_endpoint), + Keyword.fetch!(config, :bucket), + strict_encode(URI.decode(file)) + ])}} + end - def put_file(name, uuid, path, content_type, _should_dedupe) do - settings = Application.get_env(:pleroma, Pleroma.Uploaders.S3) - bucket = Keyword.fetch!(settings, :bucket) - public_endpoint = Keyword.fetch!(settings, :public_endpoint) - force_media_proxy = Keyword.fetch!(settings, :force_media_proxy) - - {:ok, file_data} = File.read(path) + def put_file(upload = %Pleroma.Upload{}) do + config = Pleroma.Config.get([__MODULE__]) + bucket = Keyword.get(config, :bucket) - File.rm!(path) + {:ok, file_data} = File.read(upload.tempfile) - s3_name = "#{uuid}/#{encode(name)}" + s3_name = strict_encode(upload.path) - {:ok, _} = + op = ExAws.S3.put_object(bucket, s3_name, file_data, [ {:acl, :public_read}, - {:content_type, content_type} + {:content_type, upload.content_type} ]) - |> ExAws.request() - - url_base = "#{public_endpoint}/#{bucket}/#{s3_name}" - public_url = - if force_media_proxy do - MediaProxy.url(url_base) - else - url_base - end + case ExAws.request(op) do + {:ok, _} -> + {:ok, {:file, s3_name}} - {:ok, public_url} + error -> + Logger.error("#{__MODULE__}: #{inspect(error)}") + {:error, "S3 Upload failed"} + end end - defp encode(name) do - String.replace(name, ~r/[^0-9a-zA-Z!.*'()_-]/, "-") + @regex Regex.compile!("[^0-9a-zA-Z!.*/'()_-]") + def strict_encode(name) do + String.replace(name, @regex, "-") end end diff --git a/lib/pleroma/uploaders/swift/swift.ex b/lib/pleroma/uploaders/swift/swift.ex index fa08ca966..1e865f101 100644 --- a/lib/pleroma/uploaders/swift/swift.ex +++ b/lib/pleroma/uploaders/swift/swift.ex @@ -14,7 +14,7 @@ defmodule Pleroma.Uploaders.Swift.Client do case put("#{filename}", body, "X-Auth-Token": token, "Content-Type": content_type) do {:ok, %HTTPoison.Response{status_code: 201}} -> - {:ok, "#{object_url}/#{filename}"} + {:ok, {:file, filename}} {:ok, %HTTPoison.Response{status_code: 401}} -> {:error, "Unauthorized, Bad Token"} diff --git a/lib/pleroma/uploaders/swift/uploader.ex b/lib/pleroma/uploaders/swift/uploader.ex index 794f76cb0..b35b9807b 100644 --- a/lib/pleroma/uploaders/swift/uploader.ex +++ b/lib/pleroma/uploaders/swift/uploader.ex @@ -1,10 +1,15 @@ defmodule Pleroma.Uploaders.Swift do @behaviour Pleroma.Uploaders.Uploader - def put_file(name, uuid, tmp_path, content_type, _should_dedupe) do - {:ok, file_data} = File.read(tmp_path) - remote_name = "#{uuid}/#{name}" + def get_file(name) do + {:ok, {:url, Path.join([Pleroma.Config.get!([__MODULE__, :object_url]), name])}} + end - Pleroma.Uploaders.Swift.Client.upload_file(remote_name, file_data, content_type) + def put_file(upload) do + Pleroma.Uploaders.Swift.Client.upload_file( + upload.path, + File.read!(upload.tmpfile), + upload.content_type + ) end end diff --git a/lib/pleroma/uploaders/uploader.ex b/lib/pleroma/uploaders/uploader.ex index b58fc6d71..afda5609e 100644 --- a/lib/pleroma/uploaders/uploader.ex +++ b/lib/pleroma/uploaders/uploader.ex @@ -1,20 +1,40 @@ 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. + + """ - @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()} + @callback put_file(Pleroma.Upload.t()) :: + :ok | {:ok, {:file | :url, String.t()}} | {:error, String.t()} + + @spec put_file(module(), Pleroma.Upload.t()) :: + {:ok, {:file | :url, String.t()}} | {:error, String.t()} + def put_file(uploader, upload) do + case uploader.put_file(upload) do + :ok -> {:ok, {:file, upload.path}} + other -> other + end + end end |