mirror of
https://github.com/usual2970/certimate.git
synced 2025-08-11 20:01:45 +00:00
feat: new deployment provider: unicloud webhost
This commit is contained in:
257
internal/pkg/sdk3rd/dcloud/unicloud/client.go
Normal file
257
internal/pkg/sdk3rd/dcloud/unicloud/client.go
Normal file
@@ -0,0 +1,257 @@
|
||||
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
|
||||
}
|
Reference in New Issue
Block a user