diff options
| author | Paul Buetow <paul@buetow.org> | 2026-04-30 12:45:18 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-04-30 12:45:18 +0300 |
| commit | 54bc9013aa0463962a1cfd67fa278de3d401d85a (patch) | |
| tree | 526c719285a6153fdc82f8a7feb7b4ef98c1ce78 /internal/service | |
| parent | 38e318e71d50e4340ddbf1ae63b3d85e958b4644 (diff) | |
ja: implement public share landing page and improve error mapping
- Add web/share.html with a minimal HTML5 video player, theme variables,
centered layout, play overlay, and Back to Home link.
- Update handleSharePage to return HTML for browser Accept headers
(text/html or empty) and JSON for application/json. Return 410 Gone
for expired shares.
- Update handleShareStream to map service errors to distinct HTTP codes:
ErrShareExpired -> 410, ErrShareNotFound/ErrMediaNotFound -> 404.
- Improve ValidateShareToken and StreamSharedMedia to return sentinel
errors (ErrShareNotFound, ErrShareExpired, ErrMediaNotFound) instead
of generic string errors.
- Update and add tests for share page HTML/JSON negotiation and share
stream 404/410 responses.
Diffstat (limited to 'internal/service')
| -rw-r--r-- | internal/service/media.go | 18 | ||||
| -rw-r--r-- | internal/service/media_test.go | 9 | ||||
| -rw-r--r-- | internal/service/no_rows_test.go | 8 |
3 files changed, 19 insertions, 16 deletions
diff --git a/internal/service/media.go b/internal/service/media.go index f950db3..6f4b2db 100644 --- a/internal/service/media.go +++ b/internal/service/media.go @@ -35,8 +35,11 @@ func NewMediaService(store repository.MediaServiceStore, clk clock.Clock, mediaR // Sentinel errors returned by the media service layer. var ( - ErrNotFound = errors.New("not found") - ErrForbidden = errors.New("access denied") + ErrNotFound = errors.New("not found") + ErrForbidden = errors.New("access denied") + ErrShareNotFound = errors.New("share not found") + ErrShareExpired = errors.New("share expired") + ErrMediaNotFound = errors.New("media not found") ) func (s *mediaService) ListSets(ctx context.Context, userID int64) ([]model.Set, error) { @@ -510,16 +513,16 @@ func (s *mediaService) ValidateShareToken(ctx context.Context, token string) (*m return nil, fmt.Errorf("get share: %w", err) } if share == nil { - return nil, nil + return nil, ErrShareNotFound } now := s.clock.Now() if now.After(share.ExpiresAt) { - return nil, nil + return nil, ErrShareExpired } if share.MaxUses != nil && share.UsedCount >= *share.MaxUses { - return nil, nil + return nil, ErrShareExpired } return share, nil @@ -530,16 +533,13 @@ func (s *mediaService) StreamSharedMedia(ctx context.Context, token string) (*Fi if err != nil { return nil, err } - if share == nil { - return nil, errors.New("invalid or expired share") - } media, err := s.store.GetMediaByID(ctx, share.MediaID) if err != nil { return nil, fmt.Errorf("get media: %w", err) } if media == nil { - return nil, errors.New("media not found") + return nil, ErrMediaNotFound } _ = s.store.UseShare(ctx, token) diff --git a/internal/service/media_test.go b/internal/service/media_test.go index 1ab7dfb..33912b4 100644 --- a/internal/service/media_test.go +++ b/internal/service/media_test.go @@ -905,14 +905,17 @@ func TestMediaService_ValidateShareToken(t *testing.T) { } svc := NewMediaService(store, newMockClock(), "/tmp/media") res, err := svc.ValidateShareToken(ctx, "abc") - if err != nil { - t.Fatalf("unexpected error: %v", err) - } if tt.wantValid { + if err != nil { + t.Fatalf("unexpected error: %v", err) + } if res == nil { t.Fatal("expected valid share") } } else { + if err == nil { + t.Fatal("expected error for invalid share") + } if res != nil { t.Fatal("expected nil share") } diff --git a/internal/service/no_rows_test.go b/internal/service/no_rows_test.go index 3606b63..9200668 100644 --- a/internal/service/no_rows_test.go +++ b/internal/service/no_rows_test.go @@ -135,8 +135,8 @@ func TestService_NoRows_ReturnsNil(t *testing.T) { } svc := NewMediaService(store, newMockClock(), "/tmp/media") sh, err := svc.ValidateShareToken(ctx, "nope") - if err != nil { - t.Fatalf("expected no error, got %v", err) + if !errors.Is(err, ErrShareNotFound) { + t.Fatalf("expected ErrShareNotFound, got %v", err) } if sh != nil { t.Fatalf("expected nil share, got %+v", sh) @@ -153,8 +153,8 @@ func TestService_NoRows_ReturnsNil(t *testing.T) { } svc := NewMediaService(store, newMockClock(), "/tmp/media") _, err := svc.StreamSharedMedia(ctx, "nope") - if err == nil { - t.Fatal("expected error for missing share") + if !errors.Is(err, ErrShareNotFound) { + t.Fatalf("expected ErrShareNotFound, got %v", err) } }) |
