# `Storehouse.Batch`
[🔗](https://github.com/hattoriai/storehouse/blob/v0.2.0/lib/storehouse/batch.ex#L1)

Uploads straight to the store, many at a time: what a frontend that is
not a LiveView (React, Vue, a native app) asks of the app around the
browser's `PUT`s. `presign/2` signs a batch, `register/2` checks the
files once they are there. `Storehouse.Plug.Presign` and
`Storehouse.Plug.Register` answer them in JSON.

Each gives one result per item, in order: some files are refused (their
type, their size, the room left) and the others go ahead.

  * `{:ok, upload}` - `presign/2`: signed (`Storehouse.presign_upload/4`),
    with a `:token` that the browser gives back with the key to
    `register/2`.
  * `{:ok, file}` - `register/2`: checked, a `Storehouse.File`.
  * `{:existing, file}` - a file the app has already (`:existing`):
    nothing to upload, nothing kept twice.
  * `{:error, code}` - an atom the frontend translates: `:not_accepted`,
    `:too_large`, `:storage_full`, `:invalid_checksum`, `:not_owner`,
    `:invalid_token`, `:invalid_item`, `:too_many`, or the store's.

## The token

Each upload `presign/2` signs comes with a token: its key and owner,
signed with `:secret` (the endpoint's `secret_key_base`, which
`Storehouse.Plug.Presign` and `Storehouse.Plug.Register` take from the
conn). `register/2` checks only a key whose token is valid, for the same
owner, and less than a day old: any other is `{:error, :invalid_token}`,
and the file at it is left as it is. So a request can never name a
file already stored (to have it deleted when it fails a check), or one
of another person of the same owner.

## The room

`:room` is shared by the batch: each file presigned (or registered)
takes its size from what the next one sees, so ten files that fit one
at a time but not together are not all signed. A file the app has
already takes none.

# `max_items`

```elixir
@spec max_items() :: pos_integer()
```

The most items a batch takes, by default.

# `presign`

```elixir
@spec presign([map()], keyword()) :: {:ok, [tuple()]} | {:error, :too_many}
```

Signs uploads of `items`, each a map with `"name"`, `"type"`, `"bytes"`
and, optionally, `"checksum"` (the SHA-256 the browser read, hex or
Base64). String or atom keys.

Takes the options of `Storehouse.presign_upload/4` (`:accept`,
`:max_bytes`, `:owner`, `:existing`, `:key`, `:cache`, `:expires_in`,
`:adapter`), with `:room` shared by the batch, `:max_items`
(`100`): a larger batch gives `{:error, :too_many}`, and
`:secret`, which signs each upload's token (required).

# `register`

```elixir
@spec register([map()], keyword()) :: {:ok, [tuple()]} | {:error, :too_many}
```

Checks the files a browser uploaded (`Storehouse.verify_upload/2`):
`items` are maps with `"key"` and `"token"` (those `presign/2` gave)
and, optionally, `"name"`. A key without its token (another's, a file
already stored) is refused with `:invalid_token`, and the file at it is
left as it is. Two files of the same
bytes in one batch are one: the second gives `{:existing, first}`, and
its copy is deleted.

Takes the options of `Storehouse.verify_upload/2` (`:accept`,
`:max_bytes`, `:max_pixels`, `:owner`, `:existing`, `:metadata`,
`:adapter`), with `:room` shared by the batch (the sizes the store
gives), `:max_items`, and `:secret`, as for `presign/2`.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
