spark-server/internal/module/upload/logic/service.go
2026-09-29 10:22:28 +08:00

81 lines
1.7 KiB
Go

package logic
import (
"context"
"strconv"
"time"
"spark-server/internal/pkg/media"
"spark-server/internal/pkg/ratelimit"
"go.uber.org/zap"
)
// Presigner issues short-lived object-storage URLs. Infrastructure implements it.
type Presigner interface {
PresignPut(ctx context.Context, key, contentType string, size int64, ttl time.Duration) (string, error)
PresignGet(ctx context.Context, key string, ttl time.Duration) (string, error)
}
type AvatarACL interface {
CanSetAvatar(ctx context.Context, userID, groupID int64) error
}
type Options struct {
TTL time.Duration
Limit *ratelimit.Limiter
Now func() time.Time
Groups AvatarACL
Media media.Policy
Log *zap.Logger
}
// Service issues upload/download credentials. HTTP and TCP share it.
type Service struct {
store Presigner
ttl time.Duration
limit *ratelimit.Limiter
now func() time.Time
groups AvatarACL
media media.Policy
log *zap.Logger
}
func NewService(store Presigner, opt *Options) *Service {
if opt == nil {
opt = &Options{}
}
ttl := opt.TTL
if ttl <= 0 {
ttl = 15 * time.Minute
}
clock := opt.Now
if clock == nil {
clock = func() time.Time { return time.Now().UTC() }
}
log := opt.Log
if log == nil {
log = zap.NewNop()
}
return &Service{store: store, ttl: ttl, limit: opt.Limit, now: clock, groups: opt.Groups, media: opt.Media, log: log}
}
func (s *Service) ready() bool {
return s != nil && s.store != nil
}
func (s *Service) expiresIn() int64 {
return int64(s.ttl / time.Second)
}
func (s *Service) allow(userID int64) bool {
if s.limit == nil {
return true
}
return s.limit.Allow("upload:" + strconv.FormatInt(userID, 10))
}
func dayOf(t time.Time) string {
return t.UTC().Format("20060102")
}