Documentation
¶
Overview ¶
Package files is the service / use-case layer. It owns the write path and the consistency logic between storage and metadata; layers above it (api) talk only to this package and never reach storage or metadata directly.
Index ¶
- Variables
- type File
- type ListFilter
- type Service
- func (s *Service) Delete(ctx context.Context, id string) error
- func (s *Service) List(ctx context.Context, filter ListFilter) ([]*File, error)
- func (s *Service) Metadata(ctx context.Context, id string) (*File, error)
- func (s *Service) Open(ctx context.Context, id string) (*File, io.ReadCloser, error)
- func (s *Service) OpenRange(ctx context.Context, f *File, offset, length int64) (io.ReadCloser, error)
- func (s *Service) Presign(ctx context.Context, id string, ttl time.Duration) (*File, string, error)
- func (s *Service) UpdateMetadata(ctx context.Context, id string, tags map[string]any, merge bool) (*File, error)
- func (s *Service) Upload(ctx context.Context, in UploadInput) (*File, error)
- func (s *Service) UploadIdempotent(ctx context.Context, key string, in UploadInput) (f *File, replayed bool, err error)
- type UploadInput
Constants ¶
This section is empty.
Variables ¶
var ErrChecksumMismatch = errors.New("files: checksum mismatch")
ErrChecksumMismatch is surfaced by a verifying download reader when the stored bytes no longer hash to the recorded checksum (Phase 2 integrity).
var ErrIdempotencyConflict = errors.New("files: idempotency key conflict")
ErrIdempotencyConflict means an upload with the same Idempotency-Key is still in progress. The api layer maps it to 409 — the client should retry shortly.
var ErrIdempotencyResultGone = errors.New("files: idempotent result deleted")
ErrIdempotencyResultGone means the file an Idempotency-Key produced has since been deleted. Retrying under the same key can never succeed until the key expires, so the api layer maps it to 410 — the client must pick a new key.
var ErrInvalidID = errors.New("files: invalid id")
ErrInvalidID is returned when an id is not a well-formed file identifier. The api layer maps it to 400 — a malformed id is a client error, not a 500.
var ErrNotFound = errors.New("files: not found")
ErrNotFound is returned when no live file matches. It is the api layer's only signal for a 404, keeping storage/metadata sentinels below this seam.
Functions ¶
This section is empty.
Types ¶
type ListFilter ¶
type ListFilter = metadata.ListFilter
ListFilter narrows a List query. Aliased so the api layer can build filters without importing the metadata package directly.
type Service ¶
type Service struct {
// contains filtered or unexported fields
}
Service orchestrates the object store and the metadata repository.
func New ¶
func New(backend storage.Backend, repo metadata.Repository) *Service
New wires the service to its collaborators.
func (*Service) Delete ¶
Delete soft-deletes the file: the row is stamped deleted_at and immediately vanishes from reads (every read path filters deleted_at is null), while the object is purged asynchronously by GC (PLAN §7). Returns ErrNotFound if the file does not exist.
func (*Service) List ¶
List returns file records matching the filter, newest first. The metadata repository applies tag containment against the GIN index and keyset pagination by id. A malformed cursor is ErrInvalidID — it must be a file id handed out by a previous page.
func (*Service) Open ¶
Open streams the whole object through this process, verifying the SHA-256 as it reads: the returned reader yields ErrChecksumMismatch at EOF if the stored bytes no longer match the recorded checksum. The caller must Close it.
func (*Service) OpenRange ¶
func (s *Service) OpenRange(ctx context.Context, f *File, offset, length int64) (io.ReadCloser, error)
OpenRange streams a byte range [offset, offset+length) of an already-fetched record through this process — the caller resolves f via Metadata first (it needs the size to validate the range), so no second lookup happens here. Partial reads cannot be checksum-verified against the whole-object hash, so the reader is returned unverified. The caller must Close it.
func (*Service) Presign ¶
Presign returns the record and a time-limited URL that serves the object directly from the store, offloading the transfer from this process.
func (*Service) UpdateMetadata ¶
func (s *Service) UpdateMetadata(ctx context.Context, id string, tags map[string]any, merge bool) (*File, error)
UpdateMetadata writes tags onto a file's JSONB metadata, merging into the existing object or replacing it wholesale. Returns ErrNotFound if absent.
func (*Service) Upload ¶
Upload streams the body to storage, computing its SHA-256 inline, then commits the metadata row. On a metadata failure the freshly-written object is best-effort deleted; a leftover is reclaimed by GC.
func (*Service) UploadIdempotent ¶
func (s *Service) UploadIdempotent(ctx context.Context, key string, in UploadInput) (f *File, replayed bool, err error)
UploadIdempotent performs an upload guarded by a client-supplied idempotency key. The first call for a key uploads and records the result; a retry returns the original result (replayed=true) instead of creating a duplicate. A retry while the first is still in flight — or whose result has since been deleted — returns ErrIdempotencyConflict (the client should retry shortly).
On the replay/conflict paths the request body is intentionally not read; the HTTP server drains it.