package sharelinks import ( "context" "errors" "time" "shared-album/internal/auth" "shared-album/internal/httpx" "shared-album/internal/models" "shared-album/internal/roles" ) type AlbumAccess interface { Access(ctx context.Context, albumID, userID string) (*models.Album, string, error) } type Service struct { store *Store albums AlbumAccess } func NewService(store *Store, albums AlbumAccess) *Service { return &Service{store: store, albums: albums} } func (s *Service) Create(ctx context.Context, albumID, actorID string, ttl time.Duration) (*models.ShareLink, error) { _, role, err := s.albums.Access(ctx, albumID, actorID) if err != nil { return nil, err } if !roles.CanManageShares(role) { return nil, httpx.Forbidden("forbidden", "only owner and admin can manage share links") } if ttl <= 0 { ttl = 7 * 24 * time.Hour } token, _, err := auth.NewRefreshToken() if err != nil { return nil, err } l := &models.ShareLink{ ID: auth.NewID(), AlbumID: albumID, Token: token, CreatedBy: actorID, ExpiresAt: time.Now().Add(ttl), } if err := s.store.Create(ctx, l); err != nil { return nil, err } return l, nil } func (s *Service) List(ctx context.Context, albumID, actorID string) ([]models.ShareLink, error) { _, role, err := s.albums.Access(ctx, albumID, actorID) if err != nil { return nil, err } if !roles.CanManageShares(role) { return nil, httpx.Forbidden("forbidden", "only owner and admin can manage share links") } return s.store.ListByAlbum(ctx, albumID) } func (s *Service) Revoke(ctx context.Context, albumID, linkID, actorID string) error { _, role, err := s.albums.Access(ctx, albumID, actorID) if err != nil { return err } if !roles.CanManageShares(role) { return httpx.Forbidden("forbidden", "only owner and admin can manage share links") } if err := s.store.Revoke(ctx, linkID, albumID); err != nil { if errors.Is(err, ErrNotFound) { return httpx.NotFound("share_link_not_found", "share link not found") } return err } return nil } func (s *Service) Resolve(ctx context.Context, token string) (*models.ShareLink, error) { l, err := s.store.GetByToken(ctx, token) if err != nil { if errors.Is(err, ErrNotFound) { return nil, httpx.NotFound("share_link_not_found", "share link not found") } return nil, err } if !l.Active(time.Now()) { return nil, httpx.Forbidden("share_link_inactive", "share link is expired or revoked") } return l, nil }