diff --git a/internal/panel/client.go b/internal/panel/client.go index 75a0aad..b4e2a16 100644 --- a/internal/panel/client.go +++ b/internal/panel/client.go @@ -8,6 +8,7 @@ import ( "log/slog" "net/http" "net/url" + "reflect" "strconv" "strings" "sync" @@ -145,19 +146,31 @@ func (c *Client) Report(traffic map[int][2]int64, alive map[int][]string, online } // decodeWeakRaw decodes an interface (from JSON map) into a struct using weak type conversion. -func decodeWeakRaw(input interface{}, output interface{}) error { +func decodeWeakRaw(input map[string]interface{}, output interface{}) error { config := &mapstructure.DecoderConfig{ Metadata: nil, Result: output, WeaklyTypedInput: true, - TagName: "json", // Follow JSON tags + TagName: "json", + DecodeHook: mapstructure.ComposeDecodeHookFunc( + func(f, t reflect.Type, v interface{}) (interface{}, error) { + // Handle []interface{} -> StringOrArray + if t == reflect.TypeOf(StringOrArray("")) && f.Kind() == reflect.Slice { + arr, _ := v.([]interface{}) + var strs []string + for _, x := range arr { + if s, ok := x.(string); ok { + strs = append(strs, s) + } + } + return StringOrArray(strings.Join(strs, "\n")), nil + } + return v, nil + }, + mapstructure.StringToSliceHookFunc(","), + ), } - - decoder, err := mapstructure.NewDecoder(config) - if err != nil { - return err - } - + decoder, _ := mapstructure.NewDecoder(config) return decoder.Decode(input) } diff --git a/internal/panel/client_test.go b/internal/panel/client_test.go index 7893773..1d0c74a 100644 --- a/internal/panel/client_test.go +++ b/internal/panel/client_test.go @@ -293,3 +293,45 @@ func TestClient_NodeTypeInQuery(t *testing.T) { }) client.GetConfig() } + +func TestStringOrArrayDecodeHook(t *testing.T) { + // Test case: []interface{} -> StringOrArray + input := map[string]interface{}{ + "padding_scheme": []interface{}{"stop=8", "0=30-30", "1=100-400"}, + } + + type TestConfig struct { + PaddingScheme StringOrArray `json:"padding_scheme"` + } + + var cfg TestConfig + if err := decodeWeakRaw(input, &cfg); err != nil { + t.Fatalf("decodeWeakRaw failed: %v", err) + } + + want := "stop=8\n0=30-30\n1=100-400" + if string(cfg.PaddingScheme) != want { + t.Errorf("got %q, want %q", cfg.PaddingScheme, want) + } +} + +func TestStringOrArrayDecodeHook_String(t *testing.T) { + // Test case: string -> StringOrArray + input := map[string]interface{}{ + "padding_scheme": "stop=8\n0=30-30", + } + + type TestConfig struct { + PaddingScheme StringOrArray `json:"padding_scheme"` + } + + var cfg TestConfig + if err := decodeWeakRaw(input, &cfg); err != nil { + t.Fatalf("decodeWeakRaw failed: %v", err) + } + + want := "stop=8\n0=30-30" + if string(cfg.PaddingScheme) != want { + t.Errorf("got %q, want %q", cfg.PaddingScheme, want) + } +} diff --git a/internal/panel/ws.go b/internal/panel/ws.go index 463e560..598afa6 100644 --- a/internal/panel/ws.go +++ b/internal/panel/ws.go @@ -303,7 +303,7 @@ func (w *WSClient) handleDataEvent(msg wsMessage) { // Helper to unmarshal and decode with weak Typing decodeData := func(data []byte, target interface{}) error { - var raw any + var raw map[string]interface{} if err := json.Unmarshal(data, &raw); err != nil { return err }