cloud/api.go

844 lines
25 KiB
Go
Raw Permalink Normal View History

2024-11-10 21:34:26 +08:00
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 {
2025-01-14 14:36:09 +08:00
httpClientsLock.RLock()
hcSet := httpClients[cloudName]
httpClientsLock.RUnlock()
if hcSet == nil {
hcSet = map[int]*httpclient.ClientPool{}
httpClientsLock.Lock()
httpClients[cloudName] = hcSet
httpClientsLock.Unlock()
}
2024-11-10 21:34:26 +08:00
cloudObj := map[string]any{}
for actionName, action := range actions {
2025-01-14 14:36:09 +08:00
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()
2024-11-10 21:34:26 +08:00
}
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)
}
}
}