refactor: improve config parsing robustness

This commit is contained in:
xboard
2026-03-15 13:10:54 +08:00
parent f83952a0dd
commit 3478eb52a1
4 changed files with 64 additions and 6 deletions
+31 -2
View File
@@ -14,6 +14,7 @@ import (
"time"
"github.com/cedar2025/xboard-node/internal/config"
"github.com/mitchellh/mapstructure"
)
// Client communicates with the Xboard panel API
@@ -118,6 +119,23 @@ func (c *Client) Report(traffic map[int][2]int64, alive map[int][]string, online
return c.postJSON("/api/v2/server/report", payload)
}
// decodeWeakRaw decodes an interface (from JSON map) into a struct using weak type conversion.
func decodeWeakRaw(input interface{}, output interface{}) error {
config := &mapstructure.DecoderConfig{
Metadata: nil,
Result: output,
WeaklyTypedInput: true,
TagName: "json", // Follow JSON tags
}
decoder, err := mapstructure.NewDecoder(config)
if err != nil {
return err
}
return decoder.Decode(input)
}
// GetConfig fetches node configuration. Returns nil if not modified (304).
func (c *Client) GetConfig() (*NodeConfig, error) {
resp, err := c.doRequest("GET", "/api/v1/server/UniProxy/config", nil, c.configETag)
@@ -134,9 +152,20 @@ func (c *Client) GetConfig() (*NodeConfig, error) {
return nil, fmt.Errorf("status %d: %s", resp.StatusCode, body)
}
var raw map[string]interface{}
if err := json.NewDecoder(resp.Body).Decode(&raw); err != nil {
return nil, fmt.Errorf("decode raw config: %w", err)
}
var cfg NodeConfig
if err := json.NewDecoder(resp.Body).Decode(&cfg); err != nil {
return nil, fmt.Errorf("decode: %w", err)
// Use mapstructure for weak type conversion (string -> int, bool -> string, etc.)
if err := decodeWeakRaw(raw, &cfg); err != nil {
return nil, fmt.Errorf("weak decode config: %w", err)
}
// Basic validation
if cfg.Protocol == "" {
return nil, fmt.Errorf("invalid config: missing protocol")
}
if etag := resp.Header.Get("ETag"); etag != "" {
+12 -3
View File
@@ -268,11 +268,20 @@ func (w *WSClient) handleDataEvent(msg wsMessage) {
var event WSEvent
event.Type = msg.Event
// Helper to unmarshal and decode with weak Typing
decodeData := func(data []byte, target interface{}) error {
var raw map[string]interface{}
if err := json.Unmarshal(data, &raw); err != nil {
return err
}
return decodeWeakRaw(raw, target)
}
switch msg.Event {
case WSEventSyncConfig:
slog.Info("ws sync config event received")
var p syncConfigPayload
if err := json.Unmarshal(msg.Data, &p); err != nil {
if err := decodeData(msg.Data, &p); err != nil {
slog.Warn("ws: cannot decode config payload", "error", err)
return
}
@@ -281,7 +290,7 @@ func (w *WSClient) handleDataEvent(msg wsMessage) {
case WSEventSyncUsers:
slog.Info("ws sync users event received")
var p syncUsersPayload
if err := json.Unmarshal(msg.Data, &p); err != nil {
if err := decodeData(msg.Data, &p); err != nil {
slog.Warn("ws: cannot decode users payload", "error", err)
return
}
@@ -290,7 +299,7 @@ func (w *WSClient) handleDataEvent(msg wsMessage) {
case WSEventSyncUserDelta:
slog.Info("ws sync user delta event received")
var p syncUserDeltaPayload
if err := json.Unmarshal(msg.Data, &p); err != nil {
if err := decodeData(msg.Data, &p); err != nil {
slog.Warn("ws: cannot decode user delta payload", "error", err)
return
}