101 lines
2.1 KiB
Go
101 lines
2.1 KiB
Go
package storage
|
|
|
|
import (
|
|
"crypto/hmac"
|
|
"crypto/sha256"
|
|
"encoding/hex"
|
|
"fmt"
|
|
"net/url"
|
|
"os"
|
|
"path/filepath"
|
|
"strconv"
|
|
"time"
|
|
|
|
"go.ipao.vip/atom/container"
|
|
"go.ipao.vip/atom/opt"
|
|
)
|
|
|
|
const DefaultPrefix = "Storage"
|
|
|
|
func DefaultProvider() container.ProviderContainer {
|
|
return container.ProviderContainer{
|
|
Provider: Provide,
|
|
Options: []opt.Option{
|
|
opt.Prefix(DefaultPrefix),
|
|
},
|
|
}
|
|
}
|
|
|
|
func Provide(opts ...opt.Option) error {
|
|
o := opt.New(opts...)
|
|
var config Config
|
|
if err := o.UnmarshalConfig(&config); err != nil {
|
|
return err
|
|
}
|
|
return container.Container.Provide(func() (*Storage, error) {
|
|
return &Storage{Config: &config}, nil
|
|
}, o.DiOptions()...)
|
|
}
|
|
|
|
type Storage struct {
|
|
Config *Config
|
|
}
|
|
|
|
func (s *Storage) Delete(key string) error {
|
|
if s.Config.Type == "local" {
|
|
localPath := s.Config.LocalPath
|
|
if localPath == "" {
|
|
localPath = "./storage"
|
|
}
|
|
path := filepath.Join(localPath, key)
|
|
return os.Remove(path)
|
|
}
|
|
// TODO: S3 implementation
|
|
return nil
|
|
}
|
|
|
|
func (s *Storage) SignURL(method, key string, expires time.Duration) (string, error) {
|
|
exp := time.Now().Add(expires).Unix()
|
|
sign := s.signature(method, key, exp)
|
|
|
|
baseURL := s.Config.BaseURL
|
|
// Ensure BaseURL doesn't end with slash if we add one
|
|
// Simplified: assume standard /v1/storage prefix in BaseURL or append it
|
|
// We'll append /<key>
|
|
|
|
u, err := url.Parse(baseURL + "/" + key)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
q := u.Query()
|
|
q.Set("expires", strconv.FormatInt(exp, 10))
|
|
q.Set("sign", sign)
|
|
u.RawQuery = q.Encode()
|
|
|
|
return u.String(), nil
|
|
}
|
|
|
|
func (s *Storage) Verify(method, key, expStr, sign string) error {
|
|
exp, err := strconv.ParseInt(expStr, 10, 64)
|
|
if err != nil {
|
|
return fmt.Errorf("invalid expiry")
|
|
}
|
|
if time.Now().Unix() > exp {
|
|
return fmt.Errorf("expired")
|
|
}
|
|
|
|
expected := s.signature(method, key, exp)
|
|
if !hmac.Equal([]byte(expected), []byte(sign)) {
|
|
return fmt.Errorf("invalid signature")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *Storage) signature(method, key string, exp int64) string {
|
|
str := fmt.Sprintf("%s\n%s\n%d", method, key, exp)
|
|
h := hmac.New(sha256.New, []byte(s.Config.Secret))
|
|
h.Write([]byte(str))
|
|
return hex.EncodeToString(h.Sum(nil))
|
|
}
|