api/config.go

205 lines
4.3 KiB
Go
Raw 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 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
}