api/config.go

193 lines
3.9 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 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)
}
}
}