diff --git a/biz/application/service/cos.go b/biz/application/service/cos.go index 5abbaf8..42dece0 100644 --- a/biz/application/service/cos.go +++ b/biz/application/service/cos.go @@ -9,7 +9,6 @@ import ( "github.com/google/wire" cossts "github.com/tencentyun/qcloud-cos-sts-sdk/go" "github.com/xh-polaris/platform-sts/biz/infrastructure/consts" - "time" ) type ICosService interface { @@ -30,9 +29,12 @@ var CosSet = wire.NewSet( func (s *CosService) GenCosSts(ctx context.Context, req *gensts.GenCosStsReq) (*gensts.GenCosStsResp, error) { cosConfig := s.Config.CosConfig + if req.IsFile { + cosConfig = s.Config.FileCosConfig + } stsOption := &cossts.CredentialOptions{ // 临时密钥有效时长,单位是秒 - DurationSeconds: int64(10 * time.Minute.Seconds()), + DurationSeconds: req.Time, Region: cosConfig.Region, Policy: &cossts.CredentialPolicy{ Statement: []cossts.CredentialPolicyStatement{ @@ -48,6 +50,7 @@ func (s *CosService) GenCosSts(ctx context.Context, req *gensts.GenCosStsReq) (* "name/cos:ListParts", "name/cos:UploadPart", "name/cos:CompleteMultipartUpload", + "name/cos:GetObject", }, Effect: "allow", // 密钥可控制的资源列表。此处开放名字为用户ID的文件夹及其子文件夹 @@ -60,7 +63,7 @@ func (s *CosService) GenCosSts(ctx context.Context, req *gensts.GenCosStsReq) (* }, } - res, err := s.CosSDK.GetCredential(ctx, stsOption) + res, err := s.CosSDK.GetCredential(ctx, stsOption, req.IsFile) if err != nil { return nil, err } @@ -74,18 +77,17 @@ func (s *CosService) GenCosSts(ctx context.Context, req *gensts.GenCosStsReq) (* }, nil } -func (s *CosService) GenSignedUrl(ctx context.Context, req *gensts.GenSignedUrlReq) (*gensts.GenSignedUrlResp, error) { - signedUrl, err := s.CosSDK.GetPresignedURL(ctx, req.Method, req.Path, req.SecretId, req.SecretKey, time.Minute, nil) - if err != nil { - return nil, err - } - return &gensts.GenSignedUrlResp{SignedUrl: signedUrl.String()}, nil +func (s *CosService) GenSignedUrl(ctx context.Context, req *gensts.GenSignedUrlReq) (resp *gensts.GenSignedUrlResp, err error) { + resp = new(gensts.GenSignedUrlResp) + resp.SignedUrl = s.CosSDK.GenerateURL(s.CosSDK.CDNConf.Prefix+req.Path, int(req.Ttl)) + return resp, nil } -func (s *CosService) DeleteObject(ctx context.Context, req *gensts.DeleteObjectReq) (*gensts.DeleteObjectResp, error) { +func (s *CosService) DeleteObject(ctx context.Context, req *gensts.DeleteObjectReq) (resp *gensts.DeleteObjectResp, err error) { + resp = new(gensts.DeleteObjectResp) res, err := s.CosSDK.Delete(ctx, req.Path) if err != nil || res.StatusCode != 200 { - return nil, consts.ErrCannotDeleteObject + return resp, consts.ErrCannotDeleteObject } - return &gensts.DeleteObjectResp{}, nil + return resp, nil } diff --git a/biz/infrastructure/config/config.go b/biz/infrastructure/config/config.go index 16692e2..cae42ca 100644 --- a/biz/infrastructure/config/config.go +++ b/biz/infrastructure/config/config.go @@ -24,12 +24,16 @@ type CosConfig struct { SecretKey string } -func (c *CosConfig) CosHost() string { - return fmt.Sprintf("https://%s.cos.%s.myqcloud.com", c.BucketName, c.Region) +type CDNConfig struct { + Url string + Key string + Prefix string + MinTTL int + MaxTTL int } -func (c *CosConfig) CIHost() string { - return fmt.Sprintf("https://%s.ci.%s.myqcloud.com", c.BucketName, c.Region) +func (c *CosConfig) CosHost() string { + return fmt.Sprintf("https://%s.cos.%s.myqcloud.com", c.BucketName, c.Region) } type Config struct { @@ -39,10 +43,12 @@ type Config struct { URL string DB string } - CacheConf cache.CacheConf - Redis *redis.RedisConf - EmailConf EmailConf - CosConfig *CosConfig + CacheConf cache.CacheConf + Redis *redis.RedisConf + EmailConf EmailConf + CosConfig *CosConfig + FileCosConfig *CosConfig + CdnConfig *CDNConfig } func NewConfig() (*Config, error) { diff --git a/biz/infrastructure/util/sdk/cos/cos.go b/biz/infrastructure/util/sdk/cos/cos.go index d5df94e..2f64c1c 100644 --- a/biz/infrastructure/util/sdk/cos/cos.go +++ b/biz/infrastructure/util/sdk/cos/cos.go @@ -2,10 +2,14 @@ package cos import ( "context" + "crypto/md5" + "encoding/hex" + "fmt" "github.com/CloudStriver/cloudmind-sts/biz/infrastructure/config" "github.com/tencentyun/cos-go-sdk-v5" "net/http" "net/url" + "strconv" "time" "github.com/google/wire" @@ -15,8 +19,11 @@ import ( ) type CosSDK struct { - stsClient *sts.Client - cosClient *cos.Client + stsClient *sts.Client + cosClient *cos.Client + fileStsClient *sts.Client + fileCosClient *cos.Client + CDNConf *config.CDNConfig } func NewCosSDK(config *config.Config) (*CosSDK, error) { @@ -24,7 +31,7 @@ func NewCosSDK(config *config.Config) (*CosSDK, error) { if err != nil { return nil, err } - ciURL, err := url.Parse(config.CosConfig.CIHost()) + fileBucketURL, err := url.Parse(config.FileCosConfig.CosHost()) if err != nil { return nil, err } @@ -35,21 +42,55 @@ func NewCosSDK(config *config.Config) (*CosSDK, error) { nil), cosClient: cos.NewClient(&cos.BaseURL{ BucketURL: bucketURL, - CIURL: ciURL, }, &http.Client{ Transport: &cos.AuthorizationTransport{ SecretID: config.CosConfig.SecretId, SecretKey: config.CosConfig.SecretKey, }, }), + fileStsClient: sts.NewClient( + config.FileCosConfig.SecretId, + config.FileCosConfig.SecretKey, + nil), + fileCosClient: cos.NewClient(&cos.BaseURL{ + BucketURL: fileBucketURL, + }, &http.Client{ + Transport: &cos.AuthorizationTransport{ + SecretID: config.FileCosConfig.SecretId, + SecretKey: config.FileCosConfig.SecretKey, + }, + }), + CDNConf: config.CdnConfig, }, nil } - -func (s *CosSDK) GetCredential(ctx context.Context, opt *sts.CredentialOptions) (*sts.CredentialResult, error) { +func (s *CosSDK) GenerateURL(path string, ttl int) string { + if ttl < s.CDNConf.MinTTL { + ttl = s.CDNConf.MinTTL + } else if ttl > s.CDNConf.MaxTTL { + ttl = s.CDNConf.MaxTTL + } + url := s.CDNConf.Url + key := s.CDNConf.Key + now := time.Now().Add(-time.Duration(s.CDNConf.MaxTTL-ttl) * time.Second).Unix() + signKey := "sign" + timeKey := "t" + ttlFormat := 10 + var requestURL string + tsFormat := strconv.FormatInt(now, ttlFormat) + sign := fmt.Sprintf("%s%s%s", key, path, tsFormat) + signMD5 := md5.Sum([]byte(sign)) + signHex := hex.EncodeToString(signMD5[:]) + requestURL = fmt.Sprintf("%s%s?%s=%s&%s=%s", url, path, signKey, signHex, timeKey, tsFormat) + return requestURL +} +func (s *CosSDK) GetCredential(ctx context.Context, opt *sts.CredentialOptions, isFile bool) (*sts.CredentialResult, error) { _, span := trace.TracerFromContext(ctx).Start(ctx, "sts/GetCredential", oteltrace.WithTimestamp(time.Now()), oteltrace.WithSpanKind(oteltrace.SpanKindClient)) defer func() { span.End(oteltrace.WithTimestamp(time.Now())) }() + if isFile { + return s.fileStsClient.GetCredential(opt) + } return s.stsClient.GetCredential(opt) } diff --git a/go.mod b/go.mod index feff369..2dffc33 100644 --- a/go.mod +++ b/go.mod @@ -4,11 +4,12 @@ go 1.20 require ( github.com/CloudStriver/go-pkg v0.0.0-20240115102515-f1d7bfa047af - github.com/CloudStriver/service-idl-gen-go v0.0.0-20240124091147-b9f3b0ea9d4b + github.com/CloudStriver/service-idl-gen-go v0.0.0-20240201094804-9bf4d9eee7a9 github.com/cloudwego/kitex v0.8.0 github.com/google/wire v0.5.0 github.com/kitex-contrib/obs-opentelemetry v0.2.5 github.com/pkg/errors v0.9.1 + github.com/samber/lo v1.39.0 github.com/tencentyun/cos-go-sdk-v5 v0.7.45 github.com/tencentyun/qcloud-cos-sts-sdk v0.0.0-20231121073521-dd65d8941a16 github.com/xh-polaris/platform-sts v1.4.34 @@ -18,6 +19,8 @@ require ( google.golang.org/grpc v1.60.1 ) +//replace github.com/CloudStriver/service-idl-gen-go => ../service-idl-gen-go + require ( github.com/apache/thrift v0.16.0 // indirect github.com/beorn7/perks v1.0.1 // indirect @@ -97,6 +100,7 @@ require ( go.uber.org/automaxprocs v1.5.3 // indirect golang.org/x/arch v0.2.0 // indirect golang.org/x/crypto v0.16.0 // indirect + golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 // indirect golang.org/x/net v0.19.0 // indirect golang.org/x/sync v0.5.0 // indirect golang.org/x/sys v0.15.0 // indirect diff --git a/go.sum b/go.sum index d2dca84..430ab10 100644 --- a/go.sum +++ b/go.sum @@ -8,6 +8,8 @@ github.com/CloudStriver/go-pkg v0.0.0-20240115102515-f1d7bfa047af h1:tl3MgdfL4fO github.com/CloudStriver/go-pkg v0.0.0-20240115102515-f1d7bfa047af/go.mod h1:RMjN80WnoDiqHZIsv27u9BxJ9axldr+elFRHgSjhXnY= github.com/CloudStriver/service-idl-gen-go v0.0.0-20240124091147-b9f3b0ea9d4b h1:3QmklzpLCqO3B6enVQEo4QVbSaMr2x/Skyw5aR4BgEs= github.com/CloudStriver/service-idl-gen-go v0.0.0-20240124091147-b9f3b0ea9d4b/go.mod h1:chtR82RvfrjUujTGWROSCNAwF9Lh/U959k34bXIDvBI= +github.com/CloudStriver/service-idl-gen-go v0.0.0-20240201094804-9bf4d9eee7a9 h1:VvTEyApI2g6u+YAzbS8f7kcAwxnd0cTQDL3hHtTYo0A= +github.com/CloudStriver/service-idl-gen-go v0.0.0-20240201094804-9bf4d9eee7a9/go.mod h1:chtR82RvfrjUujTGWROSCNAwF9Lh/U959k34bXIDvBI= github.com/QcloudApi/qcloud_sign_golang v0.0.0-20141224014652-e4130a326409/go.mod h1:1pk82RBxDY/JZnPQrtqHlUFfCctgdorsd9M06fMynOM= github.com/ajstarks/deck v0.0.0-20200831202436-30c9fc6549a9/go.mod h1:JynElWSGnm/4RlzPXRlREEwqTHAN3T56Bv2ITsFT3gY= github.com/ajstarks/deck/generate v0.0.0-20210309230005-c3f852c02e19/go.mod h1:T13YZdzov6OU0A1+RfKZiZN9ca6VeKdBdyDV+BY97Tk= @@ -281,6 +283,8 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w= github.com/ruudk/golang-pdf417 v0.0.0-20201230142125-a7e3863a1245/go.mod h1:pQAZKsJ8yyVxGRWYNEm9oFB8ieLgKFnamEyDmSA0BRk= +github.com/samber/lo v1.39.0 h1:4gTz1wUhNYLhFSKl6O+8peW0v2F4BCY034GRpU9WnuA= +github.com/samber/lo v1.39.0/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= @@ -393,6 +397,8 @@ golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20191002040644-a1355ae1e2c3/go.mod h1:NOZ3BPKG0ec/BKJQgnvsSFpcKLM5xXVWnvZS97DWHgE= +golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 h1:3MTrJm4PyNL9NBqvYDSj3DHl46qQakyfqfWo4jgfaEM= +golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE= golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=