DocShell.Web.Plug (DocShell v0.1.0)

Copy Markdown View Source

Serves cached artifacts over HTTP, behind an authorization gate you supply.

Mount it wherever the documentation JSON should live:

forward "/docs/api",
  to: DocShell.Web.Plug,
  init_opts: [gate: &MyApp.Auth.allow_docs?/1]

The last path segment names the artifact, with .json optional, so GET /docs/api/navigation and GET /docs/api/navigation.json both return navigation.json from DocShell.Web.Cache. The stored envelope is served as-is, so generated_at is the time of the build that produced the artifact — not the time of the request, which would be useless to a client and would defeat any caching built on top of it.

The conn is halted once a response is sent, so this is safe to place in a pipeline as well as behind forward.

The gate

Documentation is rarely uniformly public. Internal runbooks, unreleased features, and per-tenant guides all need someone to decide who may read them — and that someone is the host, which knows about sessions, roles, and tenancy. DocShell knows about none of it and should not pretend otherwise.

So authorization is a single :gate option, either a unary function or an {module, function, extra_args} tuple receiving the conn first:

init_opts: [gate: {MyApp.Auth, :allow_docs?, [:internal]}]

Return :ok or true to allow the request; anything else yields 403. Omitting :gate serves every artifact to every caller, which is the right default for genuinely public documentation and the wrong one everywhere else — decide deliberately.

Responses

  • 200 with application/json — the enveloped artifact
  • 403 — the gate refused
  • 404 — no such artifact, or a path that is not a single segment
  • 500 — the artifact is cached but could not be encoded

Multi-segment paths are rejected rather than joined, so no request can walk out of the cache and into the filesystem.

Multiple caches

The :cache option selects which named DocShell.Web.Cache to read, defaulting to DocShell.Web.Cache itself. A host serving public and internal artifact sets runs two caches and mounts this plug twice, with a different gate on each.

Hosts that would rather route through their own controller can call DocShell.Web.Controller.show/2 instead. This module is only compiled when Plug is installed.