Compare commits
No commits in common. "ec0444b35cef452614f3f44015af9ba5eda4a9b1" and "8888f7661a5b00e1fe2a8740a1723cbd65755d94" have entirely different histories.
ec0444b35c
...
8888f7661a
@ -6,10 +6,9 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type BaseDestination struct {
|
type BaseDestination struct {
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
Label string `json:"label"`
|
Label string `json:"label"`
|
||||||
Region string `json:"region"`
|
LabelRegion string `json:"label_region"`
|
||||||
IsBoost bool `json:"is_boost"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type LocalDestination struct {
|
type LocalDestination struct {
|
||||||
|
@ -2,7 +2,8 @@ package api
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"net/url"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
"git.solsynth.dev/hypernet/nexus/pkg/nex/sec"
|
"git.solsynth.dev/hypernet/nexus/pkg/nex/sec"
|
||||||
"git.solsynth.dev/hypernet/paperclip/pkg/internal/database"
|
"git.solsynth.dev/hypernet/paperclip/pkg/internal/database"
|
||||||
@ -11,32 +12,64 @@ import (
|
|||||||
"git.solsynth.dev/hypernet/paperclip/pkg/internal/models"
|
"git.solsynth.dev/hypernet/paperclip/pkg/internal/models"
|
||||||
"git.solsynth.dev/hypernet/paperclip/pkg/internal/services"
|
"git.solsynth.dev/hypernet/paperclip/pkg/internal/services"
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
|
jsoniter "github.com/json-iterator/go"
|
||||||
|
"github.com/samber/lo"
|
||||||
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
|
|
||||||
func openAttachment(c *fiber.Ctx) error {
|
func openAttachment(c *fiber.Ctx) error {
|
||||||
id := c.Params("id")
|
id := c.Params("id")
|
||||||
region := c.Query("region")
|
|
||||||
|
|
||||||
var err error
|
|
||||||
var url, mimetype string
|
|
||||||
if len(region) > 0 {
|
|
||||||
url, mimetype, err = services.OpenAttachmentByRID(id, region)
|
|
||||||
} else {
|
|
||||||
url, mimetype, err = services.OpenAttachmentByRID(id)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
metadata, err := services.GetAttachmentByRID(id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
return fiber.NewError(fiber.StatusNotFound)
|
||||||
}
|
}
|
||||||
|
|
||||||
c.Set(fiber.HeaderContentType, mimetype)
|
destMap := viper.GetStringMap(fmt.Sprintf("destinations.%d", metadata.Destination))
|
||||||
|
|
||||||
if strings.HasPrefix(url, "file://") {
|
var dest models.BaseDestination
|
||||||
fp := strings.Replace(url, "file://", "", 1)
|
rawDest, _ := jsoniter.Marshal(destMap)
|
||||||
return c.SendFile(fp)
|
_ = jsoniter.Unmarshal(rawDest, &dest)
|
||||||
|
|
||||||
|
switch dest.Type {
|
||||||
|
case models.DestinationTypeLocal:
|
||||||
|
var destConfigured models.LocalDestination
|
||||||
|
_ = jsoniter.Unmarshal(rawDest, &destConfigured)
|
||||||
|
if len(destConfigured.AccessBaseURL) > 0 && !c.QueryBool("direct", false) {
|
||||||
|
// This will drop all query parameters,
|
||||||
|
// for not it's okay because the openAttachment api won't take any query parameters
|
||||||
|
return c.Redirect(fmt.Sprintf(
|
||||||
|
"%s%s?direct=true",
|
||||||
|
destConfigured.AccessBaseURL,
|
||||||
|
c.Path(),
|
||||||
|
), fiber.StatusMovedPermanently)
|
||||||
|
}
|
||||||
|
if len(metadata.MimeType) > 0 {
|
||||||
|
c.Set(fiber.HeaderContentType, metadata.MimeType)
|
||||||
|
}
|
||||||
|
return c.SendFile(filepath.Join(destConfigured.Path, metadata.Uuid))
|
||||||
|
case models.DestinationTypeS3:
|
||||||
|
var destConfigured models.S3Destination
|
||||||
|
_ = jsoniter.Unmarshal(rawDest, &destConfigured)
|
||||||
|
if len(destConfigured.AccessBaseURL) > 0 {
|
||||||
|
return c.Redirect(fmt.Sprintf(
|
||||||
|
"%s/%s",
|
||||||
|
destConfigured.AccessBaseURL,
|
||||||
|
url.QueryEscape(filepath.Join(destConfigured.Path, metadata.Uuid)),
|
||||||
|
), fiber.StatusMovedPermanently)
|
||||||
|
} else {
|
||||||
|
protocol := lo.Ternary(destConfigured.EnableSSL, "https", "http")
|
||||||
|
return c.Redirect(fmt.Sprintf(
|
||||||
|
"%s://%s.%s/%s",
|
||||||
|
protocol,
|
||||||
|
destConfigured.Bucket,
|
||||||
|
destConfigured.Endpoint,
|
||||||
|
url.QueryEscape(filepath.Join(destConfigured.Path, metadata.Uuid)),
|
||||||
|
), fiber.StatusMovedPermanently)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("invalid destination: unsupported protocol %s", dest.Type)
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.Redirect(url, fiber.StatusFound)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func getAttachmentMeta(c *fiber.Ctx) error {
|
func getAttachmentMeta(c *fiber.Ctx) error {
|
||||||
|
@ -8,9 +8,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func listDestination(c *fiber.Ctx) error {
|
func listDestination(c *fiber.Ctx) error {
|
||||||
var destinations []models.BaseDestination
|
var destinations []models.LocalDestination
|
||||||
for _, value := range viper.GetStringSlice("destinations") {
|
for _, value := range viper.GetStringSlice("destinations") {
|
||||||
var parsed models.BaseDestination
|
var parsed models.LocalDestination
|
||||||
raw, _ := jsoniter.Marshal(value)
|
raw, _ := jsoniter.Marshal(value)
|
||||||
_ = jsoniter.Unmarshal(raw, &parsed)
|
_ = jsoniter.Unmarshal(raw, &parsed)
|
||||||
destinations = append(destinations, parsed)
|
destinations = append(destinations, parsed)
|
@ -1,7 +1,6 @@
|
|||||||
package services
|
package services
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"git.solsynth.dev/hypernet/nexus/pkg/nex/sec"
|
"git.solsynth.dev/hypernet/nexus/pkg/nex/sec"
|
||||||
@ -64,14 +63,9 @@ func CreateBoost(user *sec.UserInfo, source models.Attachment, destination int)
|
|||||||
AccountID: user.ID,
|
AccountID: user.ID,
|
||||||
}
|
}
|
||||||
|
|
||||||
if des, ok := destinationsByIndex[destination]; !ok {
|
dests := cast.ToSlice(viper.Get("destinations"))
|
||||||
|
if destination >= len(dests) {
|
||||||
return boost, fmt.Errorf("invalid destination: %d", destination)
|
return boost, fmt.Errorf("invalid destination: %d", destination)
|
||||||
} else {
|
|
||||||
var destBase models.BaseDestination
|
|
||||||
json.Unmarshal(des.Raw, &destBase)
|
|
||||||
if !destBase.IsBoost {
|
|
||||||
return boost, fmt.Errorf("invalid destination: %d; wasn't available for boost", destination)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := database.C.Create(&boost).Error; err != nil {
|
if err := database.C.Create(&boost).Error; err != nil {
|
||||||
|
@ -1,41 +0,0 @@
|
|||||||
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")
|
|
||||||
}
|
|
@ -1,155 +0,0 @@
|
|||||||
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)}),
|
|
||||||
)
|
|
||||||
}
|
|
@ -93,7 +93,6 @@ func main() {
|
|||||||
go grpc.NewGrpc().Listen()
|
go grpc.NewGrpc().Listen()
|
||||||
|
|
||||||
// Post-boot actions
|
// Post-boot actions
|
||||||
services.BuildDestinationMapping()
|
|
||||||
services.ScanUnanalyzedFileFromDatabase()
|
services.ScanUnanalyzedFileFromDatabase()
|
||||||
fs.RunMarkLifecycleDeletionTask()
|
fs.RunMarkLifecycleDeletionTask()
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user