plugin/dctw/v1/core/core.go

197 lines
4.4 KiB
Go

package core
import (
"bytes"
"context"
"fmt"
"gitea.cdlsxd.cn/sdk/plugin/utils"
"github.com/go-playground/validator/v10"
"io/ioutil"
"net/http"
"strings"
"time"
)
const (
DeBug = true
SignType = "md5"
SignKeyName = "Authorization"
ApplicationJSON = "application/json"
)
// DctWRequest 直连天下 Direct connection The world
type DctWRequest interface {
Json() ([]byte, error)
Validate() error
}
type DctWConfig struct {
AppId string `json:"app_id" validate:"required"`
AppKey string `json:"app_key" validate:"required"`
BaseUri string `json:"base_uri" validate:"required"`
}
func (c *DctWConfig) Validate() error {
if err := validator.New().Struct(c); err != nil {
for _, err = range err.(validator.ValidationErrors) {
return err
}
}
return nil
}
type DctWServer struct {
DeBug bool
Config *DctWConfig
HttpClient *http.Client
}
type Option func(*DctWServer)
func WithHttpClient(client *http.Client) Option {
return func(s *DctWServer) {
s.HttpClient = client
}
}
func WithDebug(debug bool) Option {
return func(s *DctWServer) {
s.DeBug = debug
}
}
func NewDctWServer(config *DctWConfig, o ...Option) (*DctWServer, error) {
if err := config.Validate(); err != nil {
return nil, err
}
httpClient := &http.Client{
Timeout: 15 * time.Second,
}
server := &DctWServer{
DeBug: DeBug,
Config: config,
HttpClient: httpClient,
}
for _, f := range o {
f(server)
}
return server, nil
}
func (c *DctWServer) Sign(body, path, timestamp string) string {
str := c.Config.AppId + body + path + timestamp + c.Config.AppKey
return utils.Md5([]byte(str))
}
func (c *DctWServer) BuildAuth(signStr, timestamp string) string {
return fmt.Sprintf("%s appid=%s,timestamp=%s,sign=%s", SignType, c.Config.AppId, timestamp, signStr)
}
func (c *DctWServer) Headers(signStr, timestamp string) http.Header {
h := make(http.Header)
h.Add("Content-type", ApplicationJSON)
h.Add(SignKeyName, c.BuildAuth(signStr, timestamp))
return h
}
func (c *DctWServer) verify(authorization, path string, body []byte) error {
trimmedAuth := strings.TrimPrefix(authorization, "MD5 ")
authData := make(map[string]string)
parts := strings.Split(trimmedAuth, ",")
for _, part := range parts {
keyValue := strings.Split(part, "=")
if len(keyValue) != 2 {
return fmt.Errorf("授权信息格式错误: %s", part)
}
authData[keyValue[0]] = keyValue[1]
}
sign, ok := authData["sign"]
if !ok {
return fmt.Errorf("签名数据获取失败")
}
timestamp, ok := authData["timestamp"]
if !ok {
return fmt.Errorf("时间戳获取失败")
}
calculatedSign := c.Sign(string(body), path, timestamp)
if sign == calculatedSign {
return nil
}
return fmt.Errorf("验签失败")
}
func (c *DctWServer) VerifyDctW(authorization string, body []byte) error {
trimmedAuth := strings.TrimPrefix(authorization, "MD5 ")
authData := make(map[string]string)
parts := strings.Split(trimmedAuth, ",")
for _, part := range parts {
keyValue := strings.Split(part, "=")
if len(keyValue) != 2 {
return fmt.Errorf("授权信息格式错误: %s", part)
}
authData[keyValue[0]] = keyValue[1]
}
sign, ok := authData["sign"]
if !ok {
return fmt.Errorf("签名数据获取失败")
}
signStr := fmt.Sprintf("%s%s%s", c.Config.AppId, string(body), c.Config.AppKey)
calculatedSign := utils.Md5([]byte(signStr))
if sign == calculatedSign {
return nil
}
return fmt.Errorf("验签失败")
}
func (c *DctWServer) Request(_ context.Context, request DctWRequest, path string) ([]byte, error) {
timestamp := fmt.Sprintf("%d", time.Now().Unix())
body, err := request.Json()
if err != nil {
return nil, err
}
signStr := c.Sign(string(body), path, timestamp)
url := fmt.Sprintf("%s%s", c.Config.BaseUri, path)
httpClient := &http.Client{
Timeout: 15 * time.Second,
}
req, err := http.NewRequest("POST", url, bytes.NewBuffer(body))
if err != nil {
return nil, err
}
req.Header = c.Headers(signStr, timestamp)
if c.DeBug {
fmt.Printf("url:%s\n", url)
fmt.Printf("body:%s\n", string(body))
fmt.Printf("signStr:%s\n", signStr)
fmt.Printf("Header:%+v\n", req.Header)
}
resp, err := httpClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
bodyBytes, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
if c.DeBug {
fmt.Printf("resp.Status:%s\n", resp.Status)
fmt.Printf("bodyStr:%s\n", string(bodyBytes))
}
return bodyBytes, nil
}