From c6b38e2c0cd9b8d69b1238b6c65f870e3aa6b39c Mon Sep 17 00:00:00 2001 From: "aime.rolandi" Date: Fri, 22 Aug 2025 10:21:47 -0300 Subject: [PATCH] Adjuntos desde binario --- .../controllers/attachment_controller.ex | 124 ++++++++++++++++-- 1 file changed, 112 insertions(+), 12 deletions(-) diff --git a/lib/api_web/controllers/attachment_controller.ex b/lib/api_web/controllers/attachment_controller.ex index cdc57d1..d4ecc84 100644 --- a/lib/api_web/controllers/attachment_controller.ex +++ b/lib/api_web/controllers/attachment_controller.ex @@ -6,23 +6,62 @@ defmodule ApiWeb.AttachmentController do def index(conn, params) do decode = """ - SELECT convert_from(decrypt(decode('#{params["idstudyattachment"]}', 'hex')::bytea, '1nf0rm3'::bytea, 'aes'), 'UTF8'); + SELECT convert_from( + decrypt(decode('#{params["idstudyattachment"]}', 'hex')::bytea, '1nf0rm3'::bytea, 'aes'), + 'UTF8' + ); """ - idstudyattachment = Repo.query!(decode).rows |> hd() |> hd() - idstudyattachment = String.to_integer(idstudyattachment) + + idstudyattachment = + Repo.query!(decode).rows + |> hd() + |> hd() + |> String.to_integer() [attachment | _] = get_attachment(idstudyattachment) Logger.info("Adjunto: #{inspect(attachment)}") - file_path = attachment[:path] - if File.exists?(file_path) do - conn - |> put_resp_content_type("application/#{attachment[:format]}") - |> put_resp_header("content-disposition", "inline; filename=#{attachment[:fullname]}") - |> send_file(200, file_path) - else - conn - |> send_resp(404, "File not found") + 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 @@ -37,6 +76,67 @@ defmodule ApiWeb.AttachmentController do 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