mirror of
https://github.com/YspCoder/clawgo.git
synced 2026-05-17 14:17:28 +08:00
webui config: add schema validation and risky-field confirmation on save
This commit is contained in:
@@ -2,6 +2,7 @@ package nodes
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
@@ -18,6 +19,8 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
cfgpkg "clawgo/pkg/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
type RegistryServer struct {
|
type RegistryServer struct {
|
||||||
@@ -263,6 +266,61 @@ func (s *RegistryServer) handleWebUIConfig(w http.ResponseWriter, r *http.Reques
|
|||||||
http.Error(w, "invalid json", http.StatusBadRequest)
|
http.Error(w, "invalid json", http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
confirmRisky, _ := body["confirm_risky"].(bool)
|
||||||
|
delete(body, "confirm_risky")
|
||||||
|
|
||||||
|
oldCfgRaw, _ := os.ReadFile(s.configPath)
|
||||||
|
var oldMap map[string]interface{}
|
||||||
|
_ = json.Unmarshal(oldCfgRaw, &oldMap)
|
||||||
|
|
||||||
|
riskyPaths := []string{
|
||||||
|
"channels.telegram.token",
|
||||||
|
"channels.telegram.allow_from",
|
||||||
|
"channels.telegram.allow_chats",
|
||||||
|
"providers.proxy.base_url",
|
||||||
|
"providers.proxy.api_key",
|
||||||
|
"gateway.token",
|
||||||
|
"gateway.port",
|
||||||
|
}
|
||||||
|
changedRisky := make([]string, 0)
|
||||||
|
for _, p := range riskyPaths {
|
||||||
|
if fmt.Sprintf("%v", getPathValue(oldMap, p)) != fmt.Sprintf("%v", getPathValue(body, p)) {
|
||||||
|
changedRisky = append(changedRisky, p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(changedRisky) > 0 && !confirmRisky {
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
_ = json.NewEncoder(w).Encode(map[string]interface{}{
|
||||||
|
"ok": false,
|
||||||
|
"error": "risky fields changed; confirmation required",
|
||||||
|
"requires_confirm": true,
|
||||||
|
"changed_fields": changedRisky,
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
candidate, err := json.Marshal(body)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
cfg := cfgpkg.DefaultConfig()
|
||||||
|
dec := json.NewDecoder(bytes.NewReader(candidate))
|
||||||
|
dec.DisallowUnknownFields()
|
||||||
|
if err := dec.Decode(cfg); err != nil {
|
||||||
|
http.Error(w, "config schema validation failed: "+err.Error(), http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if errs := cfgpkg.Validate(cfg); len(errs) > 0 {
|
||||||
|
list := make([]string, 0, len(errs))
|
||||||
|
for _, e := range errs {
|
||||||
|
list = append(list, e.Error())
|
||||||
|
}
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
_ = json.NewEncoder(w).Encode(map[string]interface{}{"ok": false, "error": "config validation failed", "details": list})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
b, err := json.MarshalIndent(body, "", " ")
|
b, err := json.MarshalIndent(body, "", " ")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||||
@@ -288,6 +346,22 @@ func (s *RegistryServer) handleWebUIConfig(w http.ResponseWriter, r *http.Reques
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getPathValue(m map[string]interface{}, path string) interface{} {
|
||||||
|
if m == nil || strings.TrimSpace(path) == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
parts := strings.Split(path, ".")
|
||||||
|
var cur interface{} = m
|
||||||
|
for _, p := range parts {
|
||||||
|
node, ok := cur.(map[string]interface{})
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
cur = node[p]
|
||||||
|
}
|
||||||
|
return cur
|
||||||
|
}
|
||||||
|
|
||||||
func (s *RegistryServer) handleWebUIUpload(w http.ResponseWriter, r *http.Request) {
|
func (s *RegistryServer) handleWebUIUpload(w http.ResponseWriter, r *http.Request) {
|
||||||
if !s.checkAuth(r) {
|
if !s.checkAuth(r) {
|
||||||
http.Error(w, "unauthorized", http.StatusUnauthorized)
|
http.Error(w, "unauthorized", http.StatusUnauthorized)
|
||||||
|
|||||||
Reference in New Issue
Block a user