Configure Plug.Static at runtime

Hey,

is there a way to configure Plug.Static at runtime (right after boot)?

I have a usecase where the location of the files and the desired request path (:from and :at options) is only known after startup.

This is going to be complicated because Plug.Static uses pattern matching to only match against particular from / at values. I guess you could write a custom plug that makes its decisions dynamically and eat the performance loss.

1 Like

Performance is not a big deal here. So I might look into building my own version of Plug.Static.

EDIT: Seems like it would be enough to create a wrapper plug that calls Plug.Static.call/2 with the conn and the dynamically created options.

2 Likes

Since this post’s title is very descriptive and the first one that comes up when you search about the topic on the forum, I want to document an alternative in case someone ends up here trying to find a solution. The documentation currently states that you can also pass a MFA tuple to Plug.Static:

:from - the file system path to read static assets from. It can be either: a string containing a file system path, an atom representing the application name (where assets will be served from priv/static), a tuple containing the application name and the directory to serve assets from (besides priv/static), or an MFA tuple.

This is also, exactly how Livebook does it (see):

plug Plug.Static,
    at: "/",
    from: {__MODULE__, :static_from, []},
    gzip: true,
    only: LivebookWeb.static_paths()

  @doc false
  def static_from(), do: Path.join(Livebook.Config.priv_path(), "static")

This means you can change static_from to something like the following and resolve the path properly at runtime as well:

# endpoint.exs
def static_from(), do: Application.get_env(:app, :path)

# runtime.exs
config :app, path: System.get_env("RUNTIME_PATH")

Even where mfas are not explicitly allowed one can always wrap a plug, which does so:

That was my first instinct but interestingly enough, wrapping Plug.Static and calling it with the exact same options wasn’t serving the files for me (an absolute path outside of the project). I stopped investigating once I read that you can pass an MFA to it.

I’m wondering if you called Plug.Static.init/1 still. The opts for call/2 are not always the same data passed on the plug call.