cloud/api.go
2025-01-14 14:36:09 +08:00

844 lines
25 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package cloud
import (
"bytes"
_ "embed"
"encoding/json"
"errors"
"fmt"
"net/http"
"net/url"
"regexp"
"slices"
"strings"
"sync"
"text/template"
"time"
"apigo.cc/gojs"
"apigo.cc/gojs/goja"
"github.com/gorilla/websocket"
"github.com/ssgo/config"
"github.com/ssgo/httpclient"
"github.com/ssgo/log"
"github.com/ssgo/u"
)
type Action struct {
Desc string
Config map[string]any
Method string
Path string
RequestFormat string
ResponseFormat string
Timeout int
RequestParams []*RequestParam
RequestHeaders []*HeaderParam
Response []*ResponseParam
SuccessBy string
FailedMessageBy string
}
type FixedAction struct {
Action
RequiredRequestParams []*RequestParam
NotRequiredRequestParams []*RequestParam
FuncParams string
}
type RequestParam struct {
Name string
Type string
Required bool
Value any
Desc string
Sub []*RequestParam
}
type HeaderParam struct {
Name string
Value string
Desc string
}
type ResponseParam struct {
Name string
Type string
Desc string
Sub []*ResponseParam
}
type CloudConfig struct {
Desc string
Config map[string]any
RequestFormat string
ResponseFormat string
Timeout int
RequestParams []*RequestParam
RequestHeaders []*HeaderParam
Response []*ResponseParam
Api map[string]*Action
}
// var encryptdMatcher = regexp.MustCompile(`<\*\*([\w-=]+)\*\*>`)
var encryptdMatcher = regexp.MustCompile(`^[\w-=]+$`)
var confAes = u.NewAes([]byte("?GQ$0K0GgLdO=f+~L68PLm$uhKr4'=tV"), []byte("VFs7@sK61cj^f?HZ"))
var keysIsSet = false
func SetSSKey(key, iv []byte) {
if !keysIsSet {
confAes = u.NewAes(key, iv)
keysIsSet = true
}
}
//go:embed export.ts
var exportTS string
var cloudConfigs map[string]*CloudConfig
var fixedCloudConfigs map[string]map[string]*FixedAction
var httpClients = map[string]map[int]*httpclient.ClientPool{}
var httpClientsLock = sync.RWMutex{}
func init() {
gojs.Register("apigo.cc/cloud", gojs.Module{
ObjectMaker: func(vm *goja.Runtime) gojs.Map {
logger := gojs.GetLogger(vm)
makeConfig(logger)
gojsObj := gojs.Map{}
for cloudName, actions := range fixedCloudConfigs {
httpClientsLock.RLock()
hcSet := httpClients[cloudName]
httpClientsLock.RUnlock()
if hcSet == nil {
hcSet = map[int]*httpclient.ClientPool{}
httpClientsLock.Lock()
httpClients[cloudName] = hcSet
httpClientsLock.Unlock()
}
cloudObj := map[string]any{}
for actionName, action := range actions {
httpClientsLock.RLock()
_, hcOK := hcSet[action.Timeout]
httpClientsLock.RUnlock()
if !hcOK {
hc := httpclient.GetClient(time.Duration(action.Timeout) * time.Millisecond)
httpClientsLock.Lock()
hcSet[action.Timeout] = hc
httpClientsLock.Unlock()
}
cloudObj[actionName] = makeAction(cloudName, action)
}
gojsObj[cloudName] = cloudObj
}
return gojsObj
},
TsCodeMaker: func() string {
makeConfig(log.DefaultLogger)
var err error
tsCode := ""
tpl := template.New("export")
tpl = tpl.Funcs(template.FuncMap{
"makeMap": func(args ...any) map[string]any {
out := map[string]any{}
for i := 0; i < len(args); i += 2 {
out[u.String(args[i])] = args[i+1]
}
// fmt.Println(u.BGreen(u.JsonP(out)))
return out
},
})
if tpl, err = tpl.Parse(strings.ReplaceAll(exportTS, "//----", "")); err == nil {
buf := bytes.NewBuffer(make([]byte, 0))
if err = tpl.Execute(buf, fixedCloudConfigs); err == nil {
tsCode = buf.String()
} else {
fmt.Println(u.BRed(err.Error()))
}
} else {
fmt.Println(u.BRed(err.Error()))
}
return tsCode
},
SetSSKey: SetSSKey,
})
}
func getHttpClient(cloudName string, timeout int) *httpclient.ClientPool {
httpClientsLock.RLock()
defer httpClientsLock.RUnlock()
return httpClients[cloudName][timeout]
}
var expressionMatcher = regexp.MustCompile(`(?ms){{.+?}}`)
var funcNameExcludesMatcher = regexp.MustCompile(`[^\w]`)
func makeAction(cloudName string, action *FixedAction) any {
return func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
// 解析参数
requiredCount := len(action.RequiredRequestParams)
args := gojs.MakeArgs(&argsIn, vm).Check(requiredCount)
params := args.Map(requiredCount + 1)
headers := args.Map(requiredCount + 2)
// 合并请求参数
for i, param := range action.RequiredRequestParams {
params[param.Name] = args.Any(i)
}
// 处理默认值
for _, param := range action.RequestParams {
if param.Value != nil && params[param.Name] == nil {
if strValue, ok := param.Value.(string); ok {
if !strings.Contains(strValue, "{{") {
params[param.Name] = param.Value
}
} else {
params[param.Name] = param.Value
}
}
}
for _, header := range action.RequestHeaders {
if header.Value != "" && headers[header.Name] == nil {
if !strings.Contains(header.Value, "{{") {
headers[header.Name] = header.Value
}
}
}
// 生成request信息
requestUrl, err := url.Parse(u.String(action.Config["endpoint"]) + action.Path)
if err != nil {
panic(vm.NewGoError(err))
}
paramsSortJoin := func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
args := gojs.MakeArgs(&argsIn, vm)
joinTag := args.Str(0)
linkTag := args.Str(1)
if joinTag == "" {
joinTag = "&"
}
if linkTag == "" {
linkTag = "="
}
a := make([]string, 0)
for k, v := range params {
a = append(a, k+linkTag+u.String(v))
}
slices.Sort(a)
return vm.ToValue(strings.Join(a, joinTag))
}
if requestUrl.RawPath == "" {
requestUrl.RawPath = requestUrl.Path
}
requestInfo := map[string]any{
"method": action.Method,
"scheme": requestUrl.Scheme,
"host": requestUrl.Host,
"path": requestUrl.Path,
"rawPath": requestUrl.RawPath,
"rawQuery": requestUrl.RawQuery,
"fragment": requestUrl.Fragment,
"rawFragment": requestUrl.RawFragment,
"fullPath": requestUrl.RequestURI(),
"url": requestUrl.String(),
"sortJoin": paramsSortJoin,
}
requestValue := vm.ToValue(requestInfo)
// 计算请求参数中的动态值
configValue := vm.ToValue(action.Config)
actionValue := vm.ToValue(gojs.MakeMap(action))
var paramsValue goja.Value
var headersValue goja.Value
var dataValue goja.Value
headersValue = vm.ToValue(headers)
// 处理基本信息中的动态值
if action.Path != "" && strings.Contains(action.Path, "{{") {
action.Path = expressionMatcher.ReplaceAllStringFunc(action.Path, func(s string) string {
fnCode := s[2 : len(s)-2]
newValue := runFn(fnCode, vm, "action, request, config, params, headers", actionValue, requestValue, configValue, paramsValue, headersValue)
return u.String(newValue)
})
requestUrl, err = url.Parse(u.String(action.Config["endpoint"]) + action.Path)
if err != nil {
panic(vm.NewGoError(err))
}
if requestUrl.RawPath == "" {
requestUrl.RawPath = requestUrl.Path
}
requestInfo = map[string]any{
"method": action.Method,
"scheme": requestUrl.Scheme,
"host": requestUrl.Host,
"path": requestUrl.Path,
"rawPath": requestUrl.RawPath,
"rawQuery": requestUrl.RawQuery,
"fragment": requestUrl.Fragment,
"rawFragment": requestUrl.RawFragment,
"fullPath": requestUrl.RequestURI(),
"url": requestUrl.String(),
"sortJoin": paramsSortJoin,
}
requestValue = vm.ToValue(requestInfo)
}
// 处理请求参数中的动态值
for _, param := range action.RequestParams {
if param.Value != nil && params[param.Name] == nil {
if strValue, ok := param.Value.(string); ok && strings.Contains(strValue, "{{") {
isTest := strings.Contains(param.Name, "TEST__")
if isTest {
fmt.Println("make param:", u.Cyan(param.Name))
}
// 将数据拼接到URL中部分API在请求中需要计算签名
if action.RequestFormat == "query" || action.RequestFormat == "binary" || action.Method == "GET" || action.Method == "WS" {
q := requestUrl.Query()
for k, v := range params {
q.Set(k, u.String(v))
}
requestUrl.RawQuery = q.Encode()
requestInfo["rawQuery"] = requestUrl.RawQuery
requestInfo["fullPath"] = requestUrl.RequestURI()
requestInfo["url"] = requestUrl.String()
requestValue = vm.ToValue(requestInfo)
}
// 计算动态值
var lastRealValue any
replaceTimes := 0
newValue := expressionMatcher.ReplaceAllStringFunc(strValue, func(s string) string {
replaceTimes++
fnCode := s[2 : len(s)-2]
// fnName := "_CLOUD_FN_" + funcNameExcludesMatcher.ReplaceAllString(fnCode, "_")
// fnValue := vm.Get(fnName)
// if fnValue == nil {
// if _, err := vm.RunString("function " + fnName + "(action, request, config, params, headers){return " + fnCode + "}"); err != nil {
// panic(vm.NewGoError(err))
// }
// fnValue = vm.Get(fnName)
// }
// if fn, ok := goja.AssertFunction(fnValue); ok {
// if r, err := fn(nil, actionValue, requestValue, configValue, vm.ToValue(params), vm.ToValue(headers)); err != nil {
// panic(vm.NewGoError(err))
// } else {
// lastRealValue = r.Export()
// if isTest {
// fmt.Println(" >", u.BMagenta(s), u.BCyan(u.JsonP(lastRealValue)), ".")
// }
// return u.String(r.Export())
// }
// } else {
// panic(vm.NewGoError(errors.New("failed to make function " + fnName)))
// }
paramsValue = vm.ToValue(params)
lastRealValue = runFn(fnCode, vm, "action, request, config, params, headers", actionValue, requestValue, configValue, paramsValue, headersValue)
if isTest {
fmt.Println(" >", u.BMagenta(s), u.BCyan(lastRealValue), ".")
}
return u.String(lastRealValue)
})
if replaceTimes == 1 && newValue == u.String(lastRealValue) {
// 如果是单一公式,直接使用最后一次替换真实类型的结果
if !isTest {
params[param.Name] = lastRealValue
}
} else {
// 替换为计算的结果
if !isTest {
params[param.Name] = newValue
} else {
// 打印调试信息
fmt.Println(" >", u.Cyan(u.JsonP(newValue)), ".")
fmt.Println()
}
}
}
}
}
paramsValue = vm.ToValue(params)
// 根据RequestFormat处理请求数据
if action.RequestFormat == "query" || action.RequestFormat == "binary" || action.Method == "GET" || action.Method == "WS" {
// 将数据拼接到URL中
q := requestUrl.Query()
for k, v := range params {
q.Set(k, u.String(v))
}
requestUrl.RawQuery = q.Encode()
requestInfo["rawQuery"] = requestUrl.RawQuery
requestInfo["fullPath"] = requestUrl.RequestURI()
requestInfo["url"] = requestUrl.String()
requestValue = vm.ToValue(requestInfo)
}
var data any
switch action.RequestFormat {
case "query":
// 将数据拼接到URL中
case "binary":
// 将数据以二进制形式POST参数拼接到URL中
args.Check(requiredCount + 1)
data = args.Bytes(requiredCount)
case "form", "upload":
// 将数据以表单类型提交
args.Check(requiredCount + 1)
form := make(map[string]string, 0)
for k, v := range params {
form[k] = u.String(v)
}
data = form
case "json":
// 将数据以JSON格式提交
data = u.Json(params)
default:
if action.Method == "GET" {
// 将数据拼接到URL中
} else {
// 将数据以JSON格式提交
data = params
}
}
// data = `{"EngSerViceType":"8k_zh","SourceType":1,"VoiceFormat":"mp3","Data":"AA","DataLen":0,"WordInfo":0,"FilterDirty":0,"FilterModal":0,"FilterPunc":0,"ConvertNumMode":1,"CustomizationId":"","InputSampleRate":0}`
dataValue = vm.ToValue(data)
// 计算请求头中的动态值
for _, header := range action.RequestHeaders {
if header.Value != "" && headers[header.Name] == nil {
if strings.Contains(header.Value, "{{") {
isTest := strings.Contains(header.Name, "TEST__")
if isTest {
fmt.Println("make header:", u.Cyan(header.Name))
}
var lastRealValue any
replaceTimes := 0
newValue := expressionMatcher.ReplaceAllStringFunc(header.Value, func(s string) string {
replaceTimes++
fnCode := s[2 : len(s)-2]
// fnName := "_CLOUD_FN_" + funcNameExcludesMatcher.ReplaceAllString(fnCode, "_")
// fnValue := vm.Get(fnName)
// if fnValue == nil {
// if _, err := vm.RunString("function " + fnName + "(action, request, config, params, headers, data){return " + fnCode + "}"); err != nil {
// panic(vm.NewGoError(err))
// }
// fnValue = vm.Get(fnName)
// }
// if fn, ok := goja.AssertFunction(fnValue); ok {
// if r, err := fn(nil, actionValue, requestValue, configValue, vm.ToValue(params), vm.ToValue(headers), vm.ToValue(data)); err != nil {
// panic(vm.NewGoError(err))
// } else {
// lastRealValue = r.Export()
// if isTest {
// fmt.Println(" >", u.BMagenta(s), u.BCyan(u.JsonP(lastRealValue)), ".")
// }
// return u.String(lastRealValue)
// }
// } else {
// panic(vm.NewGoError(errors.New("failed to make function " + fnName)))
// }
headersValue = vm.ToValue(headers)
lastRealValue = runFn(fnCode, vm, "action, request, config, params, headers, data", actionValue, requestValue, configValue, paramsValue, headersValue, dataValue)
if isTest {
fmt.Println(" >", u.BMagenta(s), u.BCyan(lastRealValue), ".")
}
// if header.Name == "TEST__待签名" {
// fmt.Println(u.BGreen(u.Json(lastRealValue)), ".")
// fmt.Println(u.BGreen(hex.EncodeToString(u.Sha256([]byte(u.String(lastRealValue))))))
// }
return u.String(lastRealValue)
})
if replaceTimes == 1 && newValue == u.String(lastRealValue) {
// 如果是单一公式,直接使用最后一次替换真实类型的结果
if !isTest {
headers[header.Name] = lastRealValue
}
} else {
// 替换为计算的结果
if !isTest {
headers[header.Name] = newValue
} else {
// 打印调试信息
fmt.Println(" >", u.Cyan(u.JsonP(newValue)), ".")
fmt.Println()
}
}
}
}
}
// TODO 支持SSE
// TODO 支持Download异步回调
// 处理请求头
headersArr := make([]string, 0)
for k, v := range headers {
headersArr = append(headersArr, k, u.String(v))
}
// 发送请求
hc := getHttpClient(cloudName, action.Timeout)
var httpResult *httpclient.Result
var otherResult any
t1 := time.Now().UnixMilli()
if action.RequestFormat == "upload" {
// 处理上传文件
files := args.Map(requiredCount)
httpResult, _ = hc.MPost(requestUrl.String(), data.(map[string]string), files, headersArr...)
} else if action.Method == "WS" {
// 处理WS请求
args.Check(requiredCount + 1)
callback := args.Func(requiredCount)
reqHeader := http.Header{}
for k, v := range headers {
reqHeader.Set(k, u.String(v))
}
if conn, resp, err := websocket.DefaultDialer.Dial(strings.Replace(requestUrl.String(), "http", "ws", 1), reqHeader); err == nil {
for {
if conn != nil {
var data any
var msgType string
if typ, buf, err := conn.ReadMessage(); err == nil {
switch typ {
case websocket.TextMessage:
msgType = "text"
if action.ResponseFormat == "json" {
obj := map[string]any{}
if err := json.Unmarshal(buf, &obj); err == nil {
msgType = "json"
data = obj
} else {
data = string(buf)
}
}
case websocket.BinaryMessage:
msgType = "binary"
data = buf
default:
msgType = "unknown"
data = string(buf)
}
} else {
break
}
if msgType == action.ResponseFormat {
// 最后一个类型相同的消息作为输出
otherResult = data
}
if callback != nil {
if r, err := callback(nil, vm.ToValue(data), vm.ToValue(msgType)); err != nil {
conn.Close()
panic(vm.NewGoError(err))
} else if u.Bool(r.Export()) {
fmt.Println(222, r.Export())
conn.Close()
break
} else {
fmt.Println(333, r.Export())
}
}
} else {
break
}
}
httpResult = &httpclient.Result{
Response: resp,
Error: err,
}
} else {
panic(vm.NewGoError(err))
}
} else {
// fmt.Println("request", action.Method, requestUrl.String(), u.Json(headersArr), "...")
httpResult = hc.Do(action.Method, requestUrl.String(), data, headersArr...)
// fmt.Println("response", result.Response.StatusCode, result.Response.Status, result.String(), "...")
}
if httpResult.Error != nil {
panic(vm.NewGoError(httpResult.Error))
}
usedTime := time.Now().UnixMilli() - t1
outHeaders := map[string]string{}
for k, v := range httpResult.Response.Header {
outHeaders[k] = v[0]
}
gojsResult := gojs.Map{
"statusCode": httpResult.Response.StatusCode,
"status": httpResult.Response.Status,
"headers": outHeaders,
"usedTime": usedTime,
}
var result any
if action.Method == "WS" {
result = otherResult
} else {
switch action.ResponseFormat {
case "json":
result = httpResult.Map()
case "binary":
gojsResult["result"] = httpResult.Bytes()
default:
gojsResult["result"] = httpResult.String()
}
}
var responseValue goja.Value
var resultValue goja.Value
if action.SuccessBy != "" || action.FailedMessageBy != "" {
responseValue = vm.ToValue(gojsResult)
resultValue = vm.ToValue(result)
}
if action.SuccessBy != "" {
fnResult := runFn(action.SuccessBy, vm, "action, request, config, params, headers, data, response, result", actionValue, requestValue, configValue, paramsValue, headersValue, dataValue, responseValue, resultValue)
gojsResult["success"] = u.Bool(fnResult)
} else {
gojsResult["success"] = httpResult.Response.StatusCode == 200
}
if gojsResult["success"] == true {
gojsResult["failedMessage"] = ""
} else {
if action.FailedMessageBy != "" {
fnResult := runFn(action.FailedMessageBy, vm, "action, request, config, params, headers, data, response, result", actionValue, requestValue, configValue, paramsValue, headersValue, dataValue, responseValue, resultValue)
gojsResult["failedMessage"] = u.String(fnResult)
} else {
gojsResult["failedMessage"] = httpResult.Response.Status
}
}
gojsResult["result"] = result
return vm.ToValue(gojsResult)
}
}
func runFn(fnCode string, vm *goja.Runtime, argsParams string, args ...goja.Value) any {
fnName := "_CLOUD_FN_" + funcNameExcludesMatcher.ReplaceAllString(fnCode, "_")
fnValue := vm.Get(fnName)
if fnValue == nil {
if _, err := vm.RunString("function " + fnName + "(" + argsParams + "){return " + fnCode + "}"); err != nil {
panic(vm.NewGoError(err))
}
fnValue = vm.Get(fnName)
}
if fn, ok := goja.AssertFunction(fnValue); ok {
if r, err := fn(nil, args...); err != nil {
panic(vm.NewGoError(err))
} else {
return r.Export()
}
} else {
panic(vm.NewGoError(errors.New("failed to make function " + fnName)))
}
}
func makeConfig(logger *log.Logger) {
if cloudConfigs != nil {
return
}
// 读取所有的配置文件
cloudConfigs = map[string]*CloudConfig{}
fixedCloudConfigs = map[string]map[string]*FixedAction{}
for _, f := range u.ReadDirN("api") {
if strings.HasSuffix(f.Name, ".yml") {
apiName := f.Name[:len(f.Name)-4]
cloudConf := CloudConfig{}
if err := u.LoadX(f.FullName, &cloudConf); err != nil {
logger.Error(err.Error())
continue
}
cloudConfigs[apiName] = &cloudConf
}
}
// 载入补充的配置
config.LoadConfig("cloud", &cloudConfigs)
// 根据配置生成gojs对象
for cloudName, cloudConf := range cloudConfigs {
// 解密 & 合并配置
if cloudConf.Config == nil {
cloudConf.Config = map[string]any{}
}
decryptConfig(cloudConf.Config)
fixedActions := map[string]*FixedAction{}
for actionName, apiConf := range cloudConf.Api {
// var newConf map[string]any
// if v, ok := cloudConf.ApiConfig[actionName]; ok && v != nil {
// newConf = *v
// }
// if newConf == nil {
// newConf = map[string]any{}
// }
// decryptConfig(newConf)
// for k, v := range cloudConf.Config {
// if _, ok := newConf[k]; !ok {
// newConf[k] = v
// }
// }
decryptConfig(apiConf.Config)
for k, v := range cloudConf.Config {
if _, ok := apiConf.Config[k]; !ok {
apiConf.Config[k] = v
}
}
fixedActions[actionName] = &FixedAction{
Action: *apiConf,
// Config: newConf,
RequiredRequestParams: make([]*RequestParam, 0),
NotRequiredRequestParams: make([]*RequestParam, 0),
}
}
for actionName, apiConf := range fixedActions {
// 合并请求参数
existsRequestParams := map[string]bool{}
for _, param := range apiConf.RequestParams {
existsRequestParams[param.Name] = true
}
for _, param := range cloudConf.RequestParams {
if !existsRequestParams[param.Name] {
existsRequestParams[param.Name] = true
apiConf.RequestParams = append(apiConf.RequestParams, param)
}
}
for _, param := range apiConf.RequestParams {
if param.Value == nil {
if param.Required {
apiConf.RequiredRequestParams = append(apiConf.RequiredRequestParams, param)
} else {
apiConf.NotRequiredRequestParams = append(apiConf.NotRequiredRequestParams, param)
}
} else {
apiConf.NotRequiredRequestParams = append(apiConf.NotRequiredRequestParams, param)
}
}
fixedActions[actionName].RequestParams = apiConf.RequestParams
fixedActions[actionName].RequiredRequestParams = apiConf.RequiredRequestParams
fixedActions[actionName].NotRequiredRequestParams = apiConf.NotRequiredRequestParams
// 合并请求头
if apiConf.RequestHeaders == nil {
apiConf.RequestHeaders = make([]*HeaderParam, 0)
}
existsRequestHeaders := map[string]bool{}
for _, header := range apiConf.RequestHeaders {
existsRequestHeaders[header.Name] = true
}
for _, header := range cloudConf.RequestHeaders {
if !existsRequestHeaders[header.Name] {
existsRequestHeaders[header.Name] = true
apiConf.RequestHeaders = append(apiConf.RequestHeaders, header)
}
}
fixedActions[actionName].RequestHeaders = apiConf.RequestHeaders
// 合并响应参数
if apiConf.Response == nil {
apiConf.Response = make([]*ResponseParam, 0)
}
existsResponse := map[string]bool{}
for _, resp := range apiConf.Response {
existsResponse[resp.Name] = true
}
for _, resp := range cloudConf.Response {
if !existsResponse[resp.Name] {
existsResponse[resp.Name] = true
apiConf.Response = append(apiConf.Response, resp)
}
}
fixedActions[actionName].Response = apiConf.Response
// 处理Sub类型
makeRequestType(cloudName, actionName, fixedActions[actionName].RequestParams)
makeResponseType(cloudName, actionName, fixedActions[actionName].Response)
// 合并其他
apiConf.Method = strings.ToUpper(apiConf.Method)
if apiConf.Method == "" {
apiConf.Method = "POST"
}
apiConf.RequestFormat = strings.ToLower(apiConf.RequestFormat)
if apiConf.RequestFormat == "" {
apiConf.RequestFormat = cloudConf.RequestFormat
}
apiConf.ResponseFormat = strings.ToLower(apiConf.ResponseFormat)
if apiConf.ResponseFormat == "" {
apiConf.ResponseFormat = cloudConf.ResponseFormat
}
if apiConf.Timeout == 0 {
apiConf.Timeout = cloudConf.Timeout
}
// 生成函数参数
funcParams := make([]string, 0)
for _, param := range apiConf.RequiredRequestParams {
if len(param.Sub) > 0 {
funcParams = append(funcParams, fmt.Sprintf("%s:%s", param.Name, param.Type))
} else {
funcParams = append(funcParams, fmt.Sprintf("%s:%s", param.Name, param.Type))
}
}
if apiConf.RequestFormat == "binary" {
funcParams = append(funcParams, "data:any")
}
if apiConf.Method == "WS" {
funcParams = append(funcParams, "callback:(data:any,type:string)=>boolean")
}
if len(apiConf.NotRequiredRequestParams) > 0 {
funcParams = append(funcParams, fmt.Sprintf("options?:%s_%sParams", cloudName, actionName))
}
if len(apiConf.RequestHeaders) > 0 {
funcParams = append(funcParams, fmt.Sprintf("headers?:%s_%sHeaders", cloudName, actionName))
}
apiConf.FuncParams = strings.Join(funcParams, ", ")
fixedActions[actionName].FuncParams = apiConf.FuncParams
}
fixedCloudConfigs[cloudName] = fixedActions
}
}
func makeRequestType(cloudName, actionName string, params []*RequestParam) {
for i, param := range params {
if len(param.Sub) > 0 {
subType := fmt.Sprintf("%s_%sParams%s", cloudName, actionName, param.Name)
if param.Type == "Array" {
params[i].Type = fmt.Sprintf("Array<%s>", subType)
} else {
params[i].Type = subType
}
makeRequestType(cloudName, actionName, param.Sub)
}
}
}
func makeResponseType(cloudName, actionName string, params []*ResponseParam) {
for i, param := range params {
if len(param.Sub) > 0 {
subType := fmt.Sprintf("%s_%sResult%s", cloudName, actionName, param.Name)
if param.Type == "Array" {
params[i].Type = fmt.Sprintf("Array<%s>", subType)
} else {
params[i].Type = subType
}
makeResponseType(cloudName, actionName, param.Sub)
}
}
}
func decryptConfig(conf map[string]any) {
for k, v := range conf {
if vStr, ok := v.(string); ok && encryptdMatcher.MatchString(vStr) {
conf[k] = confAes.DecryptUrlBase64ToString(vStr)
}
}
}