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 )
}
}
}