Current section
Files
Jump to
Current section
Files
lib/plugs/serve_index/default_serve_index.ex
defmodule Outerfaces.Plugs.ServeIndex.DefaultServeIndex do
@moduledoc """
A default example plug to handle both static asset requests and index.html fallback.
"""
import Plug.Conn
@behaviour Plug
@impl true
def init(opts) do
static_root = Keyword.get(opts, :static_root, "priv/static")
root = Path.expand(static_root)
index_path = Keyword.get(opts, :index_path, Path.join(root, "index.html")) |> Path.expand()
%{
index_path: index_path,
static_root: root,
static_patterns: Keyword.get(opts, :static_patterns, default_static_patterns())
}
end
@impl true
def call(conn, %{
index_path: index_path,
static_root: static_root,
static_patterns: static_patterns
})
when is_binary(index_path) and is_list(static_patterns) and is_binary(static_root) do
request_path = conn.request_path
cond do
static_asset_request?(request_path, static_patterns) ->
serve_static_asset(conn, static_root, request_path)
true ->
serve_index_html(conn, index_path)
end
end
defp serve_static_asset(conn, root, request_path) do
with false <- String.contains?(request_path, <<0>>),
rel <- String.trim_leading(request_path, "/"),
false <- String.contains?(rel, ["\\", ":"]),
candidate <- Path.expand(rel, root),
true <- candidate == root or String.starts_with?(candidate, root <> "/"),
true <- File.regular?(candidate) do
mime_type = MIME.from_path(candidate) || "application/octet-stream"
conn
|> put_resp_content_type(mime_type)
|> send_file(200, candidate)
|> halt()
else
_ ->
conn |> send_resp(404, "File not found") |> halt()
end
end
defp serve_index_html(conn, index_path) do
if File.exists?(index_path) do
conn
|> put_resp_content_type("text/html")
|> send_file(200, index_path)
|> halt()
else
send_resp(conn, 404, "index.html not found")
|> halt()
end
end
defp static_asset_request?(request_path, patterns) do
Enum.any?(patterns, &Regex.match?(&1, request_path))
end
defp default_static_patterns do
[
~r{\.js$},
~r{\.css$},
~r{\.png$},
~r{\.jpg$},
~r{\.svg$},
~r{\.json$},
~r{\.ico$},
~r{\.txt$}
]
end
end