api/config.go

193 lines
3.9 KiB
Go
Raw Permalink Normal View History

package api
import (
"reflect"
"strings"
"sync"
"apigo.cc/go/cast"
"apigo.cc/go/config"
"apigo.cc/go/crypto"
"apigo.cc/go/encoding"
)
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 获取某个动作经过层级合并后的完整配置
func GetActionConfig(actionName string) map[string]any {
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
}
// 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
}
}
decryptMap(res)
return res
}
// 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 {
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) {
for k, v := range m {
if s, ok := v.(string); ok {
if b64, err := encoding.UnUrlBase64FromString(s); err == nil {
if dec, err := confAes.DecryptBytes(b64); err == nil {
m[k] = string(dec)
continue
}
}
if b64, err := encoding.UnBase64FromString(s); err == nil {
if dec, err := confAes.DecryptBytes(b64); err == nil {
m[k] = string(dec)
}
}
} else if subMap, ok := v.(map[string]any); ok {
decryptMap(subMap)
}
}
}