defmodule ShootingEventPhxWeb.FrontendController do use ShootingEventPhxWeb, :controller def serve(conn, %{"path" => segments}) do request_path = segments |> List.wrap() |> Enum.join("/") |> String.trim_leading("/") if String.starts_with?(request_path, "api") do send_resp(conn, 404, "Not Found") else web_dir = web_dir() requested = if request_path == "", do: "index.html", else: request_path requested_file = safe_file_path(web_dir, requested) cond do requested_file != nil and File.regular?(requested_file) -> send_static_file(conn, requested_file) File.regular?(Path.join(web_dir, "index.html")) -> send_static_file(conn, Path.join(web_dir, "index.html")) true -> send_resp(conn, 404, "frontend build not found. run make build") end end end defp web_dir do System.get_env("WEB_DIR") || Path.expand("../frontend/dist", File.cwd!()) end defp safe_file_path(web_dir, request_path) do cleaned = request_path |> Path.expand(web_dir) root = Path.expand(web_dir) if String.starts_with?(cleaned, root) do cleaned else nil end end defp send_static_file(conn, file_path) do content_type = case MIME.from_path(file_path) do "" -> "application/octet-stream" type -> type end conn |> put_resp_content_type(content_type) |> send_file(200, file_path) end end