♻️ Refactored opener
✨ Support boost in opener
This commit is contained in:
41
pkg/internal/services/destinations.go
Normal file
41
pkg/internal/services/destinations.go
Normal file
@ -0,0 +1,41 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"git.solsynth.dev/hypernet/paperclip/pkg/internal/models"
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
type destinationMapping struct {
|
||||
Index int
|
||||
Raw []byte
|
||||
}
|
||||
|
||||
var (
|
||||
destinationsByIndex = make(map[int]destinationMapping)
|
||||
destinationsByRegion = make(map[string]destinationMapping)
|
||||
)
|
||||
|
||||
func BuildDestinationMapping() {
|
||||
count := 0
|
||||
for idx, value := range viper.GetStringSlice("destinations") {
|
||||
var parsed models.BaseDestination
|
||||
raw, _ := jsoniter.Marshal(value)
|
||||
_ = jsoniter.Unmarshal(raw, &parsed)
|
||||
|
||||
mapping := destinationMapping{
|
||||
Index: idx,
|
||||
Raw: raw,
|
||||
}
|
||||
|
||||
if len(parsed.Region) > 0 {
|
||||
destinationsByIndex[idx] = mapping
|
||||
destinationsByRegion[parsed.Region] = mapping
|
||||
}
|
||||
|
||||
count++
|
||||
}
|
||||
|
||||
log.Info().Int("count", count).Msg("Destinations mapping built")
|
||||
}
|
155
pkg/internal/services/opener.go
Normal file
155
pkg/internal/services/opener.go
Normal file
@ -0,0 +1,155 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math/rand/v2"
|
||||
nurl "net/url"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
localCache "git.solsynth.dev/hypernet/paperclip/pkg/internal/cache"
|
||||
"git.solsynth.dev/hypernet/paperclip/pkg/internal/database"
|
||||
"git.solsynth.dev/hypernet/paperclip/pkg/internal/models"
|
||||
"github.com/eko/gocache/lib/v4/cache"
|
||||
"github.com/eko/gocache/lib/v4/marshaler"
|
||||
"github.com/eko/gocache/lib/v4/store"
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
type openAttachmentResult struct {
|
||||
Attachment models.Attachment `json:"attachment"`
|
||||
Boosts []models.AttachmentBoost `json:"boost"`
|
||||
}
|
||||
|
||||
func GetAttachmentOpenCacheKey(rid string) any {
|
||||
return fmt.Sprintf("attachment-open#%s", rid)
|
||||
}
|
||||
|
||||
func OpenAttachmentByRID(rid string, region ...string) (url string, mimetype string, err error) {
|
||||
cacheManager := cache.New[any](localCache.S)
|
||||
marshal := marshaler.New(cacheManager)
|
||||
contx := context.Background()
|
||||
|
||||
var result *openAttachmentResult
|
||||
if val, err := marshal.Get(
|
||||
contx,
|
||||
GetAttachmentOpenCacheKey(rid),
|
||||
new(openAttachmentResult),
|
||||
); err == nil {
|
||||
result = val.(*openAttachmentResult)
|
||||
}
|
||||
|
||||
if result == nil {
|
||||
var attachment models.Attachment
|
||||
if err = database.C.Where(models.Attachment{
|
||||
Rid: rid,
|
||||
}).
|
||||
Preload("Pool").
|
||||
Preload("Thumbnail").
|
||||
Preload("Compressed").
|
||||
Preload("Boosts").
|
||||
First(&attachment).Error; err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var boosts []models.AttachmentBoost
|
||||
boosts, err = ListBoostByAttachment(attachment.ID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
result = &openAttachmentResult{
|
||||
Attachment: attachment,
|
||||
Boosts: boosts,
|
||||
}
|
||||
}
|
||||
|
||||
if len(result.Attachment.MimeType) > 0 {
|
||||
mimetype = result.Attachment.MimeType
|
||||
}
|
||||
|
||||
var dest models.BaseDestination
|
||||
var rawDest []byte
|
||||
|
||||
if len(region) > 0 {
|
||||
if des, ok := destinationsByRegion[region[0]]; ok {
|
||||
for _, boost := range result.Boosts {
|
||||
if boost.Destination == des.Index {
|
||||
rawDest = des.Raw
|
||||
json.Unmarshal(rawDest, &dest)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if rawDest == nil {
|
||||
if len(result.Boosts) > 0 {
|
||||
randomIdx := rand.IntN(len(result.Boosts))
|
||||
if des, ok := destinationsByIndex[randomIdx]; ok {
|
||||
rawDest = des.Raw
|
||||
json.Unmarshal(rawDest, &dest)
|
||||
}
|
||||
} else {
|
||||
if des, ok := destinationsByIndex[result.Attachment.Destination]; ok {
|
||||
rawDest = des.Raw
|
||||
json.Unmarshal(rawDest, &dest)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if rawDest == nil {
|
||||
err = fmt.Errorf("no destination found")
|
||||
return
|
||||
}
|
||||
|
||||
switch dest.Type {
|
||||
case models.DestinationTypeLocal:
|
||||
var destConfigured models.LocalDestination
|
||||
_ = jsoniter.Unmarshal(rawDest, &destConfigured)
|
||||
url = "file://" + filepath.Join(destConfigured.Path, result.Attachment.Uuid)
|
||||
return
|
||||
case models.DestinationTypeS3:
|
||||
var destConfigured models.S3Destination
|
||||
_ = jsoniter.Unmarshal(rawDest, &destConfigured)
|
||||
if len(destConfigured.AccessBaseURL) > 0 {
|
||||
url = fmt.Sprintf(
|
||||
"%s/%s",
|
||||
destConfigured.AccessBaseURL,
|
||||
nurl.QueryEscape(filepath.Join(destConfigured.Path, result.Attachment.Uuid)),
|
||||
)
|
||||
} else {
|
||||
protocol := lo.Ternary(destConfigured.EnableSSL, "https", "http")
|
||||
url = fmt.Sprintf(
|
||||
"%s://%s.%s/%s",
|
||||
protocol,
|
||||
destConfigured.Bucket,
|
||||
destConfigured.Endpoint,
|
||||
nurl.QueryEscape(filepath.Join(destConfigured.Path, result.Attachment.Uuid)),
|
||||
)
|
||||
}
|
||||
return
|
||||
default:
|
||||
err = fmt.Errorf("invalid destination: unsupported protocol %s", dest.Type)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func CacheOpenAttachment(item *openAttachmentResult) {
|
||||
if item == nil {
|
||||
return
|
||||
}
|
||||
|
||||
cacheManager := cache.New[any](localCache.S)
|
||||
marshal := marshaler.New(cacheManager)
|
||||
contx := context.Background()
|
||||
|
||||
_ = marshal.Set(
|
||||
contx,
|
||||
GetAttachmentCacheKey(item.Attachment.Rid),
|
||||
*item,
|
||||
store.WithExpiration(60*time.Minute),
|
||||
store.WithTags([]string{"attachment-open", fmt.Sprintf("user#%s", item.Attachment.Rid)}),
|
||||
)
|
||||
}
|
Reference in New Issue
Block a user