2025-09-11 23:42:22 +08:00
|
|
|
|
package plugin
|
|
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
|
"encoding/base64"
|
|
|
|
|
|
"reflect"
|
|
|
|
|
|
"regexp"
|
|
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
|
|
|
|
"apigo.cc/gojs"
|
|
|
|
|
|
"apigo.cc/gojs/goja"
|
|
|
|
|
|
"github.com/ssgo/config"
|
|
|
|
|
|
"github.com/ssgo/u"
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
const pluginName = "api"
|
|
|
|
|
|
|
|
|
|
|
|
var confAes *u.Aes = u.NewAes([]byte("?GQ$0K0GgLdO=f+~L68PLm$uhKr4'=tV"), []byte("VFs7@sK61cj^f?HZ"))
|
|
|
|
|
|
|
|
|
|
|
|
type Result struct {
|
|
|
|
|
|
StatusCode int
|
|
|
|
|
|
Status string
|
|
|
|
|
|
Headers map[string]string
|
|
|
|
|
|
Data any
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func init() {
|
2025-12-01 00:30:00 +08:00
|
|
|
|
config.LoadConfig(pluginName, &apiConfigs)
|
|
|
|
|
|
for k, v := range apiConfigs {
|
|
|
|
|
|
Config(k, *v, nil)
|
|
|
|
|
|
}
|
2025-09-11 23:42:22 +08:00
|
|
|
|
|
|
|
|
|
|
obj := map[string]any{
|
2025-12-01 00:30:00 +08:00
|
|
|
|
"config": Config,
|
|
|
|
|
|
"getConfigVersion": GetConfigVersion,
|
|
|
|
|
|
"makeApi": MakeApi,
|
|
|
|
|
|
"registerSigner": RegisterSigner,
|
|
|
|
|
|
"getSignerVersion": GetSignerVersion,
|
|
|
|
|
|
"sortParams": SortParams,
|
2025-09-11 23:42:22 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-01 00:30:00 +08:00
|
|
|
|
// TODO 动态更新配置时无法重新生成obj,预处理配置生成Action,增加通用的Call方法
|
|
|
|
|
|
for apiName := range apiConfigs {
|
|
|
|
|
|
// cfg := signerConfig[signer]
|
|
|
|
|
|
// o := map[string]any{}
|
|
|
|
|
|
// o["do"] = MakeAction(signer, nil)
|
|
|
|
|
|
// o["get"] = MakeAction(signer, map[string]any{
|
|
|
|
|
|
// "method": "GET",
|
|
|
|
|
|
// })
|
|
|
|
|
|
// o["post"] = MakeAction(signer, map[string]any{
|
|
|
|
|
|
// "method": "POST",
|
|
|
|
|
|
// })
|
|
|
|
|
|
// o["put"] = MakeAction(signer, map[string]any{
|
|
|
|
|
|
// "method": "PUT",
|
|
|
|
|
|
// })
|
|
|
|
|
|
// o["delete"] = MakeAction(signer, map[string]any{
|
|
|
|
|
|
// "method": "DELETE",
|
|
|
|
|
|
// })
|
|
|
|
|
|
// o["head"] = MakeAction(signer, map[string]any{
|
|
|
|
|
|
// "method": "HEAD",
|
|
|
|
|
|
// })
|
|
|
|
|
|
// o["options"] = MakeAction(signer, map[string]any{
|
|
|
|
|
|
// "method": "OPTIONS",
|
|
|
|
|
|
// })
|
|
|
|
|
|
// o["version"] = (*cfg)["_version"]
|
2025-09-11 23:42:22 +08:00
|
|
|
|
|
2025-12-01 00:30:00 +08:00
|
|
|
|
// actions := map[string]map[string]any{}
|
|
|
|
|
|
// u.Convert((*cfg)["actions"], &actions)
|
|
|
|
|
|
// for k, v := range actions {
|
|
|
|
|
|
// o[k] = MakeAction(signer, v)
|
|
|
|
|
|
// }
|
|
|
|
|
|
obj[apiName] = MakeApi(apiName)
|
2025-09-11 23:42:22 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
tsCode := gojs.MakeTSCode(obj)
|
|
|
|
|
|
mappedObj := gojs.ToMap(obj)
|
|
|
|
|
|
gojs.Register("apigo.cc/gojs/"+pluginName, gojs.Module{
|
|
|
|
|
|
ObjectMaker: func(vm *goja.Runtime) gojs.Map {
|
|
|
|
|
|
return mappedObj
|
|
|
|
|
|
},
|
|
|
|
|
|
TsCode: tsCode,
|
|
|
|
|
|
Desc: pluginName,
|
2025-12-01 00:30:00 +08:00
|
|
|
|
JsCode: `
|
|
|
|
|
|
let $MOD$_obj = $MOD$
|
|
|
|
|
|
$MOD$ = new Proxy($MOD$_obj, {
|
|
|
|
|
|
get(target, prop) {
|
|
|
|
|
|
if(['config', 'getConfigVersion', 'registerSigner', 'getSignerVersion', 'sortParams'].indexOf(prop) >= 0) {
|
|
|
|
|
|
return target[prop]
|
|
|
|
|
|
}
|
|
|
|
|
|
let o = target[prop]
|
|
|
|
|
|
let curVer = o && o.version || 0
|
|
|
|
|
|
if(curVer == 0 || curVer != $MOD$_obj.getConfigVersion(prop)){
|
|
|
|
|
|
o = $MOD$_obj.makeApi(prop)
|
|
|
|
|
|
target[prop] = o
|
|
|
|
|
|
}
|
|
|
|
|
|
return o
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
`,
|
2025-09-11 23:42:22 +08:00
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-01 00:30:00 +08:00
|
|
|
|
func MakeApi(apiName string) map[string]any {
|
|
|
|
|
|
cfg := apiConfigs[apiName]
|
|
|
|
|
|
o := map[string]any{}
|
|
|
|
|
|
o["do"] = MakeAction(apiName, "", nil)
|
|
|
|
|
|
o["get"] = MakeAction(apiName, "get", map[string]any{
|
|
|
|
|
|
"method": "GET",
|
|
|
|
|
|
})
|
|
|
|
|
|
o["post"] = MakeAction(apiName, "post", map[string]any{
|
|
|
|
|
|
"method": "POST",
|
|
|
|
|
|
})
|
|
|
|
|
|
o["put"] = MakeAction(apiName, "put", map[string]any{
|
|
|
|
|
|
"method": "PUT",
|
|
|
|
|
|
})
|
|
|
|
|
|
o["delete"] = MakeAction(apiName, "delete", map[string]any{
|
|
|
|
|
|
"method": "DELETE",
|
|
|
|
|
|
})
|
|
|
|
|
|
o["head"] = MakeAction(apiName, "head", map[string]any{
|
|
|
|
|
|
"method": "HEAD",
|
|
|
|
|
|
})
|
|
|
|
|
|
o["options"] = MakeAction(apiName, "options", map[string]any{
|
|
|
|
|
|
"method": "OPTIONS",
|
|
|
|
|
|
})
|
|
|
|
|
|
o["version"] = (*cfg)["_version"]
|
|
|
|
|
|
|
|
|
|
|
|
actions := map[string]map[string]any{}
|
|
|
|
|
|
u.Convert((*cfg)["actions"], &actions)
|
|
|
|
|
|
for k, v := range actions {
|
|
|
|
|
|
o[k] = MakeAction(apiName, k, v)
|
|
|
|
|
|
}
|
|
|
|
|
|
return o
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func MakeAction(apiName, actionName string, actionCfg map[string]any) func(req *Request, vm *goja.Runtime) (*Result, error) {
|
|
|
|
|
|
cfg := &ApiConfig{data: map[string]any{}}
|
|
|
|
|
|
if c := apiConfigs[apiName]; c != nil {
|
2025-09-11 23:42:22 +08:00
|
|
|
|
u.Convert(c, cfg.data)
|
|
|
|
|
|
delete(cfg.data, "actions")
|
|
|
|
|
|
}
|
|
|
|
|
|
defaultUrl := cfg.String("url", "")
|
|
|
|
|
|
|
|
|
|
|
|
reqSet := &Request{}
|
|
|
|
|
|
u.Convert(actionCfg, reqSet)
|
|
|
|
|
|
|
|
|
|
|
|
delete(actionCfg, "headers")
|
|
|
|
|
|
delete(actionCfg, "query")
|
|
|
|
|
|
delete(actionCfg, "data")
|
|
|
|
|
|
delete(actionCfg, "form")
|
|
|
|
|
|
delete(actionCfg, "file")
|
|
|
|
|
|
delete(actionCfg, "text")
|
|
|
|
|
|
delete(actionCfg, "binary")
|
|
|
|
|
|
delete(actionCfg, "requestType")
|
|
|
|
|
|
delete(actionCfg, "callback")
|
|
|
|
|
|
delete(actionCfg, "timeout")
|
|
|
|
|
|
u.Convert(actionCfg, cfg.data)
|
|
|
|
|
|
|
|
|
|
|
|
makeConfigVar(cfg.data, reflect.ValueOf(cfg.data))
|
|
|
|
|
|
// 没有在外层配置url,或者url中有待处理的变量
|
|
|
|
|
|
if defaultUrl == "" || strings.Contains(defaultUrl, "{{.") {
|
|
|
|
|
|
defaultUrl = cfg.String("url", "")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-01 00:30:00 +08:00
|
|
|
|
actionFullName := apiName
|
|
|
|
|
|
if actionName != "" {
|
|
|
|
|
|
actionFullName += "." + actionName
|
|
|
|
|
|
}
|
|
|
|
|
|
actionsLock.Lock()
|
|
|
|
|
|
actionConfigs[actionFullName] = cfg
|
|
|
|
|
|
actionsLock.Unlock()
|
|
|
|
|
|
|
|
|
|
|
|
return func(req *Request, vm *goja.Runtime) (*Result, error) {
|
2025-09-11 23:42:22 +08:00
|
|
|
|
if req == nil {
|
|
|
|
|
|
req = &Request{}
|
|
|
|
|
|
}
|
|
|
|
|
|
var req1 *Request
|
|
|
|
|
|
if reqSet != nil {
|
|
|
|
|
|
req1v := *reqSet
|
|
|
|
|
|
req1 = &req1v
|
|
|
|
|
|
if req.Url != "" {
|
|
|
|
|
|
req1.Url = req.Url
|
|
|
|
|
|
}
|
|
|
|
|
|
if req.Method != "" {
|
|
|
|
|
|
req1.Method = req.Method
|
|
|
|
|
|
}
|
|
|
|
|
|
if req.Config != nil {
|
|
|
|
|
|
if req1.Config == nil {
|
|
|
|
|
|
req1.Config = map[string]string{}
|
|
|
|
|
|
}
|
|
|
|
|
|
for k, v := range req.Config {
|
|
|
|
|
|
req1.Config[k] = v
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if req.Headers != nil {
|
|
|
|
|
|
if req1.Headers == nil {
|
|
|
|
|
|
req1.Headers = map[string]string{}
|
|
|
|
|
|
}
|
|
|
|
|
|
for k, v := range req.Headers {
|
|
|
|
|
|
req1.Headers[k] = v
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if req.Query != nil {
|
|
|
|
|
|
if req1.Query == nil {
|
|
|
|
|
|
req1.Query = map[string]string{}
|
|
|
|
|
|
}
|
|
|
|
|
|
for k, v := range req.Query {
|
|
|
|
|
|
req1.Query[k] = v
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if req.Data != nil {
|
|
|
|
|
|
if req1.Data == nil {
|
|
|
|
|
|
req1.Data = map[string]any{}
|
|
|
|
|
|
}
|
|
|
|
|
|
for k, v := range req.Data {
|
|
|
|
|
|
req1.Data[k] = v
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if req.Form != nil {
|
|
|
|
|
|
if req1.Form == nil {
|
|
|
|
|
|
req1.Form = map[string]string{}
|
|
|
|
|
|
}
|
|
|
|
|
|
for k, v := range req.Form {
|
|
|
|
|
|
req1.Form[k] = v
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if req.File != nil {
|
|
|
|
|
|
if req1.File == nil {
|
|
|
|
|
|
req1.File = map[string]any{}
|
|
|
|
|
|
}
|
|
|
|
|
|
for k, v := range req.File {
|
|
|
|
|
|
req1.File[k] = v
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if req.Text != "" {
|
|
|
|
|
|
req1.Text = req.Text
|
|
|
|
|
|
}
|
|
|
|
|
|
if req.Binary != nil {
|
|
|
|
|
|
req1.Binary = req.Binary
|
|
|
|
|
|
}
|
|
|
|
|
|
if req.RequestType != "" {
|
|
|
|
|
|
req1.RequestType = req.RequestType
|
|
|
|
|
|
}
|
|
|
|
|
|
if req.ResponseType != "" {
|
|
|
|
|
|
req1.ResponseType = req.ResponseType
|
|
|
|
|
|
}
|
|
|
|
|
|
if req.Callback != nil {
|
|
|
|
|
|
req1.Callback = req.Callback
|
|
|
|
|
|
}
|
|
|
|
|
|
if req.Timeout == 0 {
|
|
|
|
|
|
req1.Timeout = req.Timeout
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
req1 = req
|
|
|
|
|
|
}
|
|
|
|
|
|
if !strings.Contains(req1.Url, "://") && defaultUrl != "" {
|
|
|
|
|
|
has1 := strings.HasSuffix(defaultUrl, "/")
|
|
|
|
|
|
has2 := strings.HasPrefix(req1.Url, "/")
|
|
|
|
|
|
if !has1 && !has2 {
|
|
|
|
|
|
req1.Url = defaultUrl + "/" + req1.Url
|
|
|
|
|
|
} else if has1 && has2 {
|
|
|
|
|
|
req1.Url = defaultUrl + req1.Url[1:]
|
|
|
|
|
|
} else {
|
|
|
|
|
|
req1.Url = defaultUrl + req1.Url
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-01 00:30:00 +08:00
|
|
|
|
data, resp, err := Do(actionFullName, req1, vm)
|
2025-09-11 23:42:22 +08:00
|
|
|
|
headers := map[string]string{}
|
|
|
|
|
|
statusCode := 0
|
|
|
|
|
|
status := ""
|
|
|
|
|
|
if resp != nil {
|
|
|
|
|
|
statusCode = resp.StatusCode
|
|
|
|
|
|
status = resp.Status
|
|
|
|
|
|
for k, v := range resp.Header {
|
|
|
|
|
|
headers[k] = v[0]
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return &Result{
|
|
|
|
|
|
Status: status,
|
|
|
|
|
|
StatusCode: statusCode,
|
|
|
|
|
|
Headers: headers,
|
|
|
|
|
|
Data: data,
|
|
|
|
|
|
}, err
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-01 00:30:00 +08:00
|
|
|
|
// func Config(cfg map[string]*map[string]any) {
|
|
|
|
|
|
// for k, v := range cfg {
|
|
|
|
|
|
// if u.String((*v)["signer"]) == "" {
|
|
|
|
|
|
// (*v)["signer"] = k
|
|
|
|
|
|
// }
|
|
|
|
|
|
// // 尝试解密配置
|
|
|
|
|
|
// decryptConfig(reflect.ValueOf(v))
|
|
|
|
|
|
// makeConfigVar((*v), reflect.ValueOf(v))
|
|
|
|
|
|
|
|
|
|
|
|
// confLock.Lock()
|
|
|
|
|
|
// signerConfig[k] = v
|
|
|
|
|
|
// confLock.Unlock()
|
|
|
|
|
|
// }
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
func Config(name string, cfg map[string]any, version *uint64) {
|
|
|
|
|
|
ver := u.Uint64(version)
|
|
|
|
|
|
if ver == 0 {
|
|
|
|
|
|
ver = 1
|
|
|
|
|
|
}
|
|
|
|
|
|
cfg["_version"] = ver
|
|
|
|
|
|
|
|
|
|
|
|
// 尝试解密配置
|
|
|
|
|
|
cfgV := reflect.ValueOf(cfg)
|
|
|
|
|
|
decryptConfig(cfgV)
|
|
|
|
|
|
makeConfigVar(cfg, cfgV)
|
|
|
|
|
|
|
|
|
|
|
|
confLock.Lock()
|
|
|
|
|
|
apiConfigs[name] = &cfg
|
|
|
|
|
|
confLock.Unlock()
|
|
|
|
|
|
}
|
2025-09-11 23:42:22 +08:00
|
|
|
|
|
2025-12-01 00:30:00 +08:00
|
|
|
|
func GetConfigVersion(name string) uint64 {
|
|
|
|
|
|
confLock.RLock()
|
|
|
|
|
|
defer confLock.RUnlock()
|
|
|
|
|
|
cfg := apiConfigs[name]
|
|
|
|
|
|
if cfg != nil {
|
|
|
|
|
|
return u.Uint64((*cfg)["_version"])
|
2025-09-11 23:42:22 +08:00
|
|
|
|
}
|
2025-12-01 00:30:00 +08:00
|
|
|
|
return 0
|
2025-09-11 23:42:22 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func decryptConfig(v reflect.Value) {
|
|
|
|
|
|
v = u.FinalValue(v)
|
|
|
|
|
|
if v.Kind() == reflect.Map {
|
|
|
|
|
|
for _, k := range v.MapKeys() {
|
|
|
|
|
|
v2 := u.FinalValue(v.MapIndex(k))
|
|
|
|
|
|
if v2.Kind() == reflect.String {
|
|
|
|
|
|
if b64, err := base64.URLEncoding.DecodeString(v2.String()); err == nil {
|
|
|
|
|
|
if dec, err := confAes.DecryptBytes(b64); err == nil {
|
|
|
|
|
|
v.SetMapIndex(k, reflect.ValueOf(string(dec)))
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
} else if v2.Kind() == reflect.Map || v.Kind() == reflect.Slice {
|
|
|
|
|
|
decryptConfig(v2)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
} else if v.Kind() == reflect.Slice {
|
|
|
|
|
|
for i := 0; i < v.Len(); i++ {
|
|
|
|
|
|
v2 := u.FinalValue(v.Index(i))
|
|
|
|
|
|
if v2.Kind() == reflect.String {
|
|
|
|
|
|
if b64, err := base64.URLEncoding.DecodeString(v2.String()); err == nil {
|
|
|
|
|
|
if dec, err := confAes.DecryptBytes(b64); err == nil {
|
|
|
|
|
|
v.Index(i).Set(reflect.ValueOf(string(dec)))
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
} else if v2.Kind() == reflect.Map || v.Kind() == reflect.Slice {
|
|
|
|
|
|
decryptConfig(v2)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var varMatcher = regexp.MustCompile(`{{\.([\w\.\-]+)}}`)
|
|
|
|
|
|
var configMatcher = regexp.MustCompile(`{{config\.([\w\.\-]+)}}`)
|
|
|
|
|
|
var fnMatcher = regexp.MustCompile(`{{/(\w+)\s*(.*?)}}`)
|
|
|
|
|
|
|
|
|
|
|
|
func makeConfigVar(cfg map[string]any, v reflect.Value) {
|
|
|
|
|
|
v = u.FinalValue(v)
|
|
|
|
|
|
if v.Kind() == reflect.Map {
|
|
|
|
|
|
for _, k := range v.MapKeys() {
|
|
|
|
|
|
v2 := u.FinalValue(v.MapIndex(k))
|
|
|
|
|
|
if v2.Kind() == reflect.String {
|
|
|
|
|
|
str := v2.String()
|
|
|
|
|
|
if m := varMatcher.FindAllStringSubmatch(str, 100); m != nil {
|
|
|
|
|
|
for _, m1 := range m {
|
|
|
|
|
|
if v1 := getConfigValue(cfg, m1[1]); v1 != "" {
|
|
|
|
|
|
str = strings.ReplaceAll(str, m1[0], v1)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
v.SetMapIndex(k, reflect.ValueOf(str))
|
|
|
|
|
|
}
|
|
|
|
|
|
} else if v2.Kind() == reflect.Map || v.Kind() == reflect.Slice {
|
|
|
|
|
|
makeConfigVar(cfg, v2)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
} else if v.Kind() == reflect.Slice {
|
|
|
|
|
|
for i := 0; i < v.Len(); i++ {
|
|
|
|
|
|
v2 := u.FinalValue(v.Index(i))
|
|
|
|
|
|
if v2.Kind() == reflect.String {
|
|
|
|
|
|
str := v2.String()
|
|
|
|
|
|
if m := varMatcher.FindAllStringSubmatch(str, 100); m != nil {
|
|
|
|
|
|
for _, m1 := range m {
|
|
|
|
|
|
if v1 := getConfigValue(cfg, m1[1]); v1 != "" {
|
|
|
|
|
|
str = strings.ReplaceAll(str, m1[0], v1)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
v.Index(i).Set(reflect.ValueOf(str))
|
|
|
|
|
|
}
|
|
|
|
|
|
} else if v2.Kind() == reflect.Map || v.Kind() == reflect.Slice {
|
|
|
|
|
|
makeConfigVar(cfg, v2)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|