2018-12-23 20:04:54 +00:00
|
|
|
# Pleroma: A lightweight social networking server
|
2023-01-02 20:38:50 +00:00
|
|
|
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
2018-12-23 20:04:54 +00:00
|
|
|
# SPDX-License-Identifier: AGPL-3.0-only
|
|
|
|
|
2018-08-28 01:20:54 +00:00
|
|
|
defmodule Pleroma.Uploaders.S3 do
|
2018-08-28 12:57:41 +00:00
|
|
|
@behaviour Pleroma.Uploaders.Uploader
|
2018-11-23 16:40:45 +00:00
|
|
|
require Logger
|
|
|
|
|
2023-12-11 05:25:05 +00:00
|
|
|
@ex_aws_impl Application.compile_env(:pleroma, [__MODULE__, :ex_aws_impl], ExAws)
|
|
|
|
@config_impl Application.compile_env(:pleroma, [__MODULE__, :config_impl], Pleroma.Config)
|
2019-08-10 11:27:59 +00:00
|
|
|
|
2019-03-05 04:37:33 +00:00
|
|
|
# The file name is re-encoded with S3's constraints here to comply with previous
|
|
|
|
# links with less strict filenames
|
2020-01-12 18:48:58 +00:00
|
|
|
@impl true
|
2018-11-23 16:40:45 +00:00
|
|
|
def get_file(file) do
|
|
|
|
{:ok,
|
|
|
|
{:url,
|
|
|
|
Path.join([
|
2021-01-08 16:49:12 +00:00
|
|
|
Pleroma.Upload.base_url(),
|
2018-11-23 16:40:45 +00:00
|
|
|
strict_encode(URI.decode(file))
|
|
|
|
])}}
|
|
|
|
end
|
2018-08-28 01:20:54 +00:00
|
|
|
|
2020-01-12 18:48:58 +00:00
|
|
|
@impl true
|
2019-02-06 19:19:39 +00:00
|
|
|
def put_file(%Pleroma.Upload{} = upload) do
|
2023-12-11 05:25:05 +00:00
|
|
|
config = @config_impl.get([__MODULE__])
|
2018-11-23 16:40:45 +00:00
|
|
|
bucket = Keyword.get(config, :bucket)
|
2019-09-23 20:38:53 +00:00
|
|
|
streaming = Keyword.get(config, :streaming_enabled)
|
2018-08-28 01:20:54 +00:00
|
|
|
|
2018-11-29 20:11:45 +00:00
|
|
|
s3_name = strict_encode(upload.path)
|
2018-08-28 01:20:54 +00:00
|
|
|
|
2018-11-23 16:40:45 +00:00
|
|
|
op =
|
2019-09-23 20:38:53 +00:00
|
|
|
if streaming do
|
2020-08-19 07:22:59 +00:00
|
|
|
op =
|
|
|
|
upload.tempfile
|
|
|
|
|> ExAws.S3.Upload.stream_file()
|
|
|
|
|> ExAws.S3.upload(bucket, s3_name, [
|
|
|
|
{:acl, :public_read},
|
|
|
|
{:content_type, upload.content_type}
|
|
|
|
])
|
|
|
|
|
2020-09-02 07:50:51 +00:00
|
|
|
if Application.get_env(:tesla, :adapter) == Tesla.Adapter.Gun do
|
|
|
|
# set s3 upload timeout to respect :upload pool timeout
|
|
|
|
# timeout should be slightly larger, so s3 can retry upload on fail
|
|
|
|
timeout = Pleroma.HTTP.AdapterHelper.Gun.pool_timeout(:upload) + 1_000
|
|
|
|
opts = Keyword.put(op.opts, :timeout, timeout)
|
|
|
|
Map.put(op, :opts, opts)
|
|
|
|
else
|
|
|
|
op
|
|
|
|
end
|
2019-09-23 20:38:53 +00:00
|
|
|
else
|
|
|
|
{:ok, file_data} = File.read(upload.tempfile)
|
|
|
|
|
|
|
|
ExAws.S3.put_object(bucket, s3_name, file_data, [
|
|
|
|
{:acl, :public_read},
|
|
|
|
{:content_type, upload.content_type}
|
|
|
|
])
|
|
|
|
end
|
2018-10-29 18:00:59 +00:00
|
|
|
|
2023-12-11 05:25:05 +00:00
|
|
|
case @ex_aws_impl.request(op) do
|
2018-11-23 16:40:45 +00:00
|
|
|
{:ok, _} ->
|
|
|
|
{:ok, {:file, s3_name}}
|
2018-10-29 18:00:59 +00:00
|
|
|
|
2018-11-23 16:40:45 +00:00
|
|
|
error ->
|
|
|
|
Logger.error("#{__MODULE__}: #{inspect(error)}")
|
|
|
|
{:error, "S3 Upload failed"}
|
|
|
|
end
|
2018-08-28 01:20:54 +00:00
|
|
|
end
|
2018-09-24 13:38:32 +00:00
|
|
|
|
2020-01-12 18:48:58 +00:00
|
|
|
@impl true
|
|
|
|
def delete_file(file) do
|
|
|
|
[__MODULE__, :bucket]
|
2023-12-11 05:25:05 +00:00
|
|
|
|> @config_impl.get()
|
2020-01-12 18:48:58 +00:00
|
|
|
|> ExAws.S3.delete_object(file)
|
2023-12-11 05:25:05 +00:00
|
|
|
|> @ex_aws_impl.request()
|
2020-01-12 18:48:58 +00:00
|
|
|
|> case do
|
|
|
|
{:ok, %{status_code: 204}} -> :ok
|
|
|
|
error -> {:error, inspect(error)}
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2018-11-23 16:40:45 +00:00
|
|
|
@regex Regex.compile!("[^0-9a-zA-Z!.*/'()_-]")
|
|
|
|
def strict_encode(name) do
|
|
|
|
String.replace(name, @regex, "-")
|
2018-09-24 13:38:32 +00:00
|
|
|
end
|
2018-08-28 01:20:54 +00:00
|
|
|
end
|
2023-12-11 05:25:05 +00:00
|
|
|
|
|
|
|
defmodule Pleroma.Uploaders.S3.ExAwsAPI do
|
|
|
|
@callback request(op :: ExAws.Operation.t()) :: {:ok, ExAws.Operation.t()} | {:error, term()}
|
|
|
|
end
|