310 lines
7.3 KiB
Go
310 lines
7.3 KiB
Go
|
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() {
|
|||
|
config.LoadConfig(pluginName, &signerConfig)
|
|||
|
Config(signerConfig)
|
|||
|
|
|||
|
obj := map[string]any{
|
|||
|
"config": Config,
|
|||
|
"registerSigner": RegisterSigner,
|
|||
|
"sortParams": SortParams,
|
|||
|
}
|
|||
|
|
|||
|
for signer := range signerConfig {
|
|||
|
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",
|
|||
|
})
|
|||
|
|
|||
|
actions := map[string]map[string]any{}
|
|||
|
u.Convert((*cfg)["actions"], &actions)
|
|||
|
for k, v := range actions {
|
|||
|
o[k] = MakeAction(signer, v)
|
|||
|
}
|
|||
|
obj[signer] = o
|
|||
|
}
|
|||
|
|
|||
|
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,
|
|||
|
})
|
|||
|
}
|
|||
|
|
|||
|
func MakeAction(signer string, actionCfg map[string]any) func(req *Request) (*Result, error) {
|
|||
|
cfg := &SignerConfig{data: map[string]any{}}
|
|||
|
if c := signerConfig[signer]; c != nil {
|
|||
|
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", "")
|
|||
|
}
|
|||
|
|
|||
|
return func(req *Request) (*Result, error) {
|
|||
|
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
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
data, resp, err := Do(cfg, req1)
|
|||
|
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
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
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 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)
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|