From 3b63d75219ef12ecb61819c49499a7b7c098a610 Mon Sep 17 00:00:00 2001 From: zack Date: Fri, 25 Oct 2024 17:17:56 -0400 Subject: [PATCH] fix images --- deps.nix | 15 ++++ flake.nix | 3 +- .../controllers/image_controller.ex | 17 ++++- lib/zoeyscomputer_web/plugs/discord_plug.ex | 55 --------------- lib/zoeyscomputer_web/router.ex | 69 ++++++++++++++----- mix.exs | 1 + mix.lock | 1 + 7 files changed, 85 insertions(+), 76 deletions(-) delete mode 100644 lib/zoeyscomputer_web/plugs/discord_plug.ex diff --git a/deps.nix b/deps.nix index 01da3de..f21bb8c 100644 --- a/deps.nix +++ b/deps.nix @@ -527,6 +527,21 @@ let beamDeps = [ castore hpax ]; }; + mogrify = + let + version = "0.9.3"; + in + buildMix { + inherit version; + name = "mogrify"; + + src = fetchHex { + inherit version; + pkg = "mogrify"; + sha256 = "0189b1e1de27455f2b9ae8cf88239cefd23d38de9276eb5add7159aea51731e6"; + }; + }; + nimble_options = let version = "1.1.1"; diff --git a/flake.nix b/flake.nix index f046d67..f5df256 100644 --- a/flake.nix +++ b/flake.nix @@ -290,7 +290,7 @@ import ./deps.nix { inherit pkgs lib beamPackages; }; - buildInputs = with pkgs; [nodejs]; + buildInputs = with pkgs; [nodejs imagemagick]; postBuild = '' echo ${heroicons} @@ -314,6 +314,7 @@ nodejs mix2nix node2nix + imagemagick # Add the language server of your choice. inputs.lexical.packages.${system}.default # I once added Hex via a Nix development shell, but now I install diff --git a/lib/zoeyscomputer_web/controllers/image_controller.ex b/lib/zoeyscomputer_web/controllers/image_controller.ex index 84b70b4..ced0ca0 100644 --- a/lib/zoeyscomputer_web/controllers/image_controller.ex +++ b/lib/zoeyscomputer_web/controllers/image_controller.ex @@ -1,6 +1,8 @@ defmodule ZoeyscomputerWeb.ImageController do use ZoeyscomputerWeb, :controller + import Mogrify + alias Zoeyscomputer.IdGenerator alias Zoeyscomputer.Images alias Zoeyscomputer.Images.Image @@ -59,12 +61,23 @@ defmodule ZoeyscomputerWeb.ImageController do defp handle_upload(upload) do extension = Path.extname(upload.filename) id = IdGenerator.generate() - key = "uploads/#{id}#{extension}" + key = "uploads/#{id}.png" bucket = "imgs" + local_path = "/tmp/#{id}#{extension}" + + IO.puts("upload path: #{upload.path}") {:ok, file_binary} = File.read(upload.path) - case ExAws.S3.put_object(bucket, key, file_binary) + File.write!(local_path, file_binary) + + IO.puts("wrote to #{local_path}") + image = open(local_path) |> format("png") |> save() + IO.inspect(image) + + {:ok, new_file_binary} = File.read(image.path) + + case ExAws.S3.put_object(bucket, key, new_file_binary, %{content_type: "image/png"}) |> ExAws.request() do {:ok, _response} -> {:ok, id} {:error, reason} -> {:error, reason} diff --git a/lib/zoeyscomputer_web/plugs/discord_plug.ex b/lib/zoeyscomputer_web/plugs/discord_plug.ex deleted file mode 100644 index a26ed73..0000000 --- a/lib/zoeyscomputer_web/plugs/discord_plug.ex +++ /dev/null @@ -1,55 +0,0 @@ -defmodule ZoeyscomputerWeb.DiscordPlug do - alias ExAws.S3 - alias Zoeyscomputer.Images - import Plug.Conn - - def init(opts), do: opts - - def call(%Plug.Conn{path_info: ["images", id]} = conn, _opts) do - case Images.get_image_by!(id) do - nil -> - conn - |> send_resp(404, "Image not found") - |> halt() - - image -> - serve_s3_image(conn, image) - end - end - - def call(conn, _opts), do: conn - - defp serve_s3_image(conn, image) do - key = image.key - bucket = "imgs" - - case download_from_s3(bucket, key) do - {:ok, image_binary, content_type} -> - conn - |> put_resp_header("content-type", content_type) - |> send_resp(200, image_binary) - |> halt() - - {:error, _reason} -> - conn - |> send_resp(500, "Failed to retrieve image") - |> halt() - end - end - - defp download_from_s3(bucket, key) do - case S3.get_object(bucket, key) |> ExAws.request() do - {:ok, %{body: image_binary, headers: headers}} -> - content_type = - Enum.find_value(headers, fn - {"Content-Type", value} -> value - {"content-type", value} -> value - end) - - {:ok, image_binary, content_type || "application/octet-stream"} - - error -> - error - end - end -end diff --git a/lib/zoeyscomputer_web/router.ex b/lib/zoeyscomputer_web/router.ex index 3eede26..89e8040 100644 --- a/lib/zoeyscomputer_web/router.ex +++ b/lib/zoeyscomputer_web/router.ex @@ -1,4 +1,6 @@ defmodule ZoeyscomputerWeb.Router do + alias ExAws.S3 + alias Zoeyscomputer.Images alias ZoeyscomputerWeb.DiscordPlug use ZoeyscomputerWeb, :router @@ -11,6 +13,7 @@ defmodule ZoeyscomputerWeb.Router do plug :put_root_layout, html: {ZoeyscomputerWeb.Layouts, :root} plug :protect_from_forgery plug :put_secure_browser_headers + plug :handle_discord plug :fetch_current_user end @@ -18,10 +21,6 @@ defmodule ZoeyscomputerWeb.Router do plug ZoeyscomputerWeb.Plugs.ApiAuthentication end - pipeline :discord do - plug DiscordPlug - end - pipeline :api do plug :accepts, ["json"] end @@ -108,28 +107,62 @@ defmodule ZoeyscomputerWeb.Router do live "/users/confirm/:token", UserConfirmationLive, :edit live "/users/confirm", UserConfirmationInstructionsLive, :new live "/", HomeLive, :index + + live "/images/:id", ImageLive.Show, :show end end scope "/", ZoeyscomputerWeb do - pipe_through :check_discord - live "/images/:id", ImageController, :show + pipe_through [:browser, :require_authenticated_user] end - def check_discord(conn, _opts) do - case get_req_header(conn, "user-agent") do - ["Discord" <> _rest | _] -> - conn - |> put_private(:phoenix_pipeline, {:doscord, []}) - |> DiscordPlug.call([]) + defp download_from_s3(bucket, key) do + case S3.get_object(bucket, key) |> ExAws.request() do + {:ok, %{body: image_binary, headers: headers}} -> + content_type = + Enum.find_value(headers, fn + {"Content-Type", value} -> value + {"content-type", value} -> value + _ -> nil + end) - ["Discord-Bot/" <> _rest | _] -> - conn - |> put_private(:phoenix_pipeline, {:discord, []}) - |> DiscordPlug.call([]) + {:ok, image_binary, content_type || "application/octet-stream"} - _ -> - conn |> put_private(:phoenix_pipeline, {:browser, []}) + error -> + error + end + end + + # Updated plug to return ID as string for Discord requests + def handle_discord(conn, _opts) do + is_discord = + case get_req_header(conn, "user-agent") do + ["Discord" <> _rest | _] -> true + ["Discord-Bot/" <> _rest | _] -> true + _ -> false + end + + if true do + # Extract the ID from the path + id = List.last(conn.path_info) + + case download_from_s3("imgs", "uploads/#{id}.png") do + {:ok, image_binary, content_type} -> + conn + |> put_resp_content_type(content_type) + |> send_resp(200, image_binary) + |> halt() + + {:error, reason} -> + IO.puts(reason) + + conn + |> put_resp_content_type("text/plain") + |> send_resp(500, "failed to retrieve image") + |> halt() + end + else + conn end end end diff --git a/mix.exs b/mix.exs index ed9016f..adb0120 100644 --- a/mix.exs +++ b/mix.exs @@ -46,6 +46,7 @@ defmodule Zoeyscomputer.MixProject do {:phoenix_live_dashboard, "~> 0.8.3"}, {:esbuild, "~> 0.8", runtime: Mix.env() == :dev}, {:tailwind, "~> 0.2", runtime: Mix.env() == :dev}, + {:mogrify, "~> 0.9.3"}, {:heroicons, github: "tailwindlabs/heroicons", tag: "v2.1.1", diff --git a/mix.lock b/mix.lock index aa33170..cf4aa5c 100644 --- a/mix.lock +++ b/mix.lock @@ -29,6 +29,7 @@ "mime": {:hex, :mime, "2.0.6", "8f18486773d9b15f95f4f4f1e39b710045fa1de891fada4516559967276e4dc2", [:mix], [], "hexpm", "c9945363a6b26d747389aac3643f8e0e09d30499a138ad64fe8fd1d13d9b153e"}, "mimerl": {:hex, :mimerl, "1.3.0", "d0cd9fc04b9061f82490f6581e0128379830e78535e017f7780f37fea7545726", [:rebar3], [], "hexpm", "a1e15a50d1887217de95f0b9b0793e32853f7c258a5cd227650889b38839fe9d"}, "mint": {:hex, :mint, "1.6.2", "af6d97a4051eee4f05b5500671d47c3a67dac7386045d87a904126fd4bbcea2e", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 0.1.1 or ~> 0.2.0 or ~> 1.0", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "5ee441dffc1892f1ae59127f74afe8fd82fda6587794278d924e4d90ea3d63f9"}, + "mogrify": {:hex, :mogrify, "0.9.3", "238c782f00271dace01369ad35ae2e9dd020feee3443b9299ea5ea6bed559841", [:mix], [], "hexpm", "0189b1e1de27455f2b9ae8cf88239cefd23d38de9276eb5add7159aea51731e6"}, "nanoid": {:hex, :nanoid, "2.1.0", "d192a5bf1d774258bc49762b480fca0e3128178fa6d35a464af2a738526607fd", [:mix], [], "hexpm", "ebc7a342d02d213534a7f93a091d569b9fea7f26fcd3a638dc655060fc1f76ac"}, "nimble_options": {:hex, :nimble_options, "1.1.1", "e3a492d54d85fc3fd7c5baf411d9d2852922f66e69476317787a7b2bb000a61b", [:mix], [], "hexpm", "821b2470ca9442c4b6984882fe9bb0389371b8ddec4d45a9504f00a66f650b44"}, "nimble_pool": {:hex, :nimble_pool, "1.1.0", "bf9c29fbdcba3564a8b800d1eeb5a3c58f36e1e11d7b7fb2e084a643f645f06b", [:mix], [], "hexpm", "af2e4e6b34197db81f7aad230c1118eac993acc0dae6bc83bac0126d4ae0813a"},