118 lines
3.7 KiB
Go
118 lines
3.7 KiB
Go
package storage
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"net/url"
|
|
"path"
|
|
"strings"
|
|
|
|
cos "github.com/tencentyun/cos-go-sdk-v5"
|
|
"kra/internal/biz"
|
|
"kra/internal/conf"
|
|
)
|
|
|
|
type tencentStorage struct {
|
|
client *cos.Client
|
|
baseURL, prefix string
|
|
}
|
|
|
|
func newTencentStorage(config *conf.AdminBackend_ObjectStore) (biz.FileStorage, error) {
|
|
if config == nil || config.Bucket == "" || config.Region == "" || config.AccessKey == "" || config.SecretKey == "" {
|
|
return nil, fmt.Errorf("tencent-cos storage configuration is incomplete")
|
|
}
|
|
endpoint := config.Endpoint
|
|
if endpoint == "" {
|
|
endpoint = fmt.Sprintf("https://%s.cos.%s.myqcloud.com", config.Bucket, config.Region)
|
|
} else if !strings.HasPrefix(endpoint, "http://") && !strings.HasPrefix(endpoint, "https://") {
|
|
if config.UseSsl {
|
|
endpoint = "https://" + endpoint
|
|
} else {
|
|
endpoint = "http://" + endpoint
|
|
}
|
|
}
|
|
bucketURL, err := url.Parse(endpoint)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
client := cos.NewClient(&cos.BaseURL{BucketURL: bucketURL}, &http.Client{Transport: &cos.AuthorizationTransport{SecretID: config.AccessKey, SecretKey: config.SecretKey}})
|
|
return &tencentStorage{client: client, baseURL: strings.TrimSuffix(config.BaseUrl, "/"), prefix: strings.Trim(config.PathPrefix, "/")}, nil
|
|
}
|
|
func (s *tencentStorage) key(name string) string {
|
|
if s.prefix == "" {
|
|
return strings.TrimPrefix(name, "/")
|
|
}
|
|
return path.Join(s.prefix, strings.TrimPrefix(name, "/"))
|
|
}
|
|
func (s *tencentStorage) unkey(key string) string {
|
|
return strings.TrimPrefix(strings.TrimPrefix(key, s.prefix), "/")
|
|
}
|
|
func (s *tencentStorage) file(key string, size int64) *biz.StoredFile {
|
|
name := s.unkey(key)
|
|
fileURL := s.baseURL + "/" + key
|
|
return &biz.StoredFile{Name: path.Base(name), Path: name, URL: fileURL, Size: size}
|
|
}
|
|
func (s *tencentStorage) Put(ctx context.Context, name string, reader io.Reader) (*biz.StoredFile, error) {
|
|
key := s.key(name)
|
|
if _, err := s.client.Object.Put(ctx, key, reader, nil); err != nil {
|
|
return nil, err
|
|
}
|
|
response, err := s.client.Object.Head(ctx, key, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return s.file(key, response.ContentLength), nil
|
|
}
|
|
func (s *tencentStorage) Open(ctx context.Context, name string) (io.ReadCloser, error) {
|
|
response, err := s.client.Object.Get(ctx, s.key(name), nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return response.Body, nil
|
|
}
|
|
func (s *tencentStorage) Delete(ctx context.Context, name string) error {
|
|
_, err := s.client.Object.Delete(ctx, s.key(name))
|
|
return err
|
|
}
|
|
func (s *tencentStorage) Compose(ctx context.Context, names []string, destination string) (*biz.StoredFile, string, error) {
|
|
return composeFiles(ctx, s, names, destination)
|
|
}
|
|
func (s *tencentStorage) DeletePrefix(ctx context.Context, prefix string) error {
|
|
cursor := ""
|
|
for {
|
|
items, next, more, err := s.List(ctx, prefix, cursor, 1000)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for _, item := range items {
|
|
if err = s.Delete(ctx, item.Path); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
if !more {
|
|
return nil
|
|
}
|
|
cursor = next
|
|
}
|
|
}
|
|
func (s *tencentStorage) List(ctx context.Context, prefix, cursor string, limit int) ([]*biz.StoredFile, string, bool, error) {
|
|
if limit < 1 || limit > 1000 {
|
|
limit = 100
|
|
}
|
|
result, _, err := s.client.Bucket.Get(ctx, &cos.BucketGetOptions{Prefix: s.key(prefix), Marker: cursor, MaxKeys: limit})
|
|
if err != nil {
|
|
return nil, "", false, err
|
|
}
|
|
items := make([]*biz.StoredFile, 0, len(result.Contents))
|
|
for _, object := range result.Contents {
|
|
items = append(items, s.file(object.Key, object.Size))
|
|
}
|
|
next := result.NextMarker
|
|
if result.IsTruncated && next == "" && len(result.Contents) > 0 {
|
|
next = result.Contents[len(result.Contents)-1].Key
|
|
}
|
|
return items, next, result.IsTruncated, nil
|
|
}
|