66 lines
1.8 KiB
Go
66 lines
1.8 KiB
Go
package outboundimage
|
|
|
|
import (
|
|
"context"
|
|
"crypto/sha256"
|
|
"encoding/hex"
|
|
"errors"
|
|
"path"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
type ObjectStore interface {
|
|
Put(ctx context.Context, key string, data []byte, contentType string) error
|
|
PresignGet(ctx context.Context, key string, ttl time.Duration) (string, error)
|
|
}
|
|
|
|
type HashPublisher struct {
|
|
store ObjectStore
|
|
prefix string
|
|
ttl time.Duration
|
|
}
|
|
|
|
func NewHashPublisher(store ObjectStore, prefix string, ttl time.Duration) *HashPublisher {
|
|
return &HashPublisher{store: store, prefix: strings.Trim(strings.TrimSpace(prefix), "/"), ttl: ttl}
|
|
}
|
|
|
|
func (p *HashPublisher) Publish(ctx context.Context, data []byte, contentType string) (string, error) {
|
|
if p == nil || p.store == nil || len(data) == 0 || p.prefix == "" || p.ttl <= 0 {
|
|
return "", errors.New("outbound image: invalid publisher configuration")
|
|
}
|
|
extension := imageExtension(contentType)
|
|
if extension == "" {
|
|
return "", errors.New("outbound image: unsupported image content type")
|
|
}
|
|
sum := sha256.Sum256(data)
|
|
digest := hex.EncodeToString(sum[:])
|
|
key := path.Join(p.prefix, digest[:2], digest+extension)
|
|
if err := p.store.Put(ctx, key, data, contentType); err != nil {
|
|
return "", errors.New("outbound image: publish object failed")
|
|
}
|
|
signedURL, err := p.store.PresignGet(ctx, key, p.ttl)
|
|
if err != nil {
|
|
return "", errors.New("outbound image: sign object URL failed")
|
|
}
|
|
if !strings.HasPrefix(strings.TrimSpace(signedURL), "https://") {
|
|
return "", errors.New("outbound image: signed object URL must use HTTPS")
|
|
}
|
|
return signedURL, nil
|
|
}
|
|
|
|
func imageExtension(contentType string) string {
|
|
switch strings.ToLower(strings.TrimSpace(strings.Split(contentType, ";")[0])) {
|
|
case "image/jpeg", "image/jpg":
|
|
return ".jpg"
|
|
case "image/png":
|
|
return ".png"
|
|
case "image/webp":
|
|
return ".webp"
|
|
case "image/gif":
|
|
return ".gif"
|
|
default:
|
|
return ""
|
|
}
|
|
}
|