howdy/static

Serve files from a directory.

import howdy/static

howdy.new()
|> howdy.controller(static.serve("/", from: "priv/public"))

Every file under priv/public is now answered at the site root, with index.html standing in for directories. For more control build the controller step by step:

static.new(from: "priv/public")
|> static.at("/assets")
|> static.max_age(seconds: 86_400)
|> static.build

The result is an ordinary controller, so middleware, guards, logging and CORS apply to it like any other. Only GET and HEAD are answered. Requests that would escape the root directory get a 404, and directories are never listed.

A controller mounted at "/" matches every path, so mount it last. It also stops howdy.versions from ever seeing a request, since the group only answers paths no controller matched. Mount under a prefix such as "/assets" when the app has a version group.

Types

A directory of files under construction. Build with new, adjust, then turn into a controller with build.

pub opaque type Files

Values

pub fn at(files: Files, prefix: String) -> Files

Mount the files under prefix instead of "/".

pub fn build(files: Files) -> controller.Builder(Nil)

Turn the description into a controller answering GET and HEAD.

pub fn content_type(path: String) -> String

The content type for a file, from its extension. Text types carry a UTF-8 charset. Unknown extensions are application/octet-stream.

pub fn fallback(files: Files, name: String) -> Files

Serve this file, relative to the root, with a 200 for any path that does not name a file. Single-page apps use this so a deep link such as /users/42 loads the app and lets its router take over.

pub fn file(
  ctx: context.Context(guarded),
  path: String,
) -> response.Response(content.Content)

A 200 response with the contents of the file at path and a content type from its extension, or a 404 if there is no such file. The path is used as given, so build it from trusted values only; the controller from build is the safe way to serve user-chosen paths.

pub fn index(files: Files, name: String) -> Files

The file served for a request naming a directory. Defaults to "index.html". A directory without one answers 404.

pub fn max_age(files: Files, seconds seconds: Int) -> Files

Send cache-control: public, max-age=N with every file.

pub fn new(from root: String) -> Files

Start describing a directory to serve. root is relative to the working directory unless absolute. The files are mounted at "/" with index.html as the directory index, no caching header and no fallback.

pub fn serve(
  at prefix: String,
  from root: String,
) -> controller.Builder(Nil)

Serve the files in root under prefix with the default options. The same as new(from: root) |> at(prefix) |> build.

✨ Search Document