81 lines
1.7 KiB
Go
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")
|
|
}
|