205 lines
4.3 KiB
Go
205 lines
4.3 KiB
Go
package api
|
||
|
||
import (
|
||
"reflect"
|
||
"strings"
|
||
"sync"
|
||
|
||
"apigo.cc/go/cast"
|
||
"apigo.cc/go/config"
|
||
"apigo.cc/go/crypto"
|
||
"apigo.cc/go/encoding"
|
||
"apigo.cc/go/safe"
|
||
)
|
||
|
||
var confAes, _ = crypto.NewAESGCMAndEraseKey([]byte("?GQ$0K0GgLdO=f+~L68PLm$uhKr4'=tV"), []byte("VFs7@sK61cj^f?HZ"))
|
||
var keysOnce = sync.Once{}
|
||
var configMutex sync.RWMutex
|
||
|
||
// GlobalConfigs 存储整棵配置树
|
||
var GlobalConfigs = map[string]any{}
|
||
|
||
// SetEncryptKeys 允许设置自定义的配置加解密密钥
|
||
func SetEncryptKeys(key, iv []byte) {
|
||
keysOnce.Do(func() {
|
||
if confAes != nil {
|
||
confAes.Close()
|
||
}
|
||
confAes, _ = crypto.NewAESGCMAndEraseKey(key, iv)
|
||
})
|
||
}
|
||
|
||
// Load 加载指定的配置文件并合并到 GlobalConfigs
|
||
func Load(name string) error {
|
||
if name == "" {
|
||
name = "api"
|
||
}
|
||
var conf map[string]any
|
||
err := config.Load(&conf, name)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
configMutex.Lock()
|
||
defer configMutex.Unlock()
|
||
|
||
// 合并到全局树
|
||
MergeMap(GlobalConfigs, conf)
|
||
return nil
|
||
}
|
||
|
||
// GetActionConfig 获取某个动作经过层级合并后的完整配置,返回配置图和需要手动关闭的 SafeBuf 列表
|
||
func GetActionConfig(actionName string) (map[string]any, []*safe.SafeBuf) {
|
||
configMutex.RLock()
|
||
defer configMutex.RUnlock()
|
||
|
||
parts := strings.Split(actionName, ".")
|
||
res := map[string]any{}
|
||
|
||
// 1. 获取 api 根节点
|
||
curr, ok := GlobalConfigs["api"].(map[string]any)
|
||
if !ok {
|
||
return res, nil
|
||
}
|
||
|
||
// 2. 逐级导航并合并
|
||
for _, part := range parts {
|
||
next, ok := curr[part].(map[string]any)
|
||
if !ok {
|
||
// 尝试在 actions 目录下寻找 (可选约定)
|
||
if actions, ok := curr["actions"].(map[string]any); ok {
|
||
next, ok = actions[part].(map[string]any)
|
||
}
|
||
}
|
||
|
||
if next != nil {
|
||
MergeMap(res, next)
|
||
curr = next
|
||
} else {
|
||
break
|
||
}
|
||
}
|
||
|
||
safeBufs := decryptMap(res)
|
||
return res, safeBufs
|
||
}
|
||
|
||
// fill 注入配置到 Action,非破坏性,仅注入零值
|
||
func fill(action any, actionConfig map[string]any) {
|
||
if action == nil || actionConfig == nil {
|
||
return
|
||
}
|
||
|
||
v := reflect.ValueOf(action)
|
||
for v.Kind() == reflect.Ptr {
|
||
v = v.Elem()
|
||
}
|
||
|
||
if v.Kind() == reflect.Struct {
|
||
t := v.Type()
|
||
for i := 0; i < v.NumField(); i++ {
|
||
f := v.Field(i)
|
||
if !f.CanSet() || !f.IsZero() {
|
||
continue
|
||
}
|
||
|
||
fieldName := t.Field(i).Name
|
||
if val, ok := findConfigValue(actionConfig, fieldName); ok {
|
||
// 如果目标是 string 但值是 SafeBuf,默认不注入以防泄露,除非手动处理
|
||
if f.Kind() == reflect.String {
|
||
if _, isSafe := val.(*safe.SafeBuf); isSafe {
|
||
continue
|
||
}
|
||
}
|
||
cast.Convert(f.Addr().Interface(), val)
|
||
}
|
||
}
|
||
} else if v.Kind() == reflect.Map {
|
||
for _, key := range v.MapKeys() {
|
||
kv := v.MapIndex(key)
|
||
if isZero(kv) {
|
||
if val, ok := findConfigValue(actionConfig, cast.String(key.Interface())); ok {
|
||
v.SetMapIndex(key, reflect.ValueOf(val))
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
func findConfigValue(m map[string]any, key string) (any, bool) {
|
||
if v, ok := m[key]; ok {
|
||
return v, true
|
||
}
|
||
lk := strings.ToLower(key)
|
||
for k, v := range m {
|
||
if strings.ToLower(k) == lk {
|
||
return v, true
|
||
}
|
||
}
|
||
return nil, false
|
||
}
|
||
|
||
func isZero(v reflect.Value) bool {
|
||
if !v.IsValid() {
|
||
return true
|
||
}
|
||
switch v.Kind() {
|
||
case reflect.Interface, reflect.Ptr, reflect.Map, reflect.Slice:
|
||
return v.IsNil()
|
||
default:
|
||
return v.IsZero()
|
||
}
|
||
}
|
||
|
||
// MergeMap 深度合并两个 map
|
||
func MergeMap(dst, src map[string]any) {
|
||
for k, v := range src {
|
||
if k == "actions" || k == "raw" {
|
||
continue
|
||
}
|
||
if v == nil {
|
||
continue
|
||
}
|
||
|
||
if srcMap, ok := v.(map[string]any); ok {
|
||
dstVal, ok := dst[k]
|
||
var dstMap map[string]any
|
||
if ok {
|
||
dstMap, _ = dstVal.(map[string]any)
|
||
}
|
||
if dstMap == nil {
|
||
dstMap = make(map[string]any)
|
||
dst[k] = dstMap
|
||
}
|
||
MergeMap(dstMap, srcMap)
|
||
} else {
|
||
dst[k] = v
|
||
}
|
||
}
|
||
}
|
||
|
||
func decryptMap(m map[string]any) []*safe.SafeBuf {
|
||
var safeBufs []*safe.SafeBuf
|
||
for k, v := range m {
|
||
if s, ok := v.(string); ok {
|
||
var b64 []byte
|
||
var err error
|
||
if b64, err = encoding.UnUrlBase64FromString(s); err != nil {
|
||
b64, err = encoding.UnBase64FromString(s)
|
||
}
|
||
|
||
if err == nil && len(b64) > 0 {
|
||
if dec, err := confAes.DecryptBytes(b64); err == nil {
|
||
sb := safe.NewSafeBufAndErase(dec)
|
||
m[k] = sb
|
||
safeBufs = append(safeBufs, sb)
|
||
continue
|
||
}
|
||
}
|
||
} else if subMap, ok := v.(map[string]any); ok {
|
||
safeBufs = append(safeBufs, decryptMap(subMap)...)
|
||
}
|
||
}
|
||
return safeBufs
|
||
}
|