Uploads skip per-gallery duplicates, checksums can be backfilled, and STORAGE_BACKEND plus sync tooling make switching storage backends safe.
48 lines
1.6 KiB
Go
48 lines
1.6 KiB
Go
// Package storage abstracts photo object storage. Selection follows the
|
|
// backend's convention (backend/src/lib/storage.ts): S3 when S3_ENDPOINT
|
|
// and S3_BUCKET are both set, local disk otherwise. Keys are identical on
|
|
// both backends: galleries/<galleryID>/<variantDir>/<photoID>.<ext>.
|
|
package storage
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"time"
|
|
|
|
"git.azzamo.net/Michilis/Spanglish/photo-api/internal/config"
|
|
)
|
|
|
|
// ErrNoPresign is returned by backends that cannot presign (local disk);
|
|
// callers then stream the object through the API instead.
|
|
var ErrNoPresign = errors.New("presigned URLs not supported")
|
|
|
|
// ErrNotExist is what Stat reports for a missing object on either backend.
|
|
var ErrNotExist = errors.New("object does not exist")
|
|
|
|
type Storage interface {
|
|
Put(ctx context.Context, key string, r io.Reader, size int64, contentType string) error
|
|
Open(ctx context.Context, key string) (io.ReadCloser, int64, error)
|
|
// Stat returns the object's size without reading it, or ErrNotExist.
|
|
Stat(ctx context.Context, key string) (int64, error)
|
|
Delete(ctx context.Context, key string) error
|
|
PresignGet(ctx context.Context, key, downloadFilename, contentType string, expiry time.Duration) (string, error)
|
|
}
|
|
|
|
// New builds the backend that serves requests (see config.S3Enabled).
|
|
func New(cfg config.Config) (Storage, error) {
|
|
if cfg.S3Enabled() {
|
|
return NewS3(cfg)
|
|
}
|
|
return NewLocal(cfg.StoragePath)
|
|
}
|
|
|
|
// Name describes a backend for log lines.
|
|
func Name(cfg config.Config, s Storage) string {
|
|
if _, ok := s.(*s3Store); ok {
|
|
return fmt.Sprintf("S3 bucket %s at %s", cfg.S3Bucket, cfg.S3Endpoint)
|
|
}
|
|
return fmt.Sprintf("local disk at %s", cfg.StoragePath)
|
|
}
|