api/plugin.go

310 lines
7.3 KiB
Go
Raw Permalink Normal View History

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