howdy/storage/local

Storage in a local folder, for development and for apps on one server. Each object is a file at its key under the folder, so the folder can be browsed, backed up or synced like any other.

let config =
  local.new("storage")
  |> local.serve(at: "/files", secret: env.get_or("STORAGE_SECRET", ""))
let files = local.adapter(config)

howdy.new()
|> howdy.controller(local.controller(config))

A folder cannot hold both an object a and an object a/b, as a bucket can, so the second is refused. The content type and other details of each object are kept beside it under .howdy-storage, which keys may not start with.

Serving files

storage.url needs serve: it gives a URL under that path, signed with the secret, which controller answers until it expires. Use a secret of at least 32 random bytes, and the same on every node. Files are served from the app’s own origin, so the controller sends them with X-Content-Type-Options: nosniff and a sandboxing Content-Security-Policy, and makes the browser download anything but images, audio, video, PDFs and plain text rather than show it. An HTML or SVG file someone uploaded cannot run script on your site.

Types

pub opaque type Config

Values

pub fn adapter(config: Config) -> storage.Storage
pub fn controller(config: Config) -> controller.Builder(Nil)

A controller answering the URLs storage.url makes, mounted at the prefix given to serve. An expired or altered URL gets a 403.

pub fn new(directory: String) -> Config

Storage in directory, relative to the working directory unless absolute. It is created when the first object is stored.

pub fn public(config: Config) -> Config

Serve every object to anyone, without a signature, and give storage.public_urls. Needs serve too.

pub fn serve(
  config: Config,
  at prefix: String,
  secret secret: String,
) -> Config

Make URLs under prefix, where controller is mounted, signed with secret.

✨ Search Document