feat: clean up jsmod exports and add AddConfig for in-memory updates

This commit is contained in:
AI Engineer 2026-05-30 20:09:57 +08:00
parent 6b6e191c71
commit c0e0d7c280
2 changed files with 20 additions and 33 deletions

View File

@ -30,6 +30,17 @@ func SetEncryptKeys(key, iv []byte) {
crypto.SetDefaultAES(key, iv) crypto.SetDefaultAES(key, iv)
} }
// AddConfig 允许在内存中动态添加或覆盖配置,适用于从数据库或知识库加载配置的场景
func AddConfig(name string, conf map[string]any) {
configMutex.Lock()
defer configMutex.Unlock()
if GlobalConfigs[name] == nil {
GlobalConfigs[name] = conf
} else if dst, ok := GlobalConfigs[name].(map[string]any); ok {
MergeMap(dst, conf)
}
}
// Load 加载指定的配置文件并合并到 GlobalConfigs // Load 加载指定的配置文件并合并到 GlobalConfigs
func Load(name string) error { func Load(name string) error {
if name == "" { if name == "" {

View File

@ -9,12 +9,13 @@ import (
func init() { func init() {
jsmod.Register("api", map[string]any{ jsmod.Register("api", map[string]any{
"call": CallAny, "call": call,
"load": Load, "addConfig": AddConfig,
}) })
} }
func CallAny(ctx context.Context, actionName string, payload any, options ...map[string]any) (any, error) { // call 提供给 JS 的私有入口,避免污染 Go 公开 API
func call(ctx context.Context, actionName string, payload any, options ...map[string]any) (any, error) {
var opt map[string]any var opt map[string]any
if len(options) > 0 { if len(options) > 0 {
opt = options[0] opt = options[0]
@ -24,7 +25,7 @@ func CallAny(ctx context.Context, actionName string, payload any, options ...map
payload = make(map[string]any) payload = make(map[string]any)
} }
// 1. Pre-fill the payload from GlobalConfigs (manually doing what api.Call does later) // 1. 预填 Payload (JS 传来的 map 需要手动触发一次 fill 以获取全局配置)
actionConfig, safeBufs := GetActionConfig(actionName) actionConfig, safeBufs := GetActionConfig(actionName)
defer func() { defer func() {
for _, sb := range safeBufs { for _, sb := range safeBufs {
@ -33,7 +34,7 @@ func CallAny(ctx context.Context, actionName string, payload any, options ...map
}() }()
fill(payload, actionConfig) fill(payload, actionConfig)
// 2. Wrap into jsAction for interface satisfaction // 2. 构造符合 Action 接口的私有包装器
ja := &jsAction{ ja := &jsAction{
name: actionName, name: actionName,
payload: payload, payload: payload,
@ -43,8 +44,6 @@ func CallAny(ctx context.Context, actionName string, payload any, options ...map
return Call[any](ja) return Call[any](ja)
} }
// jsAction implements Action, SignerAction, URLAction, MethodAction, and ConfigurableAction.
// It redirects reflection-based 'fill' and 'json.Marshal' to its internal payload.
type jsAction struct { type jsAction struct {
name string name string
payload any payload any
@ -52,40 +51,17 @@ type jsAction struct {
} }
func (a *jsAction) ActionName() string { return a.name } func (a *jsAction) ActionName() string { return a.name }
func (a *jsAction) SignerName() string { func (a *jsAction) SignerName() string {
if a.options != nil { if a.options != nil {
if s := cast.String(a.options["signer"]); s != "" { return cast.String(a.options["signer"])
return s
}
} }
return "" return ""
} }
func (a *jsAction) GetURL() string { return cast.String(a.options["url"]) }
func (a *jsAction) GetURL() string { func (a *jsAction) GetMethod() string { return cast.String(a.options["method"]) }
if a.options != nil {
if u := cast.String(a.options["url"]); u != "" {
return u
}
}
return ""
}
func (a *jsAction) GetMethod() string {
if a.options != nil {
if m := cast.String(a.options["method"]); m != "" {
return m
}
}
return ""
}
func (a *jsAction) Config() map[string]any { func (a *jsAction) Config() map[string]any {
return a.options return a.options
} }
// MarshalJSON ensures that when this action is sent via HTTP (as JSON),
// only the internal payload is sent, not the metadata fields.
func (a *jsAction) MarshalJSON() ([]byte, error) { func (a *jsAction) MarshalJSON() ([]byte, error) {
return cast.ToJSONBytes(a.payload) return cast.ToJSONBytes(a.payload)
} }