api/plugin.go
2025-09-11 23:42:22 +08:00

310 lines
7.3 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 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)
}
}
}
}