chore: move '/internal/pkg' to '/pkg'
This commit is contained in:
27
pkg/sdk3rd/dcloud/unicloud/api_create_domain_with_cert.go
Normal file
27
pkg/sdk3rd/dcloud/unicloud/api_create_domain_with_cert.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package unicloud
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type CreateDomainWithCertRequest struct {
|
||||
Provider string `json:"provider"`
|
||||
SpaceId string `json:"spaceId"`
|
||||
Domain string `json:"domain"`
|
||||
Cert string `json:"cert"`
|
||||
Key string `json:"key"`
|
||||
}
|
||||
|
||||
type CreateDomainWithCertResponse struct {
|
||||
apiResponseBase
|
||||
}
|
||||
|
||||
func (c *Client) CreateDomainWithCert(req *CreateDomainWithCertRequest) (*CreateDomainWithCertResponse, error) {
|
||||
if err := c.ensureApiUserTokenExists(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp := &CreateDomainWithCertResponse{}
|
||||
err := c.sendRequestWithResult(http.MethodPost, "/host/create-domain-with-cert", req, resp)
|
||||
return resp, err
|
||||
}
|
||||
358
pkg/sdk3rd/dcloud/unicloud/client.go
Normal file
358
pkg/sdk3rd/dcloud/unicloud/client.go
Normal file
@@ -0,0 +1,358 @@
|
||||
package unicloud
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"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, error) {
|
||||
if username == "" {
|
||||
return nil, fmt.Errorf("sdkerr: unset username")
|
||||
}
|
||||
if password == "" {
|
||||
return nil, fmt.Errorf("sdkerr: unset password")
|
||||
}
|
||||
|
||||
client := &Client{
|
||||
username: username,
|
||||
password: password,
|
||||
}
|
||||
client.serverlessClient = resty.New().
|
||||
SetHeader("Accept", "application/json").
|
||||
SetHeader("Content-Type", "application/json").
|
||||
SetHeader("User-Agent", "certimate")
|
||||
client.apiClient = resty.New().
|
||||
SetBaseURL("https://unicloud-api.dcloud.net.cn/unicloud/api").
|
||||
SetHeader("Accept", "application/json").
|
||||
SetHeader("Content-Type", "application/json").
|
||||
SetHeader("User-Agent", "certimate").
|
||||
SetPreRequestHook(func(c *resty.Client, req *http.Request) error {
|
||||
if client.apiUserToken != "" {
|
||||
req.Header.Set("Token", client.apiUserToken)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
return client, nil
|
||||
}
|
||||
|
||||
func (c *Client) SetTimeout(timeout time.Duration) *Client {
|
||||
c.serverlessClient.SetTimeout(timeout)
|
||||
return c
|
||||
}
|
||||
|
||||
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 := generateSignature(payload, clientSecret)
|
||||
|
||||
req := c.serverlessClient.R().
|
||||
SetHeader("Content-Type", "application/json").
|
||||
SetHeader("Origin", "https://unicloud.dcloud.net.cn").
|
||||
SetHeader("Referer", "https://unicloud.dcloud.net.cn").
|
||||
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 apiResponse) 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 apiResponse) 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
|
||||
}
|
||||
|
||||
func (c *Client) ensureServerlessJwtTokenExists() error {
|
||||
c.serverlessJwtTokenMtx.Lock()
|
||||
defer c.serverlessJwtTokenMtx.Unlock()
|
||||
if c.serverlessJwtToken != "" && c.serverlessJwtTokenExp.After(time.Now()) {
|
||||
return nil
|
||||
}
|
||||
|
||||
params := map[string]string{
|
||||
"password": "password",
|
||||
}
|
||||
if regexp.MustCompile("^1\\d{10}$").MatchString(c.username) {
|
||||
params["mobile"] = c.username
|
||||
} else if regexp.MustCompile("^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$").MatchString(c.username) {
|
||||
params["email"] = c.username
|
||||
} else {
|
||||
params["username"] = c.username
|
||||
}
|
||||
|
||||
type loginResponse struct {
|
||||
apiResponseBase
|
||||
Data *struct {
|
||||
Code int32 `json:"errCode"`
|
||||
UID string `json:"uid"`
|
||||
NewToken *struct {
|
||||
Token string `json:"token"`
|
||||
TokenExpired int64 `json:"tokenExpired"`
|
||||
} `json:"newToken,omitempty"`
|
||||
} `json:"data,omitempty"`
|
||||
}
|
||||
|
||||
resp := &loginResponse{}
|
||||
if err := c.invokeServerlessWithResult(
|
||||
uniIdentityEndpoint, uniIdentityClientSecret, uniIdentityAppId, uniIdentitySpaceId,
|
||||
"uni-id-co", "login", "", params, nil,
|
||||
resp); err != nil {
|
||||
return err
|
||||
} else if resp.Data == nil || resp.Data.NewToken == nil || resp.Data.NewToken.Token == "" {
|
||||
return fmt.Errorf("unicloud api error: received empty token")
|
||||
}
|
||||
|
||||
c.serverlessJwtToken = resp.Data.NewToken.Token
|
||||
c.serverlessJwtTokenExp = time.UnixMilli(resp.Data.NewToken.TokenExpired)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) ensureApiUserTokenExists() error {
|
||||
if err := c.ensureServerlessJwtTokenExists(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.apiUserTokenMtx.Lock()
|
||||
defer c.apiUserTokenMtx.Unlock()
|
||||
if c.apiUserToken != "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
type getUserTokenResponse struct {
|
||||
apiResponseBase
|
||||
Data *struct {
|
||||
Code int32 `json:"code"`
|
||||
Data *struct {
|
||||
Result int32 `json:"ret"`
|
||||
Description string `json:"desc"`
|
||||
Data *struct {
|
||||
Email string `json:"email"`
|
||||
Token string `json:"token"`
|
||||
} `json:"data,omitempty"`
|
||||
} `json:"data,omitempty"`
|
||||
} `json:"data,omitempty"`
|
||||
}
|
||||
|
||||
resp := &getUserTokenResponse{}
|
||||
if err := c.invokeServerlessWithResult(
|
||||
uniConsoleEndpoint, uniConsoleClientSecret, uniConsoleAppId, uniConsoleSpaceId,
|
||||
"uni-cloud-kernel", "", "user/getUserToken", nil, map[string]any{"isLogin": true},
|
||||
resp); err != nil {
|
||||
return err
|
||||
} else if resp.Data == nil || resp.Data.Data == nil || resp.Data.Data.Data == nil || resp.Data.Data.Data.Token == "" {
|
||||
return fmt.Errorf("unicloud api error: received empty user token")
|
||||
}
|
||||
|
||||
c.apiUserToken = resp.Data.Data.Data.Token
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func 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
|
||||
}
|
||||
64
pkg/sdk3rd/dcloud/unicloud/types.go
Normal file
64
pkg/sdk3rd/dcloud/unicloud/types.go
Normal file
@@ -0,0 +1,64 @@
|
||||
package unicloud
|
||||
|
||||
type apiResponse interface {
|
||||
GetSuccess() bool
|
||||
GetErrorCode() string
|
||||
GetErrorMessage() string
|
||||
|
||||
GetReturnCode() int32
|
||||
GetReturnDesc() string
|
||||
}
|
||||
|
||||
type apiResponseBase struct {
|
||||
Success *bool `json:"success,omitempty"`
|
||||
Header *map[string]string `json:"header,omitempty"`
|
||||
Error *struct {
|
||||
Code string `json:"code"`
|
||||
Message string `json:"message"`
|
||||
} `json:"error,omitempty"`
|
||||
|
||||
ReturnCode *int32 `json:"ret,omitempty"`
|
||||
ReturnDesc *string `json:"desc,omitempty"`
|
||||
}
|
||||
|
||||
func (r *apiResponseBase) GetReturnCode() int32 {
|
||||
if r.ReturnCode == nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
return *r.ReturnCode
|
||||
}
|
||||
|
||||
func (r *apiResponseBase) GetReturnDesc() string {
|
||||
if r.ReturnDesc == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
return *r.ReturnDesc
|
||||
}
|
||||
|
||||
func (r *apiResponseBase) GetSuccess() bool {
|
||||
if r.Success == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return *r.Success
|
||||
}
|
||||
|
||||
func (r *apiResponseBase) GetErrorCode() string {
|
||||
if r.Error == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
return r.Error.Code
|
||||
}
|
||||
|
||||
func (r *apiResponseBase) GetErrorMessage() string {
|
||||
if r.Error == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
return r.Error.Message
|
||||
}
|
||||
|
||||
var _ apiResponse = (*apiResponseBase)(nil)
|
||||
Reference in New Issue
Block a user