defmodule Bloccs.Web.Assets do @moduledoc """ Serves the dashboard's own precompiled `app.css` / `app.js` from `priv/static/assets`, so the host needs no `Plug.Static` configuration and the bundles can't collide with the host's own assets. `forward`ed by `bloccs_dashboard/2` at `/assets`, so the filename arrives as the remaining `path_info` segment. A long `cache-control` is safe because the filenames are content-stable per release. """ @behaviour Plug @types %{ "app.css" => "text/css", "app.js" => "application/javascript", "mark.svg" => "image/svg+xml" } @impl true def init(opts), do: opts @impl true def call(%Plug.Conn{path_info: path_info} = conn, _opts) do serve(conn, List.last(path_info)) end defp serve(conn, file) when is_binary(file) do case Map.fetch(@types, Path.basename(file)) do {:ok, content_type} -> path = Path.join(:code.priv_dir(:bloccs_web), "static/assets/#{Path.basename(file)}") if File.regular?(path) do conn |> Plug.Conn.put_resp_header("content-type", content_type) |> Plug.Conn.put_resp_header("cache-control", "public, max-age=31536000") |> Plug.Conn.send_file(200, path) |> Plug.Conn.halt() else not_found(conn) end :error -> not_found(conn) end end defp serve(conn, _), do: not_found(conn) defp not_found(conn) do conn |> Plug.Conn.send_resp(404, "not found") |> Plug.Conn.halt() end end