143 lines
4.4 KiB
Elixir
143 lines
4.4 KiB
Elixir
defmodule ApiWeb.AttachmentController do
|
|
use ApiWeb, :controller
|
|
require Logger
|
|
import Ecto.Query
|
|
alias Api.Repo
|
|
|
|
def index(conn, params) do
|
|
decode = """
|
|
SELECT convert_from(
|
|
decrypt(decode('#{params["idstudyattachment"]}', 'hex')::bytea, '1nf0rm3'::bytea, 'aes'),
|
|
'UTF8'
|
|
);
|
|
"""
|
|
|
|
idstudyattachment =
|
|
Repo.query!(decode).rows
|
|
|> hd()
|
|
|> hd()
|
|
|> String.to_integer()
|
|
|
|
[attachment | _] = get_attachment(idstudyattachment)
|
|
Logger.info("Adjunto: #{inspect(attachment)}")
|
|
|
|
case attachment do
|
|
# Caso 1: archivo en filesystem
|
|
%{path: path} when not is_nil(path) ->
|
|
if File.exists?(path) do
|
|
conn
|
|
|> put_resp_content_type("application/#{attachment[:format]}")
|
|
|> put_resp_header("content-disposition", "inline; filename=#{attachment[:fullname]}")
|
|
|> send_file(200, path)
|
|
else
|
|
conn |> send_resp(404, "File not found")
|
|
end
|
|
|
|
# Caso 2: archivo en S3 (zip → redirige)
|
|
%{s3_path: s3_path, format: "zip"} when not is_nil(s3_path) ->
|
|
conn |> redirect(external: s3_path)
|
|
|
|
# Caso 3: archivo en S3 (otros formatos → preview o descarga)
|
|
%{s3_path: s3_path} when not is_nil(s3_path) ->
|
|
redirect_or_preview(conn, s3_path, attachment[:fullname])
|
|
|
|
# Caso 4: ni path ni s3_path → usar campo attachment (bytea)
|
|
%{path: nil, s3_path: nil, attachment: bin} when not is_nil(bin) ->
|
|
content_type = content_type_for(attachment[:format])
|
|
disposition =
|
|
if content_type == "application/octet-stream" do
|
|
"attachment"
|
|
else
|
|
"inline"
|
|
end
|
|
|
|
conn
|
|
|> put_resp_content_type(content_type)
|
|
|> put_resp_header(
|
|
"content-disposition",
|
|
"#{disposition}; filename=#{attachment[:fullname]}"
|
|
)
|
|
|> send_resp(200, bin)
|
|
|
|
# Caso default
|
|
_ ->
|
|
conn |> send_resp(400, "Invalid attachment")
|
|
end
|
|
end
|
|
|
|
def get_attachment(idstudyattachment) do
|
|
query =
|
|
from sa in "studyattachments",
|
|
where: sa.idstudyattachment == ^idstudyattachment,
|
|
select: %{
|
|
attachment: sa.attachment,
|
|
path: sa.path,
|
|
s3_path: sa.s3_path,
|
|
format: sa.format,
|
|
fullname: fragment("concat(name, '.', format)")
|
|
}
|
|
|
|
Repo.all(query)
|
|
end
|
|
|
|
defp redirect_or_preview(conn, path, fullname) do
|
|
case HTTPoison.get(path, [], [follow_redirect: false, hackney: [pool: :default]]) do
|
|
{:ok, %HTTPoison.Response{status_code: 302, headers: headers}} ->
|
|
handle_redirect(conn, headers, fullname)
|
|
|
|
{:ok, %HTTPoison.Response{status_code: 200, body: body, headers: file_headers}} ->
|
|
preview_attachment(conn, body, file_headers, fullname)
|
|
|
|
{:error, reason} ->
|
|
Logger.error("Error al obtener el adjunto: #{inspect(reason)}")
|
|
conn |> send_resp(404, "File not found")
|
|
end
|
|
end
|
|
|
|
defp handle_redirect(conn, headers, fullname) do
|
|
case List.keyfind(headers, "location", 0) do
|
|
{"location", redirected_url} ->
|
|
Logger.info("Redirigiendo a: #{redirected_url}")
|
|
|
|
case HTTPoison.get(redirected_url, [], [follow_redirect: true]) do
|
|
{:ok, %HTTPoison.Response{status_code: 200, body: body, headers: file_headers}} ->
|
|
preview_attachment(conn, body, file_headers, fullname)
|
|
|
|
{:error, reason} ->
|
|
Logger.error("Error al descargar el adjunto tras redirección: #{inspect(reason)}")
|
|
conn |> send_resp(404, "File not found")
|
|
end
|
|
|
|
_ ->
|
|
Logger.error("Redirección sin ubicación válida.")
|
|
conn |> send_resp(404, "File not found")
|
|
end
|
|
end
|
|
|
|
defp preview_attachment(conn, body, file_headers, fullname) do
|
|
content_type = get_content_type(file_headers)
|
|
|
|
conn
|
|
|> put_resp_content_type(content_type)
|
|
|> put_resp_header("content-disposition", "inline; filename=#{fullname}")
|
|
|> send_resp(200, body)
|
|
end
|
|
|
|
defp get_content_type(headers) do
|
|
case List.keyfind(headers, "content-type", 0) do
|
|
{"content-type", content_type} -> content_type
|
|
_ -> "application/octet-stream"
|
|
end
|
|
end
|
|
|
|
defp content_type_for(format) do
|
|
case String.downcase(format || "") do
|
|
"pdf" -> "application/pdf"
|
|
"jpg" -> "image/jpeg"
|
|
"jpeg" -> "image/jpeg"
|
|
"png" -> "image/png"
|
|
_ -> "application/octet-stream"
|
|
end
|
|
end
|
|
end
|