mirror of
https://github.com/usual2970/certimate.git
synced 2025-06-07 21:19:51 +00:00
258 lines
7.0 KiB
Go
258 lines
7.0 KiB
Go
package unicloud
|
|
|
|
import (
|
|
"crypto/hmac"
|
|
"crypto/md5"
|
|
"encoding/hex"
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
"runtime"
|
|
"sort"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/go-resty/resty/v2"
|
|
)
|
|
|
|
type Client struct {
|
|
username string
|
|
password string
|
|
|
|
serverlessJwtToken string
|
|
serverlessJwtTokenExp time.Time
|
|
serverlessJwtTokenMtx sync.Mutex
|
|
|
|
serverlessClient *resty.Client
|
|
|
|
apiUserToken string
|
|
apiUserTokenMtx sync.Mutex
|
|
|
|
apiClient *resty.Client
|
|
}
|
|
|
|
const (
|
|
uniIdentityEndpoint = "https://account.dcloud.net.cn/client"
|
|
uniIdentityClientSecret = "ba461799-fde8-429f-8cc4-4b6d306e2339"
|
|
uniIdentityAppId = "__UNI__uniid_server"
|
|
uniIdentitySpaceId = "uni-id-server"
|
|
uniConsoleEndpoint = "https://unicloud.dcloud.net.cn/client"
|
|
uniConsoleClientSecret = "4c1f7fbf-c732-42b0-ab10-4634a8bbe834"
|
|
uniConsoleAppId = "__UNI__unicloud_console"
|
|
uniConsoleSpaceId = "dc-6nfabcn6ada8d3dd"
|
|
)
|
|
|
|
func NewClient(username, password string) *Client {
|
|
client := &Client{
|
|
username: username,
|
|
password: password,
|
|
}
|
|
client.serverlessClient = resty.New()
|
|
client.apiClient = resty.New().
|
|
SetBaseURL("https://unicloud-api.dcloud.net.cn/unicloud/api").
|
|
SetPreRequestHook(func(c *resty.Client, req *http.Request) error {
|
|
if client.apiUserToken != "" {
|
|
req.Header.Set("Token", client.apiUserToken)
|
|
}
|
|
|
|
return nil
|
|
})
|
|
|
|
return client
|
|
}
|
|
|
|
func (c *Client) WithTimeout(timeout time.Duration) *Client {
|
|
c.serverlessClient.SetTimeout(timeout)
|
|
return c
|
|
}
|
|
|
|
func (c *Client) generateSignature(params map[string]any, secret string) string {
|
|
keys := make([]string, 0, len(params))
|
|
for k := range params {
|
|
keys = append(keys, k)
|
|
}
|
|
sort.Strings(keys)
|
|
|
|
canonicalStr := ""
|
|
for i, k := range keys {
|
|
if i > 0 {
|
|
canonicalStr += "&"
|
|
}
|
|
canonicalStr += k + "=" + fmt.Sprintf("%v", params[k])
|
|
}
|
|
|
|
mac := hmac.New(md5.New, []byte(secret))
|
|
mac.Write([]byte(canonicalStr))
|
|
sign := mac.Sum(nil)
|
|
signHex := hex.EncodeToString(sign)
|
|
|
|
return signHex
|
|
}
|
|
|
|
func (c *Client) buildServerlessClientInfo(appId string) (_clientInfo map[string]any, _err error) {
|
|
return map[string]any{
|
|
"PLATFORM": "web",
|
|
"OS": strings.ToUpper(runtime.GOOS),
|
|
"APPID": appId,
|
|
"DEVICEID": "certimate",
|
|
"LOCALE": "zh-Hans",
|
|
"osName": runtime.GOOS,
|
|
"appId": appId,
|
|
"appName": "uniCloud",
|
|
"deviceId": "certimate",
|
|
"deviceType": "pc",
|
|
"uniPlatform": "web",
|
|
"uniCompilerVersion": "4.45",
|
|
"uniRuntimeVersion": "4.45",
|
|
}, nil
|
|
}
|
|
|
|
func (c *Client) buildServerlessPayloadInfo(appId, spaceId, target, method, action string, params, data interface{}) (map[string]any, error) {
|
|
clientInfo, err := c.buildServerlessClientInfo(appId)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
functionArgsParams := make([]any, 0)
|
|
if params != nil {
|
|
functionArgsParams = append(functionArgsParams, params)
|
|
}
|
|
|
|
functionArgs := map[string]any{
|
|
"clientInfo": clientInfo,
|
|
"uniIdToken": c.serverlessJwtToken,
|
|
}
|
|
if method != "" {
|
|
functionArgs["method"] = method
|
|
functionArgs["params"] = make([]any, 0)
|
|
}
|
|
if action != "" {
|
|
type _obj struct{}
|
|
functionArgs["action"] = action
|
|
functionArgs["data"] = &_obj{}
|
|
}
|
|
if params != nil {
|
|
functionArgs["params"] = []any{params}
|
|
}
|
|
if data != nil {
|
|
functionArgs["data"] = data
|
|
}
|
|
|
|
jsonb, err := json.Marshal(map[string]any{
|
|
"functionTarget": target,
|
|
"functionArgs": functionArgs,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
payload := map[string]any{
|
|
"method": "serverless.function.runtime.invoke",
|
|
"params": string(jsonb),
|
|
"spaceId": spaceId,
|
|
"timestamp": time.Now().UnixMilli(),
|
|
}
|
|
|
|
return payload, nil
|
|
}
|
|
|
|
func (c *Client) invokeServerless(endpoint, clientSecret, appId, spaceId, target, method, action string, params, data interface{}) (*resty.Response, error) {
|
|
if endpoint == "" {
|
|
return nil, fmt.Errorf("unicloud api error: endpoint cannot be empty")
|
|
}
|
|
|
|
payload, err := c.buildServerlessPayloadInfo(appId, spaceId, target, method, action, params, data)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("unicloud api error: failed to build request: %w", err)
|
|
}
|
|
|
|
clientInfo, _ := c.buildServerlessClientInfo(appId)
|
|
clientInfoJsonb, _ := json.Marshal(clientInfo)
|
|
|
|
sign := c.generateSignature(payload, clientSecret)
|
|
|
|
req := c.serverlessClient.R().
|
|
SetHeader("Origin", "https://unicloud.dcloud.net.cn").
|
|
SetHeader("Referer", "https://unicloud.dcloud.net.cn").
|
|
SetHeader("Content-Type", "application/json").
|
|
SetHeader("X-Client-Info", string(clientInfoJsonb)).
|
|
SetHeader("X-Client-Token", c.serverlessJwtToken).
|
|
SetHeader("X-Serverless-Sign", sign).
|
|
SetBody(payload)
|
|
resp, err := req.Post(endpoint)
|
|
if err != nil {
|
|
return resp, fmt.Errorf("unicloud api error: failed to send request: %w", err)
|
|
} else if resp.IsError() {
|
|
return resp, fmt.Errorf("unicloud api error: unexpected status code: %d, resp: %s", resp.StatusCode(), resp.String())
|
|
}
|
|
|
|
return resp, nil
|
|
}
|
|
|
|
func (c *Client) invokeServerlessWithResult(endpoint, clientSecret, appId, spaceId, target, method, action string, params, data interface{}, result BaseResponse) error {
|
|
resp, err := c.invokeServerless(endpoint, clientSecret, appId, spaceId, target, method, action, params, data)
|
|
if err != nil {
|
|
if resp != nil {
|
|
json.Unmarshal(resp.Body(), &result)
|
|
}
|
|
return err
|
|
}
|
|
|
|
if err := json.Unmarshal(resp.Body(), &result); err != nil {
|
|
return fmt.Errorf("unicloud api error: failed to unmarshal response: %w", err)
|
|
} else if success := result.GetSuccess(); !success {
|
|
return fmt.Errorf("unicloud api error: code='%s', message='%s'", result.GetErrorCode(), result.GetErrorMessage())
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (c *Client) sendRequest(method string, path string, params interface{}) (*resty.Response, error) {
|
|
req := c.apiClient.R()
|
|
if strings.EqualFold(method, http.MethodGet) {
|
|
qs := make(map[string]string)
|
|
if params != nil {
|
|
temp := make(map[string]any)
|
|
jsonb, _ := json.Marshal(params)
|
|
json.Unmarshal(jsonb, &temp)
|
|
for k, v := range temp {
|
|
if v != nil {
|
|
qs[k] = fmt.Sprintf("%v", v)
|
|
}
|
|
}
|
|
}
|
|
|
|
req = req.SetQueryParams(qs)
|
|
} else {
|
|
req = req.SetHeader("Content-Type", "application/json").SetBody(params)
|
|
}
|
|
|
|
resp, err := req.Execute(method, path)
|
|
if err != nil {
|
|
return resp, fmt.Errorf("unicloud api error: failed to send request: %w", err)
|
|
} else if resp.IsError() {
|
|
return resp, fmt.Errorf("unicloud api error: unexpected status code: %d, resp: %s", resp.StatusCode(), resp.String())
|
|
}
|
|
|
|
return resp, nil
|
|
}
|
|
|
|
func (c *Client) sendRequestWithResult(method string, path string, params interface{}, result BaseResponse) error {
|
|
resp, err := c.sendRequest(method, path, params)
|
|
if err != nil {
|
|
if resp != nil {
|
|
json.Unmarshal(resp.Body(), &result)
|
|
}
|
|
return err
|
|
}
|
|
|
|
if err := json.Unmarshal(resp.Body(), &result); err != nil {
|
|
return fmt.Errorf("unicloud api error: failed to unmarshal response: %w", err)
|
|
} else if retcode := result.GetReturnCode(); retcode != 0 {
|
|
return fmt.Errorf("unicloud api error: ret='%d', desc='%s'", retcode, result.GetReturnDesc())
|
|
}
|
|
|
|
return nil
|
|
}
|