github.com/weplanx/server@v0.2.6-0.20240318110640-f7e75155779a/api/tencent/service.go (about)

     1  package tencent
     2  
     3  import (
     4  	"context"
     5  	"crypto/hmac"
     6  	"crypto/sha1"
     7  	"encoding/base64"
     8  	"encoding/hex"
     9  	"fmt"
    10  	"github.com/bytedance/sonic"
    11  	"github.com/bytedance/sonic/decoder"
    12  	tcommon "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common"
    13  	"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile"
    14  	sms "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/sms/v20210111"
    15  	"github.com/tencentyun/cos-go-sdk-v5"
    16  	"github.com/weplanx/go/help"
    17  	"github.com/weplanx/server/common"
    18  	"net/http"
    19  	"net/url"
    20  	"sort"
    21  	"strings"
    22  	"time"
    23  )
    24  
    25  type Service struct {
    26  	*common.Inject
    27  }
    28  
    29  func (x *Service) Cos() (_ *cos.Client) {
    30  	u, _ := url.Parse(fmt.Sprintf(`https://%s.cos.%s.myqcloud.com`, x.V.TencentCosBucket, x.V.TencentCosRegion))
    31  	return cos.NewClient(&cos.BaseURL{BucketURL: u}, &http.Client{
    32  		Transport: &cos.AuthorizationTransport{
    33  			SecretID:  x.V.TencentSecretId,
    34  			SecretKey: x.V.TencentSecretKey,
    35  		},
    36  	})
    37  }
    38  
    39  func (x *Service) CosPresigned() (_ M, err error) {
    40  	date := time.Now()
    41  	expired := date.Add(time.Duration(x.V.TencentCosExpired) * time.Second)
    42  	keyTime := fmt.Sprintf(`%d;%d`, date.Unix(), expired.Unix())
    43  	name := help.Uuid()
    44  	key := fmt.Sprintf(`%s/%s/%s`,
    45  		x.V.Namespace, date.Format("20060102"), name)
    46  	policy := M{
    47  		"expiration": expired.Format("2006-01-02T15:04:05.000Z"),
    48  		"conditions": []interface{}{
    49  			M{"bucket": x.V.TencentCosBucket},
    50  			[]interface{}{"starts-with", "$key", key},
    51  			M{"q-sign-algorithm": "sha1"},
    52  			M{"q-ak": x.V.TencentSecretId},
    53  			M{"q-sign-time": keyTime},
    54  		},
    55  	}
    56  	var policyText []byte
    57  	if policyText, err = sonic.Marshal(policy); err != nil {
    58  		return
    59  	}
    60  	signKeyHash := hmac.New(sha1.New, []byte(x.V.TencentSecretKey))
    61  	signKeyHash.Write([]byte(keyTime))
    62  	signKey := hex.EncodeToString(signKeyHash.Sum(nil))
    63  	stringToSignHash := sha1.New()
    64  	stringToSignHash.Write(policyText)
    65  	stringToSign := hex.EncodeToString(stringToSignHash.Sum(nil))
    66  	signatureHash := hmac.New(sha1.New, []byte(signKey))
    67  	signatureHash.Write([]byte(stringToSign))
    68  	signature := hex.EncodeToString(signatureHash.Sum(nil))
    69  	return M{
    70  		"key":              key,
    71  		"policy":           policyText,
    72  		"q-sign-algorithm": "sha1",
    73  		"q-ak":             x.V.TencentSecretId,
    74  		"q-key-time":       keyTime,
    75  		"q-signature":      signature,
    76  	}, nil
    77  }
    78  
    79  func (x *Service) CosImageInfo(ctx context.Context, url string) (r M, err error) {
    80  	client := x.Cos()
    81  	var res *cos.Response
    82  	if res, err = client.CI.Get(ctx, url, "imageInfo", nil); err != nil {
    83  		return
    84  	}
    85  	if err = decoder.NewStreamDecoder(res.Body).Decode(&r); err != nil {
    86  		return
    87  	}
    88  	return
    89  }
    90  
    91  type TC3Option struct {
    92  	Service   string
    93  	Headers   map[string]string
    94  	Timestamp int64
    95  	Body      interface{}
    96  }
    97  
    98  func (x *Service) TC3Authorization(option TC3Option) string {
    99  	algorithm := "TC3-HMAC-SHA256"
   100  	canonicalURI := "/"
   101  	canonicalQueryString := ""
   102  
   103  	var keys []string
   104  	for key := range option.Headers {
   105  		keys = append(keys, key)
   106  	}
   107  	sort.Strings(keys)
   108  
   109  	var canonicalHeaders string
   110  	var signedHeaders string
   111  	for _, key := range keys {
   112  		k, v := strings.ToLower(key), strings.ToLower(option.Headers[key])
   113  		canonicalHeaders += fmt.Sprintf("%s:%s\n", k, v)
   114  		signedHeaders += ";" + k
   115  	}
   116  
   117  	signedHeaders = signedHeaders[1:]
   118  
   119  	payload, _ := sonic.MarshalString(option.Body)
   120  	hashedRequestPayload := common.Sha256hex(payload)
   121  	canonicalRequest := fmt.Sprintf("POST\n%s\n%s\n%s\n%s\n%s",
   122  		canonicalURI,
   123  		canonicalQueryString,
   124  		canonicalHeaders,
   125  		signedHeaders,
   126  		hashedRequestPayload,
   127  	)
   128  
   129  	date := time.Unix(option.Timestamp, 0).UTC().Format("2006-01-02")
   130  	credentialScope := fmt.Sprintf("%s/%s/tc3_request", date, option.Service)
   131  	hashedCanonicalRequest := common.Sha256hex(canonicalRequest)
   132  	string2sign := fmt.Sprintf("%s\n%d\n%s\n%s",
   133  		algorithm,
   134  		option.Timestamp,
   135  		credentialScope,
   136  		hashedCanonicalRequest,
   137  	)
   138  
   139  	secretDate := common.Hmacsha256(date, "TC3"+x.V.TencentSecretKey)
   140  	secretService := common.Hmacsha256(option.Service, secretDate)
   141  	secretSigning := common.Hmacsha256("tc3_request", secretService)
   142  	signature := hex.EncodeToString([]byte(common.Hmacsha256(string2sign, secretSigning)))
   143  
   144  	return fmt.Sprintf("%s Credential=%s/%s, SignedHeaders=%s, Signature=%s",
   145  		algorithm,
   146  		x.V.TencentSecretId,
   147  		credentialScope,
   148  		signedHeaders,
   149  		signature,
   150  	)
   151  }
   152  
   153  type KeyAuthResult struct {
   154  	Date string
   155  	Txt  string
   156  }
   157  
   158  func (x *Service) KeyAuth(source string, id string, key string) (r *KeyAuthResult, err error) {
   159  	r = new(KeyAuthResult)
   160  	location, _ := time.LoadLocation("Etc/UTC")
   161  	r.Date = time.Now().In(location).Format("Mon, 02 Jan 2006 15:04:05 GMT")
   162  	signStr := fmt.Sprintf("x-date: %s\nx-source: %s", r.Date, source)
   163  
   164  	mac := hmac.New(sha1.New, []byte(key))
   165  	mac.Write([]byte(signStr))
   166  	sign := base64.StdEncoding.EncodeToString(mac.Sum(nil))
   167  
   168  	r.Txt = fmt.Sprintf("hmac id=\"%s\", algorithm=\"hmac-sha1\", headers=\"x-date x-source\", signature=\"%s\"",
   169  		id, sign)
   170  	return
   171  }
   172  
   173  type IpResult interface {
   174  	GetMsg() string
   175  	IsSuccess() bool
   176  	GetDetail() interface{}
   177  }
   178  
   179  type Ipv4Result struct {
   180  	Msg     string `json:"msg"`
   181  	Success bool   `json:"success"`
   182  	Code    int    `json:"code"`
   183  	Data    struct {
   184  		OrderNo string     `json:"orderNo"`
   185  		Result  Ipv4Detail `json:"result"`
   186  	} `json:"data"`
   187  }
   188  
   189  func (x *Ipv4Result) GetMsg() string {
   190  	return x.Msg
   191  }
   192  
   193  func (x *Ipv4Result) IsSuccess() bool {
   194  	return x.Success
   195  }
   196  
   197  func (x *Ipv4Result) GetDetail() interface{} {
   198  	return x.Data.Result
   199  }
   200  
   201  type Ipv4Detail struct {
   202  	Continent string `bson:"continent" json:"continent"`
   203  	Country   string `bson:"country" json:"country"`
   204  	Province  string `bson:"prov" json:"prov"`
   205  	City      string `bson:"city" json:"city"`
   206  	Owner     string `bson:"owner" json:"owner"`
   207  	ISP       string `bson:"isp" json:"isp"`
   208  	Areacode  string `bson:"areacode" json:"areacode"`
   209  	Asnumber  string `bson:"asnumber" json:"asnumber"`
   210  	Adcode    string `bson:"adcode" json:"adcode"`
   211  	Zipcode   string `bson:"zipcode" json:"zipcode"`
   212  	Timezone  string `bson:"timezone" json:"timezone"`
   213  	Accuracy  string `bson:"accuracy" json:"accuracy"`
   214  	Lat       string `bson:"lat" json:"lat"`
   215  	Lng       string `bson:"lng" json:"lng"`
   216  	Radius    string `bson:"radius" json:"radius"`
   217  	Source    string `bson:"source" json:"source"`
   218  }
   219  
   220  func (x *Service) GetIpv4(ctx context.Context, ip string) (_ IpResult, err error) {
   221  	source, kar := "market", new(KeyAuthResult)
   222  	if kar, err = x.KeyAuth(source, x.V.IpSecretId, x.V.IpSecretKey); err != nil {
   223  		return
   224  	}
   225  	var r *Ipv4Result
   226  	var msg string
   227  	if _, err = common.HttpClient(x.V.IpAddress).R().
   228  		SetContext(ctx).
   229  		SetHeader("X-Source", source).
   230  		SetHeader("X-Date", kar.Date).
   231  		SetHeader("Authorization", kar.Txt).
   232  		SetQueryParam("ip", ip).
   233  		SetSuccessResult(&r).
   234  		SetErrorResult(&msg).
   235  		Get("/ip/city/query"); err != nil {
   236  		return nil, help.E("Tencent.GetIpv4Fail", err.Error())
   237  	}
   238  	if msg != "" {
   239  		return nil, help.E("Tencent.GetIpv4Fail", msg)
   240  	}
   241  	return r, nil
   242  }
   243  
   244  type Ipv6Result struct {
   245  	Msg     string `json:"msg"`
   246  	Success bool   `json:"success"`
   247  	Code    int    `json:"code"`
   248  	Data    struct {
   249  		OrderNo string     `json:"orderNo"`
   250  		Result  Ipv6Detail `json:"result"`
   251  	} `json:"data"`
   252  }
   253  
   254  func (x *Ipv6Result) GetMsg() string {
   255  	return x.Msg
   256  }
   257  
   258  func (x *Ipv6Result) IsSuccess() bool {
   259  	return x.Success
   260  }
   261  
   262  func (x *Ipv6Result) GetDetail() interface{} {
   263  	return x.Data.Result
   264  }
   265  
   266  type Ipv6Detail struct {
   267  	Continent string `bson:"continent" json:"continent"`
   268  	Country   string `bson:"country" json:"country"`
   269  	Province  string `bson:"prov" json:"province"`
   270  	City      string `bson:"city" json:"city"`
   271  	Owner     string `bson:"owner" json:"owner"`
   272  	ISP       string `bson:"isp" json:"isp"`
   273  	Areacode  string `bson:"areacode" json:"areacode"`
   274  	Asnumber  string `bson:"asnumber" json:"asnumber"`
   275  	Adcode    string `bson:"adcode" json:"adcode"`
   276  	Zipcode   string `bson:"zipcode" json:"zipcode"`
   277  	Timezone  string `bson:"timezone" json:"timezone"`
   278  	Accuracy  string `bson:"accuracy" json:"accuracy"`
   279  	Lat       string `bson:"lat" json:"lat"`
   280  	Lng       string `bson:"lng" json:"lng"`
   281  	Radius    string `bson:"radius" json:"radius"`
   282  	Source    string `bson:"source" json:"source"`
   283  }
   284  
   285  func (x *Service) GetIpv6(ctx context.Context, ip string) (_ IpResult, err error) {
   286  	source, kar := "market", new(KeyAuthResult)
   287  	if kar, err = x.KeyAuth(source, x.V.Ipv6SecretId, x.V.Ipv6SecretKey); err != nil {
   288  		return
   289  	}
   290  
   291  	var r *Ipv6Result
   292  	var msg string
   293  	if _, err = common.HttpClient(x.V.Ipv6Address).R().
   294  		SetContext(ctx).
   295  		SetHeader("X-Source", source).
   296  		SetHeader("X-Date", kar.Date).
   297  		SetHeader("Authorization", kar.Txt).
   298  		SetQueryParam("ip", ip).
   299  		SetSuccessResult(&r).
   300  		SetErrorResult(&msg).
   301  		Get("/ip/ipv6/query"); err != nil {
   302  		return nil, help.E("Tencent.GetIpv6Fail", err.Error())
   303  	}
   304  	if msg != "" {
   305  		return nil, help.E("Tencent.GetIpv6Fail", msg)
   306  	}
   307  
   308  	return r, nil
   309  }
   310  
   311  func (x *Service) SmsSend(ctx context.Context, sign string, tid string, params []string, phone []string) (err error) {
   312  	credential := tcommon.NewCredential(
   313  		x.V.SmsSecretId,
   314  		x.V.SmsSecretKey,
   315  	)
   316  	cpf := profile.NewClientProfile()
   317  	cpf.HttpProfile.Endpoint = "sms.tencentcloudapi.com"
   318  	client, _ := sms.NewClient(credential, x.V.SmsRegion, cpf)
   319  	request := sms.NewSendSmsRequest()
   320  	request.SmsSdkAppId = tcommon.StringPtr(x.V.SmsAppId)
   321  	request.SignName = tcommon.StringPtr(sign)
   322  	request.TemplateId = tcommon.StringPtr(tid)
   323  	request.TemplateParamSet = tcommon.StringPtrs(params)
   324  	request.PhoneNumberSet = tcommon.StringPtrs(phone)
   325  	request.SetContext(ctx)
   326  	if _, err = client.SendSms(request); err != nil {
   327  		return
   328  	}
   329  	return
   330  }