feat(storage): serve media through backend and add local FS backend

- introduce storage.Backend with S3 and local file-system implementations
- deliver all blobs via signed GET/HEAD /api/objects/<key> (Range, Content-Type)
- stream S3 objects through the backend; remove presigned URL path
- share shared HMAC Signer between backends
- remove S3_PUBLIC_URL; rename S3_PRESIGN_TTL to OBJECT_URL_TTL
- make storage label in health dynamic; nginx proxy_buffering off for objects
This commit is contained in:
iu
2026-10-11 09:15:03 +03:00
parent 7ae9bcf305
commit ec8d907565
28 changed files with 1714 additions and 65 deletions
+8 -8
View File
@@ -24,11 +24,11 @@ type AlbumAccess interface {
type Service struct {
store *Store
albums AlbumAccess
storage *storage.Client
storage storage.Backend
hub *sse.Hub
}
func NewService(store *Store, albums AlbumAccess, storage *storage.Client, hub *sse.Hub) *Service {
func NewService(store *Store, albums AlbumAccess, storage storage.Backend, hub *sse.Hub) *Service {
return &Service{store: store, albums: albums, storage: storage, hub: hub}
}
@@ -118,7 +118,7 @@ func (s *Service) Upload(ctx context.Context, in UploadInput) (*models.Photo, er
func (s *Service) attachURLs(ctx context.Context, p *models.Photo) error {
if p.StorageKey != "" {
url, err := s.storage.PresignGet(ctx, p.StorageKey)
url, err := s.storage.URL(ctx, p.StorageKey)
if err != nil {
return err
}
@@ -126,12 +126,12 @@ func (s *Service) attachURLs(ctx context.Context, p *models.Photo) error {
p.DownloadURL = url
}
if p.ThumbKey != "" {
if url, err := s.storage.PresignGet(ctx, p.ThumbKey); err == nil {
if url, err := s.storage.URL(ctx, p.ThumbKey); err == nil {
p.ThumbURL = url
}
}
if p.PreviewKey != "" {
if url, err := s.storage.PresignGet(ctx, p.PreviewKey); err == nil {
if url, err := s.storage.URL(ctx, p.PreviewKey); err == nil {
p.PreviewURL = url
}
}
@@ -202,13 +202,13 @@ func (s *Service) GetPublic(ctx context.Context, photoID string) (*models.Photo,
return p, nil
}
// DownloadPublic returns a presigned original URL for a share link.
// DownloadPublic returns a signed original URL for a share link.
func (s *Service) DownloadPublic(ctx context.Context, photoID string) (string, error) {
p, err := s.GetPublic(ctx, photoID)
if err != nil {
return "", err
}
return s.storage.PresignGet(ctx, p.StorageKey)
return s.storage.URL(ctx, p.StorageKey)
}
func (s *Service) Get(ctx context.Context, photoID, userID string) (*models.Photo, error) {
@@ -236,7 +236,7 @@ func (s *Service) Download(ctx context.Context, photoID, userID string) (string,
if err != nil {
return "", err
}
url, err := s.storage.PresignGet(ctx, p.StorageKey)
url, err := s.storage.URL(ctx, p.StorageKey)
if err != nil {
return "", err
}