All notable changes to Storehouse are documented in this file.

The format follows Keep a Changelog, and Storehouse follows Semantic Versioning.

[Unreleased]

[0.1.0]

The first release: the storage of Kotoba and Hakushi, taken out of them.

Added

  • Storehouse.store/2: a file checked by its bytes and stored under a new, safe key, as a Storehouse.File, with :accept, :max_bytes, :max_pixels, :key and :preview.
  • Storehouse.Check: the type of a file from its bytes (PNG, JPEG, GIF and WebP images with their width and height, a JPEG's turned as its EXIF says; PDF; MP4 and WebM video; ZIP and Office documents; plain text), and every other file as application/octet-stream.
  • Storehouse.Adapter, the behaviour of a store (Kotoba's and Hakushi's callbacks), Storehouse.Local and Storehouse.Local.Plug, which serves files with their checked type, safe headers and byte ranges.
  • Storehouse.fetch/2: a file from an address of the hosts an app allows, with redirects checked and the download stopped at :max_bytes.
  • Storehouse.S3: Amazon S3, and the stores that speak its API (Cloudflare R2, MinIO, Tigris, LocalStack), with no other library: it signs its requests itself (Signature Version 4).
  • Direct uploads: Storehouse.presign_upload/4 signs an upload the browser makes straight to the store (its key, type and size), and Storehouse.verify_upload/2 checks the file there by its first bytes, and deletes it when it is not what it said. Storehouse.Live.allow/3 does both for a LiveView upload, with the uploader of storehouse.js. Storehouse.Local.Plug takes direct uploads too, for development.
  • Private files: Storehouse.signed_url/2, from S3 or the local plug (private: true serves only signed URLs), and Storehouse.Redirect, a lasting URL that checks the person and redirects to a signed one.
  • Storehouse.Live.consume/3: the files of a LiveView upload, each stored once, with a result for each.
  • Streamed uploads: Storehouse.Live.allow/3 with mode: :stream sends a file through the LiveView and on to the store as each chunk comes (Storehouse.Live.Writer), with no file on the app's disk: to S3 in the parts of a multipart upload, each with its checksum, two in flight from a process of their own, the upload held back when they are; its first bytes checked on the way. The optional adapter callbacks stream_open/2, stream_write/2, stream_close/1 and stream_abort/1 (Storehouse.S3, Storehouse.Local).
  • Temporary, then permanent: cache: true stores a file under the temporary prefix (Storehouse.cache_prefix/0), and Storehouse.promote/2 makes it permanent with a copy in the store (the optional adapter callback copy/2: S3's CopyObject, the local disk's copy).
  • Storehouse.Ecto: files in a record's columns (Storehouse.Ecto.File, or a list of them), put with put_file/4, checked against the field's limits, made permanent in the save's transaction, and, with update/3 and delete/3, deleted from the store once the record no longer has them.
  • Checksums: each Storehouse.File has the SHA-256 of its bytes (checksum, Base64), sent to S3 with the file (x-amz-checksum-sha256), which checks and keeps it; Storehouse.checksum/1 gives it of a copy, and the optional adapter callback checksum/1 gives a stored file's.
  • Telemetry: `[:storehouse, :store:fetch:presign_upload
    :verify_upload:delete, :start:stop:exception]` spans, with the
    file or the reason a file was refused.
  • Keys (Storehouse.key/2, safe_name/2, valid_key?/1), display names (clean_name/1) and safe_url?/1.