diff --git a/common/json.go b/common/json.go index 1625be6d51f7..8bc194a99b83 100644 --- a/common/json.go +++ b/common/json.go @@ -22,6 +22,11 @@ func Marshal(v any) ([]byte, error) { return json.Marshal(v) } +// ValidJson 校验数据是否为合法 JSON(替代 encoding/json 的 json.Valid)。 +func ValidJson(data []byte) bool { + return json.Valid(data) +} + func GetJsonType(data json.RawMessage) string { trimmed := bytes.TrimSpace(data) if len(trimmed) == 0 { diff --git a/common/json_test.go b/common/json_test.go index b59949451686..e9c614820a77 100644 --- a/common/json_test.go +++ b/common/json_test.go @@ -2,8 +2,11 @@ package common import ( "encoding/json" + "strconv" + "strings" "testing" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -41,3 +44,121 @@ func TestJsonRawMessageToString(t *testing.T) { }) } } + +// customMarshaler 用于验证底层 JSON 库仍会调用类型自定义的 MarshalJSON/UnmarshalJSON, +// 不依赖 dto 包(避免 common <-> dto 循环引用)。 +type customMarshaler struct { + V int +} + +func (c customMarshaler) MarshalJSON() ([]byte, error) { + return []byte(`"custom:` + strconv.Itoa(c.V) + `"`), nil +} + +func (c *customMarshaler) UnmarshalJSON(b []byte) error { + var s string + if err := Unmarshal(b, &s); err != nil { + return err + } + n, err := strconv.Atoi(strings.TrimPrefix(s, "custom:")) + if err != nil { + return err + } + c.V = n + return nil +} + +// TestMarshalStdCompatible 锁定与 encoding/json 字节级一致的关键契约: +// map key 字典序排序(保护依赖 JSON 字节稳定的签名场景)与自定义 Marshaler 仍然生效。 +// (HTML 转义与标准库的字节一致性由 TestMarshalMatchesEncodingJSON 覆盖。) +func TestMarshalStdCompatible(t *testing.T) { + tests := []struct { + name string + in any + want string + }{ + {"map key 字典序排序", map[string]int{"b": 2, "a": 1, "c": 3}, `{"a":1,"b":2,"c":3}`}, + {"嵌套 map 排序", map[string]any{"z": map[string]int{"y": 1, "x": 2}}, `{"z":{"x":2,"y":1}}`}, + {"自定义 Marshaler 生效", customMarshaler{42}, `"custom:42"`}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := Marshal(tt.in) + require.NoError(t, err) + assert.Equal(t, tt.want, string(got)) + }) + } +} + +// TestMarshalMatchesEncodingJSON 对同一输入直接比对 common.Marshal 与标准库的输出字节, +// 这是「升级底层库零回归」的核心保证。 +func TestMarshalMatchesEncodingJSON(t *testing.T) { + inputs := []any{ + map[string]int{"b": 2, "a": 1}, + map[string]string{"html": `&`}, + []any{1, "two", true, nil}, + struct { + Name string `json:"name"` + Tags []string `json:"tags"` + }{"foo", []string{"a", "b"}}, + } + for i, in := range inputs { + t.Run(strconv.Itoa(i), func(t *testing.T) { + std, err := json.Marshal(in) + require.NoError(t, err) + got, err := Marshal(in) + require.NoError(t, err) + assert.Equal(t, string(std), string(got)) + }) + } +} + +// TestRoundTrip 验证 RawMessage、json.Number、自定义 Marshaler 与 UnmarshalJsonStr +// 编解码后语义不变。 +func TestRoundTrip(t *testing.T) { + t.Run("RawMessage", func(t *testing.T) { + type wrap struct { + Raw json.RawMessage `json:"raw"` + } + src := wrap{Raw: json.RawMessage(`{"k":1}`)} + b, err := Marshal(src) + require.NoError(t, err) + var dst wrap + require.NoError(t, Unmarshal(b, &dst)) + assert.JSONEq(t, string(src.Raw), string(dst.Raw)) + }) + + t.Run("json.Number", func(t *testing.T) { + type wrap struct { + N json.Number `json:"n"` + } + b, err := Marshal(wrap{N: "123.456"}) + require.NoError(t, err) + assert.Equal(t, `{"n":123.456}`, string(b)) + var dst wrap + require.NoError(t, Unmarshal(b, &dst)) + assert.Equal(t, json.Number("123.456"), dst.N) + }) + + t.Run("自定义 Marshaler", func(t *testing.T) { + b, err := Marshal(customMarshaler{7}) + require.NoError(t, err) + var c customMarshaler + require.NoError(t, Unmarshal(b, &c)) + assert.Equal(t, 7, c.V) + }) + + t.Run("UnmarshalJsonStr", func(t *testing.T) { + var m map[string]int + require.NoError(t, UnmarshalJsonStr(`{"a":1,"b":2}`, &m)) + assert.Equal(t, map[string]int{"a": 1, "b": 2}, m) + }) +} + +// TestValidJson 覆盖新增的 ValidJson 封装。 +func TestValidJson(t *testing.T) { + assert.True(t, ValidJson([]byte(`{"a":1}`))) + assert.True(t, ValidJson([]byte(`[1,2,3]`))) + assert.False(t, ValidJson([]byte(`{"a":}`))) + assert.False(t, ValidJson([]byte(`not json`))) +} diff --git a/common/str.go b/common/str.go index 9f3b9d46d3e4..add09966fcaf 100644 --- a/common/str.go +++ b/common/str.go @@ -2,7 +2,6 @@ package common import ( "encoding/base64" - "encoding/json" "fmt" "net/url" "regexp" @@ -46,7 +45,7 @@ func GetRandomString(length int) string { } func MapToJsonStr(m map[string]interface{}) string { - bytes, err := json.Marshal(m) + bytes, err := Marshal(m) if err != nil { return "" } @@ -64,7 +63,7 @@ func StrToMap(str string) (map[string]interface{}, error) { func StrToJsonArray(str string) ([]interface{}, error) { var js []interface{} - err := json.Unmarshal([]byte(str), &js) + err := Unmarshal([]byte(str), &js) if err != nil { return nil, err } @@ -73,12 +72,12 @@ func StrToJsonArray(str string) ([]interface{}, error) { func IsJsonArray(str string) bool { var js []interface{} - return json.Unmarshal([]byte(str), &js) == nil + return Unmarshal([]byte(str), &js) == nil } func IsJsonObject(str string) bool { var js map[string]interface{} - return json.Unmarshal([]byte(str), &js) == nil + return Unmarshal([]byte(str), &js) == nil } func String2Int(str string) int { @@ -113,7 +112,7 @@ func GetJsonString(data any) string { if data == nil { return "" } - b, _ := json.Marshal(data) + b, _ := Marshal(data) return string(b) } diff --git a/common/topup-ratio.go b/common/topup-ratio.go index 2b60cde7d169..4c931ea5953a 100644 --- a/common/topup-ratio.go +++ b/common/topup-ratio.go @@ -1,7 +1,6 @@ package common import ( - "encoding/json" "sync" ) @@ -15,7 +14,7 @@ var topupGroupRatioMutex sync.RWMutex func TopupGroupRatio2JSONString() string { topupGroupRatioMutex.RLock() defer topupGroupRatioMutex.RUnlock() - jsonBytes, err := json.Marshal(topupGroupRatio) + jsonBytes, err := Marshal(topupGroupRatio) if err != nil { SysError("error marshalling topup group ratio: " + err.Error()) } @@ -26,7 +25,7 @@ func UpdateTopupGroupRatioByJSONString(jsonStr string) error { topupGroupRatioMutex.Lock() defer topupGroupRatioMutex.Unlock() topupGroupRatio = make(map[string]float64) - return json.Unmarshal([]byte(jsonStr), &topupGroupRatio) + return Unmarshal([]byte(jsonStr), &topupGroupRatio) } func GetTopupGroupRatio(name string) float64 { diff --git a/common/utils.go b/common/utils.go index 7e658ff4ea58..c06b5c44ce61 100644 --- a/common/utils.go +++ b/common/utils.go @@ -5,7 +5,6 @@ import ( "crypto/sha256" "encoding/base64" "encoding/hex" - "encoding/json" "fmt" "html/template" "io" @@ -305,12 +304,12 @@ func GetPointer[T any](v T) *T { func Any2Type[T any](data any) (T, error) { var zero T - bytes, err := json.Marshal(data) + bytes, err := Marshal(data) if err != nil { return zero, err } var res T - err = json.Unmarshal(bytes, &res) + err = Unmarshal(bytes, &res) if err != nil { return zero, err } diff --git a/controller/channel-billing.go b/controller/channel-billing.go index 751ee3600ac9..8140fd6c330c 100644 --- a/controller/channel-billing.go +++ b/controller/channel-billing.go @@ -1,7 +1,6 @@ package controller import ( - "encoding/json" "errors" "fmt" "io" @@ -174,7 +173,7 @@ func updateChannelCloseAIBalance(channel *model.Channel) (float64, error) { return 0, err } response := OpenAICreditGrants{} - err = json.Unmarshal(body, &response) + err = common.Unmarshal(body, &response) if err != nil { return 0, err } @@ -189,7 +188,7 @@ func updateChannelOpenAISBBalance(channel *model.Channel) (float64, error) { return 0, err } response := OpenAISBUsageResponse{} - err = json.Unmarshal(body, &response) + err = common.Unmarshal(body, &response) if err != nil { return 0, err } @@ -213,7 +212,7 @@ func updateChannelAIProxyBalance(channel *model.Channel) (float64, error) { return 0, err } response := AIProxyUserOverviewResponse{} - err = json.Unmarshal(body, &response) + err = common.Unmarshal(body, &response) if err != nil { return 0, err } @@ -232,7 +231,7 @@ func updateChannelAPI2GPTBalance(channel *model.Channel) (float64, error) { return 0, err } response := API2GPTUsageResponse{} - err = json.Unmarshal(body, &response) + err = common.Unmarshal(body, &response) if err != nil { return 0, err } @@ -247,7 +246,7 @@ func updateChannelSiliconFlowBalance(channel *model.Channel) (float64, error) { return 0, err } response := SiliconFlowUsageResponse{} - err = json.Unmarshal(body, &response) + err = common.Unmarshal(body, &response) if err != nil { return 0, err } @@ -269,7 +268,7 @@ func updateChannelDeepSeekBalance(channel *model.Channel) (float64, error) { return 0, err } response := DeepSeekUsageResponse{} - err = json.Unmarshal(body, &response) + err = common.Unmarshal(body, &response) if err != nil { return 0, err } @@ -298,7 +297,7 @@ func updateChannelAIGC2DBalance(channel *model.Channel) (float64, error) { return 0, err } response := APGC2DGPTUsageResponse{} - err = json.Unmarshal(body, &response) + err = common.Unmarshal(body, &response) if err != nil { return 0, err } @@ -313,7 +312,7 @@ func updateChannelOpenRouterBalance(channel *model.Channel) (float64, error) { return 0, err } response := OpenRouterCreditResponse{} - err = json.Unmarshal(body, &response) + err = common.Unmarshal(body, &response) if err != nil { return 0, err } @@ -343,7 +342,7 @@ func updateChannelMoonshotBalance(channel *model.Channel) (float64, error) { } response := MoonshotBalanceResponse{} - err = json.Unmarshal(body, &response) + err = common.Unmarshal(body, &response) if err != nil { return 0, err } @@ -396,7 +395,7 @@ func updateChannelBalance(channel *model.Channel) (float64, error) { return 0, err } subscription := OpenAISubscriptionResponse{} - err = json.Unmarshal(body, &subscription) + err = common.Unmarshal(body, &subscription) if err != nil { return 0, err } @@ -412,7 +411,7 @@ func updateChannelBalance(channel *model.Channel) (float64, error) { return 0, err } usage := OpenAIUsageResponse{} - err = json.Unmarshal(body, &usage) + err = common.Unmarshal(body, &usage) if err != nil { return 0, err } diff --git a/controller/channel.go b/controller/channel.go index a00011a9f9c1..c6a6e63eb139 100644 --- a/controller/channel.go +++ b/controller/channel.go @@ -589,7 +589,7 @@ func getVertexArrayKeys(keys string) ([]string, error) { case string: keyStr = strings.TrimSpace(v) default: - bytes, err := json.Marshal(v) + bytes, err := common.Marshal(v) if err != nil { return nil, fmt.Errorf("Vertex AI key JSON 编码失败: %w", err) } @@ -840,7 +840,7 @@ func EditTagChannels(c *gin.Context) { } if channelTag.ParamOverride != nil { trimmed := strings.TrimSpace(*channelTag.ParamOverride) - if trimmed != "" && !json.Valid([]byte(trimmed)) { + if trimmed != "" && !common.ValidJson([]byte(trimmed)) { c.JSON(http.StatusOK, gin.H{ "success": false, "message": "参数覆盖必须是合法的 JSON 格式", @@ -851,7 +851,7 @@ func EditTagChannels(c *gin.Context) { } if channelTag.HeaderOverride != nil { trimmed := strings.TrimSpace(*channelTag.HeaderOverride) - if trimmed != "" && !json.Valid([]byte(trimmed)) { + if trimmed != "" && !common.ValidJson([]byte(trimmed)) { c.JSON(http.StatusOK, gin.H{ "success": false, "message": "请求头覆盖必须是合法的 JSON 格式", @@ -990,7 +990,7 @@ func UpdateChannel(c *gin.Context) { if strings.HasPrefix(strings.TrimSpace(originChannel.Key), "[") { // JSON数组格式 var arr []json.RawMessage - if err := json.Unmarshal([]byte(strings.TrimSpace(originChannel.Key)), &arr); err == nil { + if err := common.Unmarshal([]byte(strings.TrimSpace(originChannel.Key)), &arr); err == nil { existingKeys = make([]string, len(arr)) for i, v := range arr { existingKeys[i] = string(v) @@ -2056,7 +2056,7 @@ func OllamaPullModelStream(c *gin.Context) { // 创建进度回调函数 progressCallback := func(progress ollama.OllamaPullResponse) { - data, _ := json.Marshal(progress) + data, _ := common.Marshal(progress) fmt.Fprintf(c.Writer, "data: %s\n\n", string(data)) c.Writer.Flush() } @@ -2065,12 +2065,12 @@ func OllamaPullModelStream(c *gin.Context) { err = ollama.PullOllamaModelStream(baseURL, key, req.ModelName, progressCallback) if err != nil { - errorData, _ := json.Marshal(gin.H{ + errorData, _ := common.Marshal(gin.H{ "error": err.Error(), }) fmt.Fprintf(c.Writer, "data: %s\n\n", string(errorData)) } else { - successData, _ := json.Marshal(gin.H{ + successData, _ := common.Marshal(gin.H{ "message": fmt.Sprintf("Model %s pulled successfully", req.ModelName), }) fmt.Fprintf(c.Writer, "data: %s\n\n", string(successData)) diff --git a/controller/console_migrate.go b/controller/console_migrate.go index 4584961047cf..e94af926aee3 100644 --- a/controller/console_migrate.go +++ b/controller/console_migrate.go @@ -3,7 +3,6 @@ package controller import ( - "encoding/json" "net/http" "github.com/QuantumNous/new-api/common" @@ -30,11 +29,11 @@ func MigrateConsoleSetting(c *gin.Context) { // 处理 APIInfo if v := valMap["ApiInfo"]; v != "" { var arr []map[string]interface{} - if err := json.Unmarshal([]byte(v), &arr); err == nil { + if err := common.Unmarshal([]byte(v), &arr); err == nil { if len(arr) > 50 { arr = arr[:50] } - bytes, _ := json.Marshal(arr) + bytes, _ := common.Marshal(arr) model.UpdateOption("console_setting.api_info", string(bytes)) } model.UpdateOption("ApiInfo", "") @@ -47,7 +46,7 @@ func MigrateConsoleSetting(c *gin.Context) { // FAQ 转换 if v := valMap["FAQ"]; v != "" { var arr []map[string]interface{} - if err := json.Unmarshal([]byte(v), &arr); err == nil { + if err := common.Unmarshal([]byte(v), &arr); err == nil { out := []map[string]interface{}{} for _, item := range arr { q, _ := item["question"].(string) @@ -65,7 +64,7 @@ func MigrateConsoleSetting(c *gin.Context) { if len(out) > 50 { out = out[:50] } - bytes, _ := json.Marshal(out) + bytes, _ := common.Marshal(out) model.UpdateOption("console_setting.faq", string(bytes)) } model.UpdateOption("FAQ", "") @@ -84,7 +83,7 @@ func MigrateConsoleSetting(c *gin.Context) { "description": "", }, } - bytes, _ := json.Marshal(groups) + bytes, _ := common.Marshal(groups) model.UpdateOption("console_setting.uptime_kuma_groups", string(bytes)) } // 清空旧键内容 diff --git a/controller/deployment.go b/controller/deployment.go index a2ffedc6675f..37d9a07977aa 100644 --- a/controller/deployment.go +++ b/controller/deployment.go @@ -2,15 +2,15 @@ package controller import ( "bytes" - "encoding/json" "fmt" "strconv" "strings" "time" + "github.com/gin-gonic/gin" + "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/pkg/ionet" - "github.com/gin-gonic/gin" ) func getIoAPIKey(c *gin.Context) (string, bool) { @@ -66,7 +66,7 @@ func TestIoNetConnection(c *gin.Context) { return } if len(bytes.TrimSpace(rawBody)) > 0 { - if err := json.Unmarshal(rawBody, &req); err != nil { + if err := common.Unmarshal(rawBody, &req); err != nil { common.ApiErrorMsg(c, "invalid request payload") return } diff --git a/controller/misc.go b/controller/misc.go index fb2029878747..ecf74286fd85 100644 --- a/controller/misc.go +++ b/controller/misc.go @@ -1,7 +1,6 @@ package controller import ( - "encoding/json" "errors" "fmt" "net/http" @@ -333,9 +332,9 @@ type PasswordResetRequest struct { func ResetPassword(c *gin.Context) { var req PasswordResetRequest - err := json.NewDecoder(c.Request.Body).Decode(&req) + err := common.DecodeJson(c.Request.Body, &req) if err != nil { - common.ApiError(c, err) + common.ApiErrorI18n(c, i18n.MsgInvalidParams) return } req.Email = model.NormalizeEmail(req.Email) diff --git a/controller/model_meta.go b/controller/model_meta.go index c3d9954677e7..65a3c8d16589 100644 --- a/controller/model_meta.go +++ b/controller/model_meta.go @@ -1,7 +1,6 @@ package controller import ( - "encoding/json" "sort" "strconv" "strings" @@ -201,7 +200,7 @@ func enrichModels(models []*model.Model) { mm := models[idx] if mm.Endpoints == "" { eps := model.GetModelSupportEndpointTypes(mm.ModelName) - if b, err := json.Marshal(eps); err == nil { + if b, err := common.Marshal(eps); err == nil { mm.Endpoints = string(b) } } @@ -291,7 +290,7 @@ func enrichModels(models []*model.Model) { for et := range es { eps = append(eps, et) } - if b, err := json.Marshal(eps); err == nil { + if b, err := common.Marshal(eps); err == nil { mm.Endpoints = string(b) } } diff --git a/controller/model_sync.go b/controller/model_sync.go index f254dc88ee5e..ad2383c0a33b 100644 --- a/controller/model_sync.go +++ b/controller/model_sync.go @@ -180,10 +180,10 @@ func fetchJSON[T any](ctx context.Context, url string, out *upstreamEnvelope[T]) cacheMutex.Unlock() // Try decode as envelope first - if err := json.Unmarshal(buf, out); err != nil { + if err := common.Unmarshal(buf, out); err != nil { // Try decode as pure array var arr []T - if err2 := json.Unmarshal(buf, &arr); err2 != nil { + if err2 := common.Unmarshal(buf, &arr); err2 != nil { lastErr = err return } @@ -205,9 +205,9 @@ func fetchJSON[T any](ctx context.Context, url string, out *upstreamEnvelope[T]) lastErr = errors.New("cache miss for 304 response") return } - if err := json.Unmarshal(buf, out); err != nil { + if err := common.Unmarshal(buf, out); err != nil { var arr []T - if err2 := json.Unmarshal(buf, &arr); err2 != nil { + if err2 := common.Unmarshal(buf, &arr); err2 != nil { lastErr = err return } diff --git a/controller/topup_creem.go b/controller/topup_creem.go index 7472690e22fb..b28f1ee06a3e 100644 --- a/controller/topup_creem.go +++ b/controller/topup_creem.go @@ -6,16 +6,16 @@ import ( "crypto/hmac" "crypto/sha256" "encoding/hex" - "encoding/json" "errors" "fmt" + "io" + "net/http" + "time" + "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/logger" "github.com/QuantumNous/new-api/model" "github.com/QuantumNous/new-api/setting" - "io" - "net/http" - "time" "github.com/gin-gonic/gin" "github.com/thanhpk/randstr" @@ -76,7 +76,7 @@ func (*CreemAdaptor) RequestPay(c *gin.Context, req *CreemPayRequest) { // 解析产品列表 var products []CreemProduct - err := json.Unmarshal([]byte(setting.CreemProducts), &products) + err := common.Unmarshal([]byte(setting.CreemProducts), &products) if err != nil { logger.LogError(c.Request.Context(), fmt.Sprintf("Creem 产品配置解析失败 user_id=%d error=%q", c.GetInt("id"), err.Error())) c.JSON(http.StatusOK, gin.H{"message": "error", "data": "产品配置错误"}) @@ -402,7 +402,7 @@ func genCreemLink(ctx context.Context, referenceId string, product *CreemProduct } // 序列化请求数据 - jsonData, err := json.Marshal(requestData) + jsonData, err := common.Marshal(requestData) if err != nil { return "", fmt.Errorf("序列化请求数据失败: %v", err) } @@ -443,7 +443,7 @@ func genCreemLink(ctx context.Context, referenceId string, product *CreemProduct } // 解析响应 var checkoutResp CreemCheckoutResponse - err = json.Unmarshal(body, &checkoutResp) + err = common.Unmarshal(body, &checkoutResp) if err != nil { return "", fmt.Errorf("解析响应失败: %v", err) } diff --git a/controller/uptime_kuma.go b/controller/uptime_kuma.go index 2beceb426f8d..9284930922d6 100644 --- a/controller/uptime_kuma.go +++ b/controller/uptime_kuma.go @@ -2,13 +2,13 @@ package controller import ( "context" - "encoding/json" "errors" "net/http" "strconv" "strings" "time" + "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/setting/console_setting" "github.com/gin-gonic/gin" @@ -51,7 +51,7 @@ func getAndDecode(ctx context.Context, client *http.Client, url string, dest int return errors.New("non-200 status") } - return json.NewDecoder(resp.Body).Decode(dest) + return common.DecodeJson(resp.Body, dest) } func fetchGroupData(ctx context.Context, client *http.Client, groupConfig map[string]interface{}) UptimeGroupResult { diff --git a/controller/user.go b/controller/user.go index 466353a43447..a7f82de47b3a 100644 --- a/controller/user.go +++ b/controller/user.go @@ -1,7 +1,6 @@ package controller import ( - "encoding/json" "errors" "fmt" "net/http" @@ -43,7 +42,7 @@ func Login(c *gin.Context) { return } var loginRequest LoginRequest - err := json.NewDecoder(c.Request.Body).Decode(&loginRequest) + err := common.DecodeJson(c.Request.Body, &loginRequest) if err != nil { common.ApiErrorI18n(c, i18n.MsgInvalidParams) return @@ -196,7 +195,7 @@ func Register(c *gin.Context) { return } var user model.User - err := json.NewDecoder(c.Request.Body).Decode(&user) + err := common.DecodeJson(c.Request.Body, &user) if err != nil { common.ApiErrorI18n(c, i18n.MsgInvalidParams) return @@ -604,7 +603,7 @@ func generateDefaultSidebarConfig(userRole int) string { // 普通用户不包含admin区域 // 转换为JSON字符串 - configBytes, err := json.Marshal(defaultConfig) + configBytes, err := common.Marshal(defaultConfig) if err != nil { common.SysLog("生成默认边栏配置失败: " + err.Error()) return "" @@ -661,7 +660,7 @@ func GetUserModels(c *gin.Context) { func UpdateUser(c *gin.Context) { var updatedUser model.User - err := json.NewDecoder(c.Request.Body).Decode(&updatedUser) + err := common.DecodeJson(c.Request.Body, &updatedUser) if err != nil || updatedUser.Id == 0 { common.ApiErrorI18n(c, i18n.MsgInvalidParams) return @@ -836,7 +835,7 @@ func UpdateSelf(c *gin.Context) { common.ApiErrorI18n(c, i18n.MsgInvalidParams) return } - if err = common.Unmarshal(requestDataBytes, &user); err != nil { + if err := common.Unmarshal(requestDataBytes, &user); err != nil { common.ApiErrorI18n(c, i18n.MsgInvalidParams) return } @@ -962,7 +961,7 @@ func DeleteSelf(c *gin.Context) { func CreateUser(c *gin.Context) { var user model.User - err := json.NewDecoder(c.Request.Body).Decode(&user) + err := common.DecodeJson(c.Request.Body, &user) user.Username = strings.TrimSpace(user.Username) if err != nil || user.Username == "" || user.Password == "" { common.ApiErrorI18n(c, i18n.MsgInvalidParams) @@ -1044,7 +1043,7 @@ type ManageRequest struct { // ManageUser Only admin user can do this func ManageUser(c *gin.Context) { var req ManageRequest - err := json.NewDecoder(c.Request.Body).Decode(&req) + err := common.DecodeJson(c.Request.Body, &req) if err != nil { common.ApiErrorI18n(c, i18n.MsgInvalidParams) diff --git a/controller/wechat.go b/controller/wechat.go index 8889daca77db..56bed8ae34e4 100644 --- a/controller/wechat.go +++ b/controller/wechat.go @@ -1,7 +1,6 @@ package controller import ( - "encoding/json" "errors" "fmt" "net/http" @@ -40,7 +39,7 @@ func getWeChatIdByCode(code string) (string, error) { } defer httpResponse.Body.Close() var res wechatLoginResponse - err = json.NewDecoder(httpResponse.Body).Decode(&res) + err = common.DecodeJson(httpResponse.Body, &res) if err != nil { return "", err } diff --git a/dto/claude.go b/dto/claude.go index 0c552b4b2815..b048d80d6836 100644 --- a/dto/claude.go +++ b/dto/claude.go @@ -414,7 +414,7 @@ func (c *ClaudeRequest) GetTools() []any { func (c *ClaudeRequest) GetEfforts() string { var OutputConfig OutputConfigForEffort - if err := json.Unmarshal(c.OutputConfig, &OutputConfig); err == nil { + if err := common.Unmarshal(c.OutputConfig, &OutputConfig); err == nil { effort := OutputConfig.Effort return effort } diff --git a/dto/openai_request.go b/dto/openai_request.go index 3bb2b34c6456..748c517fdd9c 100644 --- a/dto/openai_request.go +++ b/dto/openai_request.go @@ -5,9 +5,10 @@ import ( "fmt" "strings" + "github.com/samber/lo" + "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/types" - "github.com/samber/lo" "github.com/gin-gonic/gin" ) @@ -467,14 +468,14 @@ func (m *Message) ParseToolCalls() []ToolCallRequest { return nil } var toolCalls []ToolCallRequest - if err := json.Unmarshal(m.ToolCalls, &toolCalls); err == nil { + if err := common.Unmarshal(m.ToolCalls, &toolCalls); err == nil { return toolCalls } return toolCalls } func (m *Message) SetToolCalls(toolCalls any) { - toolCallsJson, _ := json.Marshal(toolCalls) + toolCallsJson, _ := common.Marshal(toolCalls) m.ToolCalls = toolCallsJson } @@ -664,7 +665,7 @@ func (m *Message) ParseContent() []MediaContent { } var stringContent string - if err := json.Unmarshal(m.Content, &stringContent); err == nil { + if err := common.Unmarshal(m.Content, &stringContent); err == nil { m.parsedStringContent = &stringContent return stringContent } @@ -689,14 +690,14 @@ func (m *Message) SetNullContent() { } func (m *Message) SetStringContent(content string) { - jsonContent, _ := json.Marshal(content) + jsonContent, _ := common.Marshal(content) m.Content = jsonContent m.parsedStringContent = &content m.parsedContent = nil } func (m *Message) SetMediaContent(content []MediaContent) { - jsonContent, _ := json.Marshal(content) + jsonContent, _ := common.Marshal(content) m.Content = jsonContent m.parsedContent = nil m.parsedStringContent = nil @@ -707,7 +708,7 @@ func (m *Message) IsStringContent() bool { return true } var stringContent string - if err := json.Unmarshal(m.Content, &stringContent); err == nil { + if err := common.Unmarshal(m.Content, &stringContent); err == nil { m.parsedStringContent = &stringContent return true } @@ -723,7 +724,7 @@ func (m *Message) ParseContent() []MediaContent { // 先尝试解析为字符串 var stringContent string - if err := json.Unmarshal(m.Content, &stringContent); err == nil { + if err := common.Unmarshal(m.Content, &stringContent); err == nil { contentList = []MediaContent{{ Type: ContentTypeText, Text: stringContent, @@ -734,7 +735,7 @@ func (m *Message) ParseContent() []MediaContent { // 尝试解析为数组 var arrayContent []map[string]interface{} - if err := json.Unmarshal(m.Content, &arrayContent); err == nil { + if err := common.Unmarshal(m.Content, &arrayContent); err == nil { for _, contentItem := range arrayContent { contentType, ok := contentItem["type"].(string) if !ok { diff --git a/middleware/jimeng_adapter.go b/middleware/jimeng_adapter.go index 3e3dd7ae52e0..08639ca4c822 100644 --- a/middleware/jimeng_adapter.go +++ b/middleware/jimeng_adapter.go @@ -2,14 +2,14 @@ package middleware import ( "bytes" - "encoding/json" "io" "net/http" + "github.com/gin-gonic/gin" + "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/constant" relayconstant "github.com/QuantumNous/new-api/relay/constant" - "github.com/gin-gonic/gin" ) func JimengRequestConvert() func(c *gin.Context) { @@ -35,7 +35,7 @@ func JimengRequestConvert() func(c *gin.Context) { "metadata": originalReq, } - jsonData, err := json.Marshal(unifiedReq) + jsonData, err := common.Marshal(unifiedReq) if err != nil { abortWithOpenAiMessage(c, http.StatusInternalServerError, "Failed to marshal request body") return diff --git a/middleware/kling_adapter.go b/middleware/kling_adapter.go index e200379c0c34..4492ce732a96 100644 --- a/middleware/kling_adapter.go +++ b/middleware/kling_adapter.go @@ -2,7 +2,6 @@ package middleware import ( "bytes" - "encoding/json" "io" "github.com/QuantumNous/new-api/common" @@ -32,7 +31,7 @@ func KlingRequestConvert() func(c *gin.Context) { "metadata": originalReq, } - jsonData, err := json.Marshal(unifiedReq) + jsonData, err := common.Marshal(unifiedReq) if err != nil { c.Next() return diff --git a/middleware/turnstile-check.go b/middleware/turnstile-check.go index af87fad4423c..71b946783544 100644 --- a/middleware/turnstile-check.go +++ b/middleware/turnstile-check.go @@ -1,13 +1,13 @@ package middleware import ( - "encoding/json" "net/http" "net/url" - "github.com/QuantumNous/new-api/common" "github.com/gin-contrib/sessions" "github.com/gin-gonic/gin" + + "github.com/QuantumNous/new-api/common" ) type turnstileCheckResponse struct { @@ -48,7 +48,7 @@ func TurnstileCheck() gin.HandlerFunc { } defer rawRes.Body.Close() var res turnstileCheckResponse - err = json.NewDecoder(rawRes.Body).Decode(&res) + err = common.DecodeJson(rawRes.Body, &res) if err != nil { common.SysLog(err.Error()) c.JSON(http.StatusOK, gin.H{ diff --git a/model/channel.go b/model/channel.go index 7326f28c6196..2e39babae93d 100644 --- a/model/channel.go +++ b/model/channel.go @@ -316,7 +316,7 @@ func (channel *Channel) GetOtherInfo() map[string]interface{} { } func (channel *Channel) SetOtherInfo(otherInfo map[string]interface{}) { - otherInfoBytes, err := json.Marshal(otherInfo) + otherInfoBytes, err := common.Marshal(otherInfo) if err != nil { common.SysLog(fmt.Sprintf("failed to marshal other info: channel_id=%d, tag=%s, name=%s, error=%v", channel.Id, channel.GetTag(), channel.Name, err)) return diff --git a/model/passkey.go b/model/passkey.go index 5d2595cf8aaa..a42dcb4cf83f 100644 --- a/model/passkey.go +++ b/model/passkey.go @@ -2,7 +2,6 @@ package model import ( "encoding/base64" - "encoding/json" "errors" "fmt" "strings" @@ -46,7 +45,7 @@ func (p *PasskeyCredential) TransportList() []protocol.AuthenticatorTransport { return nil } var transports []string - if err := json.Unmarshal([]byte(p.Transports), &transports); err != nil { + if err := common.Unmarshal([]byte(p.Transports), &transports); err != nil { return nil } result := make([]protocol.AuthenticatorTransport, 0, len(transports)) @@ -65,7 +64,7 @@ func (p *PasskeyCredential) SetTransports(list []protocol.AuthenticatorTransport for i, transport := range list { stringList[i] = string(transport) } - encoded, err := json.Marshal(stringList) + encoded, err := common.Marshal(stringList) if err != nil { return } diff --git a/model/prefill_group.go b/model/prefill_group.go index cc2e64da992e..0d7738a3d4b7 100644 --- a/model/prefill_group.go +++ b/model/prefill_group.go @@ -44,7 +44,7 @@ func (j *JSONValue) Scan(value interface{}) error { return nil default: // 其他类型尝试序列化为 JSON - b, err := json.Marshal(v) + b, err := common.Marshal(v) if err != nil { return err } diff --git a/oauth/discord.go b/oauth/discord.go index b626d2f82e5e..ee4fb1d50dc5 100644 --- a/oauth/discord.go +++ b/oauth/discord.go @@ -2,18 +2,19 @@ package oauth import ( "context" - "encoding/json" "fmt" "net/http" "net/url" "strings" "time" + "github.com/gin-gonic/gin" + + "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/i18n" "github.com/QuantumNous/new-api/logger" "github.com/QuantumNous/new-api/model" "github.com/QuantumNous/new-api/setting/system_setting" - "github.com/gin-gonic/gin" ) func init() { @@ -84,7 +85,7 @@ func (p *DiscordProvider) ExchangeToken(ctx context.Context, code string, c *gin logger.LogDebug(ctx, "[OAuth-Discord] ExchangeToken response status: %d", res.StatusCode) var discordResponse discordOAuthResponse - err = json.NewDecoder(res.Body).Decode(&discordResponse) + err = common.DecodeJson(res.Body, &discordResponse) if err != nil { logger.LogError(ctx, fmt.Sprintf("[OAuth-Discord] ExchangeToken decode error: %s", err.Error())) return nil, err @@ -134,7 +135,7 @@ func (p *DiscordProvider) GetUserInfo(ctx context.Context, token *OAuthToken) (* } var discordUser discordUser - err = json.NewDecoder(res.Body).Decode(&discordUser) + err = common.DecodeJson(res.Body, &discordUser) if err != nil { logger.LogError(ctx, fmt.Sprintf("[OAuth-Discord] GetUserInfo decode error: %s", err.Error())) return nil, err diff --git a/oauth/github.go b/oauth/github.go index 314118a3765c..48f24ecbdb7a 100644 --- a/oauth/github.go +++ b/oauth/github.go @@ -3,18 +3,18 @@ package oauth import ( "bytes" "context" - "encoding/json" "fmt" "io" "net/http" "strconv" "time" + "github.com/gin-gonic/gin" + "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/i18n" "github.com/QuantumNous/new-api/logger" "github.com/QuantumNous/new-api/model" - "github.com/gin-gonic/gin" ) func init() { @@ -57,7 +57,7 @@ func (p *GitHubProvider) ExchangeToken(ctx context.Context, code string, c *gin. "client_secret": common.GitHubClientSecret, "code": code, } - jsonData, err := json.Marshal(values) + jsonData, err := common.Marshal(values) if err != nil { return nil, err } @@ -82,7 +82,7 @@ func (p *GitHubProvider) ExchangeToken(ctx context.Context, code string, c *gin. logger.LogDebug(ctx, "[OAuth-GitHub] ExchangeToken response status: %d", res.StatusCode) var oAuthResponse gitHubOAuthResponse - err = json.NewDecoder(res.Body).Decode(&oAuthResponse) + err = common.DecodeJson(res.Body, &oAuthResponse) if err != nil { logger.LogError(ctx, fmt.Sprintf("[OAuth-GitHub] ExchangeToken decode error: %s", err.Error())) return nil, err @@ -135,7 +135,7 @@ func (p *GitHubProvider) GetUserInfo(ctx context.Context, token *OAuthToken) (*O } var githubUser gitHubUser - err = json.NewDecoder(res.Body).Decode(&githubUser) + err = common.DecodeJson(res.Body, &githubUser) if err != nil { logger.LogError(ctx, fmt.Sprintf("[OAuth-GitHub] GetUserInfo decode error: %s", err.Error())) return nil, err diff --git a/oauth/linuxdo.go b/oauth/linuxdo.go index 1ed91e00999c..d17ab368b9ea 100644 --- a/oauth/linuxdo.go +++ b/oauth/linuxdo.go @@ -3,7 +3,6 @@ package oauth import ( "context" "encoding/base64" - "encoding/json" "fmt" "net/http" "net/url" @@ -11,11 +10,12 @@ import ( "strings" "time" + "github.com/gin-gonic/gin" + "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/i18n" "github.com/QuantumNous/new-api/logger" "github.com/QuantumNous/new-api/model" - "github.com/gin-gonic/gin" ) func init() { @@ -90,7 +90,7 @@ func (p *LinuxDOProvider) ExchangeToken(ctx context.Context, code string, c *gin AccessToken string `json:"access_token"` Message string `json:"message"` } - if err := json.NewDecoder(res.Body).Decode(&tokenRes); err != nil { + if err := common.DecodeJson(res.Body, &tokenRes); err != nil { logger.LogError(ctx, fmt.Sprintf("[OAuth-LinuxDO] ExchangeToken decode error: %s", err.Error())) return nil, err } @@ -130,7 +130,7 @@ func (p *LinuxDOProvider) GetUserInfo(ctx context.Context, token *OAuthToken) (* logger.LogDebug(ctx, "[OAuth-LinuxDO] GetUserInfo response status: %d", res.StatusCode) var linuxdoUser linuxdoUser - if err := json.NewDecoder(res.Body).Decode(&linuxdoUser); err != nil { + if err := common.DecodeJson(res.Body, &linuxdoUser); err != nil { logger.LogError(ctx, fmt.Sprintf("[OAuth-LinuxDO] GetUserInfo decode error: %s", err.Error())) return nil, err } diff --git a/oauth/oidc.go b/oauth/oidc.go index 9bdc6d01e572..64eccf234fc6 100644 --- a/oauth/oidc.go +++ b/oauth/oidc.go @@ -2,18 +2,19 @@ package oauth import ( "context" - "encoding/json" "fmt" "net/http" "net/url" "strings" "time" + "github.com/gin-gonic/gin" + + "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/i18n" "github.com/QuantumNous/new-api/logger" "github.com/QuantumNous/new-api/model" "github.com/QuantumNous/new-api/setting/system_setting" - "github.com/gin-gonic/gin" ) func init() { @@ -86,7 +87,7 @@ func (p *OIDCProvider) ExchangeToken(ctx context.Context, code string, c *gin.Co logger.LogDebug(ctx, "[OAuth-OIDC] ExchangeToken response status: %d", res.StatusCode) var oidcResponse oidcOAuthResponse - err = json.NewDecoder(res.Body).Decode(&oidcResponse) + err = common.DecodeJson(res.Body, &oidcResponse) if err != nil { logger.LogError(ctx, fmt.Sprintf("[OAuth-OIDC] ExchangeToken decode error: %s", err.Error())) return nil, err @@ -138,7 +139,7 @@ func (p *OIDCProvider) GetUserInfo(ctx context.Context, token *OAuthToken) (*OAu } var oidcUser oidcUser - err = json.NewDecoder(res.Body).Decode(&oidcUser) + err = common.DecodeJson(res.Body, &oidcUser) if err != nil { logger.LogError(ctx, fmt.Sprintf("[OAuth-OIDC] GetUserInfo decode error: %s", err.Error())) return nil, err diff --git a/pkg/cachex/codec.go b/pkg/cachex/codec.go index 2e4957a84864..747adc730a9b 100644 --- a/pkg/cachex/codec.go +++ b/pkg/cachex/codec.go @@ -1,10 +1,11 @@ package cachex import ( - "encoding/json" "fmt" "strconv" "strings" + + "github.com/QuantumNous/new-api/common" ) type ValueCodec[V any] interface { @@ -34,7 +35,7 @@ func (c StringCodec) Decode(s string) (string, error) { return s, nil } type JSONCodec[V any] struct{} func (c JSONCodec[V]) Encode(v V) (string, error) { - b, err := json.Marshal(v) + b, err := common.Marshal(v) if err != nil { return "", err } @@ -46,7 +47,7 @@ func (c JSONCodec[V]) Decode(s string) (V, error) { if strings.TrimSpace(s) == "" { return v, fmt.Errorf("empty json value") } - if err := json.Unmarshal([]byte(s), &v); err != nil { + if err := common.Unmarshal([]byte(s), &v); err != nil { return v, err } return v, nil diff --git a/pkg/ionet/client.go b/pkg/ionet/client.go index e53947570c96..d36b63b04b46 100644 --- a/pkg/ionet/client.go +++ b/pkg/ionet/client.go @@ -2,12 +2,13 @@ package ionet import ( "bytes" - "encoding/json" "fmt" "net/http" "net/url" "strconv" "time" + + "github.com/QuantumNous/new-api/common" ) const ( @@ -101,7 +102,7 @@ func (c *Client) makeRequest(method, endpoint string, body interface{}) (*HTTPRe var err error if body != nil { - reqBody, err = json.Marshal(body) + reqBody, err = common.Marshal(body) if err != nil { return nil, fmt.Errorf("failed to marshal request body: %w", err) } @@ -132,7 +133,7 @@ func (c *Client) makeRequest(method, endpoint string, body interface{}) (*HTTPRe var errorResp struct { Detail string `json:"detail"` } - if err := json.Unmarshal(resp.Body, &errorResp); err == nil && errorResp.Detail != "" { + if err := common.Unmarshal(resp.Body, &errorResp); err == nil && errorResp.Detail != "" { apiErr = APIError{ Code: resp.StatusCode, Message: errorResp.Detail, @@ -197,13 +198,13 @@ func buildQueryParams(params map[string]interface{}) string { } case []int: if len(v) > 0 { - if encoded, err := json.Marshal(v); err == nil { + if encoded, err := common.Marshal(v); err == nil { values.Add(key, string(encoded)) } } case []string: if len(v) > 0 { - if encoded, err := json.Marshal(v); err == nil { + if encoded, err := common.Marshal(v); err == nil { values.Add(key, string(encoded)) } } diff --git a/pkg/ionet/container.go b/pkg/ionet/container.go index 805a3b162070..ed393567f431 100644 --- a/pkg/ionet/container.go +++ b/pkg/ionet/container.go @@ -1,12 +1,13 @@ package ionet import ( - "encoding/json" "fmt" "strings" "time" "github.com/samber/lo" + + "github.com/QuantumNous/new-api/common" ) // ListContainers retrieves all containers for a specific deployment @@ -290,7 +291,7 @@ func (c *Client) ExecuteInContainer(deploymentID, containerID string, command [] } var result map[string]interface{} - if err := json.Unmarshal(resp.Body, &result); err != nil { + if err := common.Unmarshal(resp.Body, &result); err != nil { return "", fmt.Errorf("failed to parse execution result: %w", err) } diff --git a/pkg/ionet/deployment.go b/pkg/ionet/deployment.go index 36597399b98c..29a2ada3b89e 100644 --- a/pkg/ionet/deployment.go +++ b/pkg/ionet/deployment.go @@ -1,11 +1,12 @@ package ionet import ( - "encoding/json" "fmt" "strings" "github.com/samber/lo" + + "github.com/QuantumNous/new-api/common" ) // DeployContainer deploys a new container with the specified configuration @@ -45,7 +46,7 @@ func (c *Client) DeployContainer(req *DeploymentRequest) (*DeploymentResponse, e // API returns direct format: // {"status": "string", "deployment_id": "..."} var deployResp DeploymentResponse - if err := json.Unmarshal(resp.Body, &deployResp); err != nil { + if err := common.Unmarshal(resp.Body, &deployResp); err != nil { return nil, fmt.Errorf("failed to parse deployment response: %w", err) } @@ -126,7 +127,7 @@ func (c *Client) UpdateDeployment(deploymentID string, req *UpdateDeploymentRequ // API returns direct format: // {"status": "string", "deployment_id": "..."} var updateResp UpdateDeploymentResponse - if err := json.Unmarshal(resp.Body, &updateResp); err != nil { + if err := common.Unmarshal(resp.Body, &updateResp); err != nil { return nil, fmt.Errorf("failed to parse update deployment response: %w", err) } @@ -176,7 +177,7 @@ func (c *Client) DeleteDeployment(deploymentID string) (*UpdateDeploymentRespons // API returns direct format: // {"status": "string", "deployment_id": "..."} var deleteResp UpdateDeploymentResponse - if err := json.Unmarshal(resp.Body, &deleteResp); err != nil { + if err := common.Unmarshal(resp.Body, &deleteResp); err != nil { return nil, fmt.Errorf("failed to parse delete deployment response: %w", err) } @@ -341,7 +342,7 @@ func (c *Client) CheckClusterNameAvailability(clusterName string) (bool, error) } var availabilityResp bool - if err := json.Unmarshal(resp.Body, &availabilityResp); err != nil { + if err := common.Unmarshal(resp.Body, &availabilityResp); err != nil { return false, fmt.Errorf("failed to parse cluster name availability response: %w", err) } @@ -369,7 +370,7 @@ func (c *Client) UpdateClusterName(clusterID string, req *UpdateClusterNameReque // Parse the response directly without data wrapper based on API docs var updateResp UpdateClusterNameResponse - if err := json.Unmarshal(resp.Body, &updateResp); err != nil { + if err := common.Unmarshal(resp.Body, &updateResp); err != nil { return nil, fmt.Errorf("failed to parse update cluster name response: %w", err) } diff --git a/pkg/ionet/hardware.go b/pkg/ionet/hardware.go index 54ccdb886fea..4f3f6c42c415 100644 --- a/pkg/ionet/hardware.go +++ b/pkg/ionet/hardware.go @@ -1,11 +1,12 @@ package ionet import ( - "encoding/json" "fmt" "strings" "github.com/samber/lo" + + "github.com/QuantumNous/new-api/common" ) // GetAvailableReplicas retrieves available replicas per location for specified hardware @@ -150,7 +151,7 @@ func (c *Client) GetHardwareType(hardwareID int) (*HardwareType, error) { // API response format not documented, assuming direct format var hardwareType HardwareType - if err := json.Unmarshal(resp.Body, &hardwareType); err != nil { + if err := common.Unmarshal(resp.Body, &hardwareType); err != nil { return nil, fmt.Errorf("failed to parse hardware type: %w", err) } @@ -172,7 +173,7 @@ func (c *Client) GetLocation(locationID int) (*Location, error) { // API response format not documented, assuming direct format var location Location - if err := json.Unmarshal(resp.Body, &location); err != nil { + if err := common.Unmarshal(resp.Body, &location); err != nil { return nil, fmt.Errorf("failed to parse location: %w", err) } @@ -194,7 +195,7 @@ func (c *Client) GetLocationAvailability(locationID int) (*LocationAvailability, // API response format not documented, assuming direct format var availability LocationAvailability - if err := json.Unmarshal(resp.Body, &availability); err != nil { + if err := common.Unmarshal(resp.Body, &availability); err != nil { return nil, fmt.Errorf("failed to parse location availability: %w", err) } diff --git a/pkg/ionet/jsonutil.go b/pkg/ionet/jsonutil.go index 0b3219cfe00b..9437b2762aaf 100644 --- a/pkg/ionet/jsonutil.go +++ b/pkg/ionet/jsonutil.go @@ -1,35 +1,36 @@ package ionet import ( - "encoding/json" "strings" "time" "github.com/samber/lo" + + "github.com/QuantumNous/new-api/common" ) // decodeWithFlexibleTimes unmarshals API responses while tolerating timestamp strings // that omit timezone information by normalizing them to RFC3339Nano. func decodeWithFlexibleTimes(data []byte, target interface{}) error { var intermediate interface{} - if err := json.Unmarshal(data, &intermediate); err != nil { + if err := common.Unmarshal(data, &intermediate); err != nil { return err } normalized := normalizeTimeValues(intermediate) - reencoded, err := json.Marshal(normalized) + reencoded, err := common.Marshal(normalized) if err != nil { return err } - return json.Unmarshal(reencoded, target) + return common.Unmarshal(reencoded, target) } func decodeData[T any](data []byte, target *T) error { var wrapper struct { Data T `json:"data"` } - if err := json.Unmarshal(data, &wrapper); err != nil { + if err := common.Unmarshal(data, &wrapper); err != nil { return err } *target = wrapper.Data diff --git a/relay/channel/ali/rerank.go b/relay/channel/ali/rerank.go index 1f7a3451fbac..6e0afedd50fa 100644 --- a/relay/channel/ali/rerank.go +++ b/relay/channel/ali/rerank.go @@ -1,10 +1,10 @@ package ali import ( - "encoding/json" "io" "net/http" + "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/dto" relaycommon "github.com/QuantumNous/new-api/relay/common" "github.com/QuantumNous/new-api/service" @@ -40,7 +40,7 @@ func RerankHandler(c *gin.Context, resp *http.Response, info *relaycommon.RelayI service.CloseResponseBodyGracefully(resp) var aliResponse AliRerankResponse - err = json.Unmarshal(responseBody, &aliResponse) + err = common.Unmarshal(responseBody, &aliResponse) if err != nil { return types.NewOpenAIError(err, types.ErrorCodeBadResponseBody, http.StatusInternalServerError), nil } @@ -64,7 +64,7 @@ func RerankHandler(c *gin.Context, resp *http.Response, info *relaycommon.RelayI Usage: usage, } - jsonResponse, err := json.Marshal(rerankResponse) + jsonResponse, err := common.Marshal(rerankResponse) if err != nil { return types.NewError(err, types.ErrorCodeBadResponseBody), nil } diff --git a/relay/channel/aws/dto.go b/relay/channel/aws/dto.go index 84facba108a8..a4ebdb3b40d9 100644 --- a/relay/channel/aws/dto.go +++ b/relay/channel/aws/dto.go @@ -45,7 +45,7 @@ func formatRequest(requestBody io.Reader, requestHeader http.Header) (*AwsClaude var tempArray []string tempArray = strings.Split(anthropicBetaValues, ",") if len(tempArray) > 0 { - betaJson, err := json.Marshal(tempArray) + betaJson, err := common.Marshal(tempArray) if err != nil { return nil, err } diff --git a/relay/channel/aws/relay-aws.go b/relay/channel/aws/relay-aws.go index 1f6ff7e69263..c009071f6eb1 100644 --- a/relay/channel/aws/relay-aws.go +++ b/relay/channel/aws/relay-aws.go @@ -2,7 +2,6 @@ package aws import ( "context" - "encoding/json" "fmt" "io" "net/http" @@ -21,12 +20,13 @@ import ( "github.com/gin-gonic/gin" "github.com/pkg/errors" - "github.com/QuantumNous/new-api/setting/model_setting" "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/credentials" "github.com/aws/aws-sdk-go-v2/service/bedrockruntime" bedrockruntimeTypes "github.com/aws/aws-sdk-go-v2/service/bedrockruntime/types" "github.com/aws/smithy-go/auth/bearer" + + "github.com/QuantumNous/new-api/setting/model_setting" ) // getAwsErrorStatusCode extracts HTTP status code from AWS SDK error @@ -321,7 +321,7 @@ func handleNovaRequest(c *gin.Context, info *relaycommon.RelayInfo, a *Adaptor) } `json:"usage"` } - if err := json.Unmarshal(awsResp.Body, &novaResp); err != nil { + if err := common.Unmarshal(awsResp.Body, &novaResp); err != nil { return types.NewError(errors.Wrap(err, "unmarshal nova response"), types.ErrorCodeBadResponseBody), nil } diff --git a/relay/channel/baidu/relay-baidu.go b/relay/channel/baidu/relay-baidu.go index a76d7689c51e..12719c991c6e 100644 --- a/relay/channel/baidu/relay-baidu.go +++ b/relay/channel/baidu/relay-baidu.go @@ -1,7 +1,6 @@ package baidu import ( - "encoding/json" "errors" "fmt" "io" @@ -10,6 +9,8 @@ import ( "sync" "time" + "github.com/samber/lo" + "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/constant" "github.com/QuantumNous/new-api/dto" @@ -17,7 +18,6 @@ import ( "github.com/QuantumNous/new-api/relay/helper" "github.com/QuantumNous/new-api/service" "github.com/QuantumNous/new-api/types" - "github.com/samber/lo" "github.com/gin-gonic/gin" ) @@ -145,7 +145,7 @@ func baiduHandler(c *gin.Context, info *relaycommon.RelayInfo, resp *http.Respon return types.NewError(err, types.ErrorCodeBadResponseBody), nil } service.CloseResponseBodyGracefully(resp) - err = json.Unmarshal(responseBody, &baiduResponse) + err = common.Unmarshal(responseBody, &baiduResponse) if err != nil { return types.NewError(err, types.ErrorCodeBadResponseBody), nil } @@ -153,7 +153,7 @@ func baiduHandler(c *gin.Context, info *relaycommon.RelayInfo, resp *http.Respon return types.NewError(fmt.Errorf("%s", baiduResponse.ErrorMsg), types.ErrorCodeBadResponseBody), nil } fullTextResponse := responseBaidu2OpenAI(&baiduResponse) - jsonResponse, err := json.Marshal(fullTextResponse) + jsonResponse, err := common.Marshal(fullTextResponse) if err != nil { return types.NewError(err, types.ErrorCodeBadResponseBody), nil } @@ -170,7 +170,7 @@ func baiduEmbeddingHandler(c *gin.Context, info *relaycommon.RelayInfo, resp *ht return types.NewError(err, types.ErrorCodeBadResponseBody), nil } service.CloseResponseBodyGracefully(resp) - err = json.Unmarshal(responseBody, &baiduResponse) + err = common.Unmarshal(responseBody, &baiduResponse) if err != nil { return types.NewError(err, types.ErrorCodeBadResponseBody), nil } @@ -178,7 +178,7 @@ func baiduEmbeddingHandler(c *gin.Context, info *relaycommon.RelayInfo, resp *ht return types.NewError(fmt.Errorf("%s", baiduResponse.ErrorMsg), types.ErrorCodeBadResponseBody), nil } fullTextResponse := embeddingResponseBaidu2OpenAI(&baiduResponse) - jsonResponse, err := json.Marshal(fullTextResponse) + jsonResponse, err := common.Marshal(fullTextResponse) if err != nil { return types.NewError(err, types.ErrorCodeBadResponseBody), nil } @@ -230,7 +230,7 @@ func getBaiduAccessTokenHelper(apiKey string) (*BaiduAccessToken, error) { defer res.Body.Close() var accessToken BaiduAccessToken - err = json.NewDecoder(res.Body).Decode(&accessToken) + err = common.DecodeJson(res.Body, &accessToken) if err != nil { return nil, err } diff --git a/relay/channel/cloudflare/relay_cloudflare.go b/relay/channel/cloudflare/relay_cloudflare.go index 589ff1269689..3abfa53f34b7 100644 --- a/relay/channel/cloudflare/relay_cloudflare.go +++ b/relay/channel/cloudflare/relay_cloudflare.go @@ -2,19 +2,20 @@ package cloudflare import ( "bufio" - "encoding/json" "io" "net/http" "strings" "time" + "github.com/samber/lo" + + "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/logger" relaycommon "github.com/QuantumNous/new-api/relay/common" "github.com/QuantumNous/new-api/relay/helper" "github.com/QuantumNous/new-api/service" "github.com/QuantumNous/new-api/types" - "github.com/samber/lo" "github.com/gin-gonic/gin" ) @@ -51,7 +52,7 @@ func cfStreamHandler(c *gin.Context, info *relaycommon.RelayInfo, resp *http.Res } var response dto.ChatCompletionsStreamResponse - err := json.Unmarshal([]byte(data), &response) + err := common.Unmarshal([]byte(data), &response) if err != nil { logger.LogError(c, "error_unmarshalling_stream_response: "+err.Error()) continue @@ -97,7 +98,7 @@ func cfHandler(c *gin.Context, info *relaycommon.RelayInfo, resp *http.Response) } service.CloseResponseBodyGracefully(resp) var response dto.TextResponse - err = json.Unmarshal(responseBody, &response) + err = common.Unmarshal(responseBody, &response) if err != nil { return types.NewError(err, types.ErrorCodeBadResponseBody), nil } @@ -109,7 +110,7 @@ func cfHandler(c *gin.Context, info *relaycommon.RelayInfo, resp *http.Response) usage := service.ResponseText2Usage(c, responseText, info.UpstreamModelName, info.GetEstimatePromptTokens()) response.Usage = *usage response.Id = helper.GetResponseID(c) - jsonResponse, err := json.Marshal(response) + jsonResponse, err := common.Marshal(response) if err != nil { return types.NewError(err, types.ErrorCodeBadResponseBody), nil } @@ -126,7 +127,7 @@ func cfSTTHandler(c *gin.Context, info *relaycommon.RelayInfo, resp *http.Respon return types.NewError(err, types.ErrorCodeBadResponseBody), nil } service.CloseResponseBodyGracefully(resp) - err = json.Unmarshal(responseBody, &cfResp) + err = common.Unmarshal(responseBody, &cfResp) if err != nil { return types.NewError(err, types.ErrorCodeBadResponseBody), nil } @@ -135,7 +136,7 @@ func cfSTTHandler(c *gin.Context, info *relaycommon.RelayInfo, resp *http.Respon Text: cfResp.Result.Text, } - jsonResponse, err := json.Marshal(audioResp) + jsonResponse, err := common.Marshal(audioResp) if err != nil { return types.NewError(err, types.ErrorCodeBadResponseBody), nil } diff --git a/relay/channel/cohere/relay-cohere.go b/relay/channel/cohere/relay-cohere.go index 7b47789f869d..ae29cb58f0ba 100644 --- a/relay/channel/cohere/relay-cohere.go +++ b/relay/channel/cohere/relay-cohere.go @@ -1,7 +1,6 @@ package cohere import ( - "encoding/json" "io" "net/http" "strings" @@ -121,7 +120,7 @@ func cohereStreamHandler(c *gin.Context, info *relaycommon.RelayInfo, resp *http } data = strings.TrimSuffix(data, "\r") var cohereResp CohereResponse - err := json.Unmarshal([]byte(data), &cohereResp) + err := common.Unmarshal([]byte(data), &cohereResp) if err != nil { common.SysLog("error unmarshalling stream response: " + err.Error()) return true @@ -156,7 +155,7 @@ func cohereStreamHandler(c *gin.Context, info *relaycommon.RelayInfo, resp *http } responseText += cohereResp.Text } - jsonStr, err := json.Marshal(openaiResp) + jsonStr, err := common.Marshal(openaiResp) if err != nil { common.SysLog("error marshalling stream response: " + err.Error()) return true @@ -182,7 +181,7 @@ func cohereHandler(c *gin.Context, info *relaycommon.RelayInfo, resp *http.Respo } service.CloseResponseBodyGracefully(resp) var cohereResp CohereResponseResult - err = json.Unmarshal(responseBody, &cohereResp) + err = common.Unmarshal(responseBody, &cohereResp) if err != nil { return nil, types.NewError(err, types.ErrorCodeBadResponseBody) } @@ -206,7 +205,7 @@ func cohereHandler(c *gin.Context, info *relaycommon.RelayInfo, resp *http.Respo }, } - jsonResponse, err := json.Marshal(openaiResp) + jsonResponse, err := common.Marshal(openaiResp) if err != nil { return nil, types.NewError(err, types.ErrorCodeBadResponseBody) } @@ -223,7 +222,7 @@ func cohereRerankHandler(c *gin.Context, resp *http.Response, info *relaycommon. } service.CloseResponseBodyGracefully(resp) var cohereResp CohereRerankResponseResult - err = json.Unmarshal(responseBody, &cohereResp) + err = common.Unmarshal(responseBody, &cohereResp) if err != nil { return nil, types.NewError(err, types.ErrorCodeBadResponseBody) } @@ -242,7 +241,7 @@ func cohereRerankHandler(c *gin.Context, resp *http.Response, info *relaycommon. rerankResp.Results = cohereResp.Results rerankResp.Usage = usage - jsonResponse, err := json.Marshal(rerankResp) + jsonResponse, err := common.Marshal(rerankResp) if err != nil { return nil, types.NewError(err, types.ErrorCodeBadResponseBody) } diff --git a/relay/channel/coze/adaptor.go b/relay/channel/coze/adaptor.go index 30f229a31ee5..5ac562126fd7 100644 --- a/relay/channel/coze/adaptor.go +++ b/relay/channel/coze/adaptor.go @@ -1,13 +1,13 @@ package coze import ( - "encoding/json" "errors" "fmt" "io" "net/http" "time" + commonpkg "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/relay/channel" "github.com/QuantumNous/new-api/relay/common" @@ -79,7 +79,7 @@ func (a *Adaptor) DoRequest(c *gin.Context, info *common.RelayInfo, requestBody if err != nil { return nil, err } - err = json.Unmarshal(respBody, &cozeResponse) + err = commonpkg.Unmarshal(respBody, &cozeResponse) if cozeResponse.Code != 0 { return nil, errors.New(cozeResponse.Msg) } diff --git a/relay/channel/coze/relay-coze.go b/relay/channel/coze/relay-coze.go index c2db5c609cde..3271332151a2 100644 --- a/relay/channel/coze/relay-coze.go +++ b/relay/channel/coze/relay-coze.go @@ -9,13 +9,14 @@ import ( "net/http" "strings" + "github.com/samber/lo" + "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/dto" relaycommon "github.com/QuantumNous/new-api/relay/common" "github.com/QuantumNous/new-api/relay/helper" "github.com/QuantumNous/new-api/service" "github.com/QuantumNous/new-api/types" - "github.com/samber/lo" "github.com/gin-gonic/gin" ) @@ -56,7 +57,7 @@ func cozeChatHandler(c *gin.Context, info *relaycommon.RelayInfo, resp *http.Res var response dto.TextResponse var cozeResponse CozeChatDetailResponse response.Model = info.UpstreamModelName - err = json.Unmarshal(responseBody, &cozeResponse) + err = common.Unmarshal(responseBody, &cozeResponse) if err != nil { return nil, types.NewError(err, types.ErrorCodeBadResponseBody) } @@ -86,7 +87,7 @@ func cozeChatHandler(c *gin.Context, info *relaycommon.RelayInfo, resp *http.Res FinishReason: "stop", }, } - jsonResponse, err := json.Marshal(response) + jsonResponse, err := common.Marshal(response) if err != nil { return nil, types.NewError(err, types.ErrorCodeBadResponseBody) } @@ -154,7 +155,7 @@ func handleCozeEvent(c *gin.Context, event string, data string, responseText *st case "conversation.chat.completed": // 将 data 解析为 CozeChatResponseData var chatData CozeChatResponseData - err := json.Unmarshal([]byte(data), &chatData) + err := common.Unmarshal([]byte(data), &chatData) if err != nil { common.SysLog("error_unmarshalling_stream_response: " + err.Error()) return @@ -171,14 +172,14 @@ func handleCozeEvent(c *gin.Context, event string, data string, responseText *st case "conversation.message.delta": // 将 data 解析为 CozeChatV3MessageDetail var messageData CozeChatV3MessageDetail - err := json.Unmarshal([]byte(data), &messageData) + err := common.Unmarshal([]byte(data), &messageData) if err != nil { common.SysLog("error_unmarshalling_stream_response: " + err.Error()) return } var content string - err = json.Unmarshal(messageData.Content, &content) + err = common.Unmarshal(messageData.Content, &content) if err != nil { common.SysLog("error_unmarshalling_stream_response: " + err.Error()) return @@ -203,7 +204,7 @@ func handleCozeEvent(c *gin.Context, event string, data string, responseText *st case "error": var errorData CozeError - err := json.Unmarshal([]byte(data), &errorData) + err := common.Unmarshal([]byte(data), &errorData) if err != nil { common.SysLog("error_unmarshalling_stream_response: " + err.Error()) return @@ -242,7 +243,7 @@ func checkIfChatComplete(a *Adaptor, c *gin.Context, info *relaycommon.RelayInfo if err != nil { return fmt.Errorf("read response body failed: %w", err), false } - err = json.Unmarshal(responseBody, &cozeResponse) + err = common.Unmarshal(responseBody, &cozeResponse) if err != nil { return fmt.Errorf("unmarshal response body failed: %w", err), false } diff --git a/relay/channel/dify/relay-dify.go b/relay/channel/dify/relay-dify.go index 263ad66837b6..743a3996e84a 100644 --- a/relay/channel/dify/relay-dify.go +++ b/relay/channel/dify/relay-dify.go @@ -11,6 +11,8 @@ import ( "os" "strings" + "github.com/samber/lo" + "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/constant" "github.com/QuantumNous/new-api/dto" @@ -18,7 +20,6 @@ import ( "github.com/QuantumNous/new-api/relay/helper" "github.com/QuantumNous/new-api/service" "github.com/QuantumNous/new-api/types" - "github.com/samber/lo" "github.com/gin-gonic/gin" ) @@ -110,7 +111,7 @@ func uploadDifyFile(c *gin.Context, info *relaycommon.RelayInfo, user string, me var result struct { Id string `json:"id"` } - if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { + if err := common.DecodeJson(resp.Body, &result); err != nil { common.SysLog("failed to decode response: " + err.Error()) return nil } @@ -135,7 +136,7 @@ func requestOpenAI2Dify(c *gin.Context, info *relaycommon.RelayInfo, request dto user = json.RawMessage(helper.GetResponseID(c)) } var stringUser string - err := json.Unmarshal(user, &stringUser) + err := common.Unmarshal(user, &stringUser) if err != nil { common.SysLog("failed to unmarshal user: " + err.Error()) stringUser = helper.GetResponseID(c) @@ -230,7 +231,7 @@ func difyStreamHandler(c *gin.Context, info *relaycommon.RelayInfo, resp *http.R helper.SetEventStreamHeaders(c) helper.StreamScannerHandler(c, resp, info, func(data string, sr *helper.StreamResult) { var difyResponse DifyChunkChatCompletionResponse - if err := json.Unmarshal([]byte(data), &difyResponse); err != nil { + if err := common.Unmarshal([]byte(data), &difyResponse); err != nil { common.SysLog("error unmarshalling stream response: " + err.Error()) sr.Error(err) return @@ -271,7 +272,7 @@ func difyHandler(c *gin.Context, info *relaycommon.RelayInfo, resp *http.Respons return nil, types.NewError(err, types.ErrorCodeBadResponseBody) } service.CloseResponseBodyGracefully(resp) - err = json.Unmarshal(responseBody, &difyResponse) + err = common.Unmarshal(responseBody, &difyResponse) if err != nil { return nil, types.NewError(err, types.ErrorCodeBadResponseBody) } @@ -290,7 +291,7 @@ func difyHandler(c *gin.Context, info *relaycommon.RelayInfo, resp *http.Respons FinishReason: "stop", } fullTextResponse.Choices = append(fullTextResponse.Choices, choice) - jsonResponse, err := json.Marshal(fullTextResponse) + jsonResponse, err := common.Marshal(fullTextResponse) if err != nil { return nil, types.NewError(err, types.ErrorCodeBadResponseBody) } diff --git a/relay/channel/jimeng/adaptor.go b/relay/channel/jimeng/adaptor.go index 1938ac1bec18..966d3db01b7e 100644 --- a/relay/channel/jimeng/adaptor.go +++ b/relay/channel/jimeng/adaptor.go @@ -1,12 +1,12 @@ package jimeng import ( - "encoding/json" "errors" "fmt" "io" "net/http" + "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/relay/channel" "github.com/QuantumNous/new-api/relay/channel/openai" @@ -79,7 +79,7 @@ func (a *Adaptor) ConvertImageRequest(c *gin.Context, info *relaycommon.RelayInf } if len(request.ExtraFields) > 0 { - if err := json.Unmarshal(request.ExtraFields, &payload); err != nil { + if err := common.Unmarshal(request.ExtraFields, &payload); err != nil { return nil, fmt.Errorf("failed to unmarshal extra fields: %w", err) } } diff --git a/relay/channel/jimeng/image.go b/relay/channel/jimeng/image.go index e422e061de6d..99b1a3745a92 100644 --- a/relay/channel/jimeng/image.go +++ b/relay/channel/jimeng/image.go @@ -1,11 +1,11 @@ package jimeng import ( - "encoding/json" "fmt" "io" "net/http" + "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/dto" relaycommon "github.com/QuantumNous/new-api/relay/common" "github.com/QuantumNous/new-api/service" @@ -57,7 +57,7 @@ func jimengImageHandler(c *gin.Context, resp *http.Response, info *relaycommon.R } service.CloseResponseBodyGracefully(resp) - err = json.Unmarshal(responseBody, &jimengResponse) + err = common.Unmarshal(responseBody, &jimengResponse) if err != nil { return nil, types.NewOpenAIError(err, types.ErrorCodeBadResponseBody, http.StatusInternalServerError) } @@ -74,7 +74,7 @@ func jimengImageHandler(c *gin.Context, resp *http.Response, info *relaycommon.R // Convert Jimeng response to OpenAI format fullTextResponse := responseJimeng2OpenAIImage(c, &jimengResponse, info) - jsonResponse, err := json.Marshal(fullTextResponse) + jsonResponse, err := common.Marshal(fullTextResponse) if err != nil { return nil, types.NewError(err, types.ErrorCodeBadResponseBody) } diff --git a/relay/channel/jimeng/sign.go b/relay/channel/jimeng/sign.go index 7c67531e414e..5da08e4508b6 100644 --- a/relay/channel/jimeng/sign.go +++ b/relay/channel/jimeng/sign.go @@ -5,7 +5,6 @@ import ( "crypto/hmac" "crypto/sha256" "encoding/hex" - "encoding/json" "errors" "fmt" "io" @@ -15,8 +14,10 @@ import ( "strings" "time" - "github.com/QuantumNous/new-api/logger" "github.com/gin-gonic/gin" + + "github.com/QuantumNous/new-api/common" + "github.com/QuantumNous/new-api/logger" ) // SignRequestForJimeng 对即梦 API 请求进行签名,支持 http.Request 或 header+url+body 方式 @@ -41,7 +42,7 @@ import ( const HexPayloadHashKey = "HexPayloadHash" func SetPayloadHash(c *gin.Context, req any) error { - body, err := json.Marshal(req) + body, err := common.Marshal(req) if err != nil { return err } diff --git a/relay/channel/minimax/adaptor.go b/relay/channel/minimax/adaptor.go index 56d3a1ec7dca..c4021e5a8ede 100644 --- a/relay/channel/minimax/adaptor.go +++ b/relay/channel/minimax/adaptor.go @@ -2,12 +2,12 @@ package minimax import ( "bytes" - "encoding/json" "errors" "fmt" "io" "net/http" + "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/relay/channel" "github.com/QuantumNous/new-api/relay/channel/claude" @@ -56,12 +56,12 @@ func (a *Adaptor) ConvertAudioRequest(c *gin.Context, info *relaycommon.RelayInf // 同步扩展字段的厂商自定义metadata if len(request.Metadata) > 0 { - if err := json.Unmarshal(request.Metadata, &minimaxRequest); err != nil { + if err := common.Unmarshal(request.Metadata, &minimaxRequest); err != nil { return nil, fmt.Errorf("error unmarshalling metadata to minimax request: %w", err) } } - jsonData, err := json.Marshal(minimaxRequest) + jsonData, err := common.Marshal(minimaxRequest) if err != nil { return nil, fmt.Errorf("error marshalling minimax request: %w", err) } diff --git a/relay/channel/minimax/tts.go b/relay/channel/minimax/tts.go index 61ecabf83106..e34a87fbb455 100644 --- a/relay/channel/minimax/tts.go +++ b/relay/channel/minimax/tts.go @@ -2,18 +2,19 @@ package minimax import ( "encoding/hex" - "encoding/json" "errors" "fmt" "io" "net/http" "strings" + "github.com/gin-gonic/gin" + + "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/dto" relaycommon "github.com/QuantumNous/new-api/relay/common" "github.com/QuantumNous/new-api/service" "github.com/QuantumNous/new-api/types" - "github.com/gin-gonic/gin" ) type MiniMaxTTSRequest struct { @@ -118,7 +119,7 @@ func handleTTSResponse(c *gin.Context, resp *http.Response, info *relaycommon.Re // Parse response var minimaxResp MiniMaxTTSResponse - if unmarshalErr := json.Unmarshal(body, &minimaxResp); unmarshalErr != nil { + if unmarshalErr := common.Unmarshal(body, &minimaxResp); unmarshalErr != nil { return nil, types.NewErrorWithStatusCode( fmt.Errorf("failed to unmarshal minimax TTS response: %w", unmarshalErr), types.ErrorCodeBadResponseBody, diff --git a/relay/channel/mokaai/relay-mokaai.go b/relay/channel/mokaai/relay-mokaai.go index 4949ed643517..1bacf0367796 100644 --- a/relay/channel/mokaai/relay-mokaai.go +++ b/relay/channel/mokaai/relay-mokaai.go @@ -1,7 +1,6 @@ package mokaai import ( - "encoding/json" "io" "net/http" @@ -59,7 +58,7 @@ func mokaEmbeddingHandler(c *gin.Context, info *relaycommon.RelayInfo, resp *htt return nil, types.NewError(err, types.ErrorCodeBadResponseBody) } service.CloseResponseBodyGracefully(resp) - err = json.Unmarshal(responseBody, &baiduResponse) + err = common.Unmarshal(responseBody, &baiduResponse) if err != nil { return nil, types.NewError(err, types.ErrorCodeBadResponseBody) } diff --git a/relay/channel/ollama/relay-ollama.go b/relay/channel/ollama/relay-ollama.go index 06e4d94cd79e..adb593bd843a 100644 --- a/relay/channel/ollama/relay-ollama.go +++ b/relay/channel/ollama/relay-ollama.go @@ -1,7 +1,6 @@ package ollama import ( - "encoding/json" "fmt" "io" "net/http" @@ -32,7 +31,9 @@ func openAIChatToOllamaChat(c *gin.Context, r *dto.GeneralOpenAIRequest) (*Ollam } else if r.ResponseFormat.Type == "json_schema" { if len(r.ResponseFormat.JsonSchema) > 0 { var schema any - _ = json.Unmarshal(r.ResponseFormat.JsonSchema, &schema) + if err := common.Unmarshal(r.ResponseFormat.JsonSchema, &schema); err != nil { + return nil, fmt.Errorf("invalid json_schema: %w", err) + } chatReq.Format = schema } } @@ -127,7 +128,9 @@ func openAIChatToOllamaChat(c *gin.Context, r *dto.GeneralOpenAIRequest) (*Ollam for _, tc := range parsed { var args interface{} if tc.Function.Arguments != "" { - _ = json.Unmarshal([]byte(tc.Function.Arguments), &args) + if err := common.Unmarshal([]byte(tc.Function.Arguments), &args); err != nil { + return nil, fmt.Errorf("invalid tool call arguments: %w", err) + } } if args == nil { args = map[string]any{} @@ -180,7 +183,9 @@ func openAIToGenerate(c *gin.Context, r *dto.GeneralOpenAIRequest) (*OllamaGener gen.Format = "json" } else if r.ResponseFormat.Type == "json_schema" { var schema any - _ = json.Unmarshal(r.ResponseFormat.JsonSchema, &schema) + if err := common.Unmarshal(r.ResponseFormat.JsonSchema, &schema); err != nil { + return nil, fmt.Errorf("invalid json_schema: %w", err) + } gen.Format = schema } } @@ -510,7 +515,7 @@ func FetchOllamaVersion(baseURL, apiKey string) (string, error) { Version string `json:"version"` } - if err := json.Unmarshal(body, &versionResp); err != nil { + if err := common.Unmarshal(body, &versionResp); err != nil { return "", fmt.Errorf("解析响应失败: %v", err) } diff --git a/relay/channel/openai/adaptor.go b/relay/channel/openai/adaptor.go index e118252352df..513836d58442 100644 --- a/relay/channel/openai/adaptor.go +++ b/relay/channel/openai/adaptor.go @@ -22,6 +22,8 @@ import ( "github.com/QuantumNous/new-api/relay/channel/lingyiwanwu" //"github.com/QuantumNous/new-api/relay/channel/minimax" + "github.com/samber/lo" + "github.com/QuantumNous/new-api/relay/channel/openrouter" "github.com/QuantumNous/new-api/relay/channel/xinference" relaycommon "github.com/QuantumNous/new-api/relay/common" @@ -31,7 +33,6 @@ import ( "github.com/QuantumNous/new-api/setting/model_setting" "github.com/QuantumNous/new-api/setting/reasoning" "github.com/QuantumNous/new-api/types" - "github.com/samber/lo" "github.com/gin-gonic/gin" ) @@ -289,7 +290,7 @@ func (a *Adaptor) ConvertOpenAIRequest(c *gin.Context, info *relaycommon.RelayIn // 没有做排除3.5Haiku等,要出问题再加吧,最佳兼容性(不是 if request.THINKING != nil && strings.HasPrefix(info.UpstreamModelName, "anthropic") { var thinking dto.Thinking // Claude标准Thinking格式 - if err := json.Unmarshal(request.THINKING, &thinking); err != nil { + if err := common.Unmarshal(request.THINKING, &thinking); err != nil { return nil, fmt.Errorf("error Unmarshal thinking: %w", err) } diff --git a/relay/channel/openai/relay_image.go b/relay/channel/openai/relay_image.go index e0f09aae28d5..0d2de3c1e1ad 100644 --- a/relay/channel/openai/relay_image.go +++ b/relay/channel/openai/relay_image.go @@ -187,7 +187,7 @@ func writeOpenaiImageStreamChunk(c *gin.Context, data []byte) error { // "data:" payload. A payload carrying just a "message" key is deliberately NOT // treated as an error to avoid false positives. func isOpenAIImageStreamErrorEvent(data []byte) bool { - if !json.Valid(data) { + if !common.ValidJson(data) { return false } var payload struct { @@ -202,7 +202,7 @@ func isOpenAIImageStreamErrorEvent(data []byte) bool { } func extractOpenAIImageStreamErrorMessage(data []byte) string { - if len(data) == 0 || !json.Valid(data) { + if len(data) == 0 || !common.ValidJson(data) { return "upstream image stream returned error event" } var payload struct { diff --git a/relay/channel/palm/relay-palm.go b/relay/channel/palm/relay-palm.go index 786ea4cd2a20..c784869a4c15 100644 --- a/relay/channel/palm/relay-palm.go +++ b/relay/channel/palm/relay-palm.go @@ -1,7 +1,6 @@ package palm import ( - "encoding/json" "io" "net/http" @@ -65,7 +64,7 @@ func palmStreamHandler(c *gin.Context, resp *http.Response) (*types.NewAPIError, } service.CloseResponseBodyGracefully(resp) var palmResponse PaLMChatResponse - err = json.Unmarshal(responseBody, &palmResponse) + err = common.Unmarshal(responseBody, &palmResponse) if err != nil { common.SysLog("error unmarshalling stream response: " + err.Error()) stopChan <- true @@ -77,7 +76,7 @@ func palmStreamHandler(c *gin.Context, resp *http.Response) (*types.NewAPIError, if len(palmResponse.Candidates) > 0 { responseText = palmResponse.Candidates[0].Content } - jsonResponse, err := json.Marshal(fullTextResponse) + jsonResponse, err := common.Marshal(fullTextResponse) if err != nil { common.SysLog("error marshalling stream response: " + err.Error()) stopChan <- true @@ -108,7 +107,7 @@ func palmHandler(c *gin.Context, info *relaycommon.RelayInfo, resp *http.Respons } service.CloseResponseBodyGracefully(resp) var palmResponse PaLMChatResponse - err = json.Unmarshal(responseBody, &palmResponse) + err = common.Unmarshal(responseBody, &palmResponse) if err != nil { return nil, types.NewOpenAIError(err, types.ErrorCodeBadResponseBody, http.StatusInternalServerError) } diff --git a/relay/channel/replicate/adaptor.go b/relay/channel/replicate/adaptor.go index 673502054b45..19944bf5c94e 100644 --- a/relay/channel/replicate/adaptor.go +++ b/relay/channel/replicate/adaptor.go @@ -2,7 +2,6 @@ package replicate import ( "bytes" - "encoding/json" "errors" "fmt" "io" @@ -111,7 +110,7 @@ func (a *Adaptor) ConvertImageRequest(c *gin.Context, info *relaycommon.RelayInf if len(request.OutputFormat) > 0 { var outputFormat string - if err := json.Unmarshal(request.OutputFormat, &outputFormat); err == nil && strings.TrimSpace(outputFormat) != "" { + if err := common.Unmarshal(request.OutputFormat, &outputFormat); err == nil && strings.TrimSpace(outputFormat) != "" { inputPayload["output_format"] = outputFormat } } diff --git a/relay/channel/siliconflow/relay-siliconflow.go b/relay/channel/siliconflow/relay-siliconflow.go index 421731fb1a96..1cc83755060e 100644 --- a/relay/channel/siliconflow/relay-siliconflow.go +++ b/relay/channel/siliconflow/relay-siliconflow.go @@ -1,10 +1,10 @@ package siliconflow import ( - "encoding/json" "io" "net/http" + "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/dto" relaycommon "github.com/QuantumNous/new-api/relay/common" "github.com/QuantumNous/new-api/service" @@ -20,7 +20,7 @@ func siliconflowRerankHandler(c *gin.Context, info *relaycommon.RelayInfo, resp } service.CloseResponseBodyGracefully(resp) var siliconflowResp SFRerankResponse - err = json.Unmarshal(responseBody, &siliconflowResp) + err = common.Unmarshal(responseBody, &siliconflowResp) if err != nil { return nil, types.NewOpenAIError(err, types.ErrorCodeBadResponseBody, http.StatusInternalServerError) } @@ -34,7 +34,7 @@ func siliconflowRerankHandler(c *gin.Context, info *relaycommon.RelayInfo, resp Usage: *usage, } - jsonResponse, err := json.Marshal(rerankResp) + jsonResponse, err := common.Marshal(rerankResp) if err != nil { return nil, types.NewError(err, types.ErrorCodeBadResponseBody) } diff --git a/relay/channel/task/taskcommon/helpers.go b/relay/channel/task/taskcommon/helpers.go index 7d1820c9de83..b74f67818e9b 100644 --- a/relay/channel/task/taskcommon/helpers.go +++ b/relay/channel/task/taskcommon/helpers.go @@ -4,15 +4,16 @@ import ( "encoding/base64" "fmt" + "github.com/gin-gonic/gin" + "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/model" relaycommon "github.com/QuantumNous/new-api/relay/common" "github.com/QuantumNous/new-api/setting/system_setting" - "github.com/gin-gonic/gin" ) // UnmarshalMetadata converts a map[string]any metadata to a typed struct via JSON round-trip. -// This replaces the repeated pattern: json.Marshal(metadata) → json.Unmarshal(bytes, &target). +// This replaces the repeated pattern: common.Marshal(metadata) → common.Unmarshal(bytes, &target). func UnmarshalMetadata(metadata map[string]any, target any) error { if metadata == nil { return nil diff --git a/relay/channel/tencent/relay-tencent.go b/relay/channel/tencent/relay-tencent.go index 4cda7541a6f4..30b8f21748c4 100644 --- a/relay/channel/tencent/relay-tencent.go +++ b/relay/channel/tencent/relay-tencent.go @@ -5,7 +5,6 @@ import ( "crypto/hmac" "crypto/sha256" "encoding/hex" - "encoding/json" "errors" "fmt" "io" @@ -140,7 +139,7 @@ func tencentHandler(c *gin.Context, info *relaycommon.RelayInfo, resp *http.Resp return nil, types.NewOpenAIError(err, types.ErrorCodeReadResponseBodyFailed, http.StatusInternalServerError) } service.CloseResponseBodyGracefully(resp) - err = json.Unmarshal(responseBody, &tencentSb) + err = common.Unmarshal(responseBody, &tencentSb) if err != nil { return nil, types.NewOpenAIError(err, types.ErrorCodeBadResponseBody, http.StatusInternalServerError) } @@ -193,7 +192,7 @@ func getTencentSign(req TencentChatRequest, adaptor *Adaptor, secId, secKey stri canonicalHeaders := fmt.Sprintf("content-type:%s\nhost:%s\nx-tc-action:%s\n", "application/json", host, strings.ToLower(adaptor.Action)) signedHeaders := "content-type;host;x-tc-action" - payload, _ := json.Marshal(req) + payload, _ := common.Marshal(req) hashedRequestPayload := sha256hex(string(payload)) canonicalRequest := fmt.Sprintf("%s\n%s\n%s\n%s\n%s\n%s", httpRequestMethod, diff --git a/relay/channel/vertex/service_account.go b/relay/channel/vertex/service_account.go index 96ec6b28f49c..1d0a6dec1a58 100644 --- a/relay/channel/vertex/service_account.go +++ b/relay/channel/vertex/service_account.go @@ -3,13 +3,13 @@ package vertex import ( "crypto/rsa" "crypto/x509" - "encoding/json" "encoding/pem" "errors" "net/http" "net/url" "strings" + "github.com/QuantumNous/new-api/common" relaycommon "github.com/QuantumNous/new-api/relay/common" "github.com/QuantumNous/new-api/service" @@ -129,7 +129,7 @@ func exchangeJwtForAccessToken(signedJWT string, info *relaycommon.RelayInfo) (s defer resp.Body.Close() var result map[string]interface{} - if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { + if err := common.DecodeJson(resp.Body, &result); err != nil { return "", err } @@ -172,7 +172,7 @@ func exchangeJwtForAccessTokenWithProxy(signedJWT string, proxy string) (string, defer resp.Body.Close() var result map[string]interface{} - if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { + if err := common.DecodeJson(resp.Body, &result); err != nil { return "", err } diff --git a/relay/channel/volcengine/adaptor.go b/relay/channel/volcengine/adaptor.go index ba9f223bd2f6..4ad93f1e2c6c 100644 --- a/relay/channel/volcengine/adaptor.go +++ b/relay/channel/volcengine/adaptor.go @@ -10,6 +10,7 @@ import ( "path/filepath" "strings" + "github.com/QuantumNous/new-api/common" channelconstant "github.com/QuantumNous/new-api/constant" "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/relay/channel" @@ -86,9 +87,15 @@ func (a *Adaptor) ConvertAudioRequest(c *gin.Context, info *relaycommon.RelayInf } if len(request.Metadata) > 0 { - if err = json.Unmarshal(request.Metadata, &volcRequest); err != nil { + if err = common.Unmarshal(request.Metadata, &volcRequest); err != nil { return nil, fmt.Errorf("error unmarshalling metadata to volcengine request: %w", err) } + // Restore server-controlled auth fields so client metadata cannot override them + volcRequest.App = VolcengineTTSApp{ + AppID: appID, + Token: token, + Cluster: "volcano_tts", + } } c.Set(contextKeyTTSRequest, volcRequest) @@ -97,7 +104,7 @@ func (a *Adaptor) ConvertAudioRequest(c *gin.Context, info *relaycommon.RelayInf info.IsStream = true } - jsonData, err := json.Marshal(volcRequest) + jsonData, err := common.Marshal(volcRequest) if err != nil { return nil, fmt.Errorf("error marshalling volcengine request: %w", err) } diff --git a/relay/channel/volcengine/tts.go b/relay/channel/volcengine/tts.go index 2b03981d4221..c15399bf1823 100644 --- a/relay/channel/volcengine/tts.go +++ b/relay/channel/volcengine/tts.go @@ -3,19 +3,20 @@ package volcengine import ( "context" "encoding/base64" - "encoding/json" "errors" "fmt" "io" "net/http" "strings" - "github.com/QuantumNous/new-api/dto" - relaycommon "github.com/QuantumNous/new-api/relay/common" - "github.com/QuantumNous/new-api/types" "github.com/gin-gonic/gin" "github.com/google/uuid" "github.com/gorilla/websocket" + + "github.com/QuantumNous/new-api/common" + "github.com/QuantumNous/new-api/dto" + relaycommon "github.com/QuantumNous/new-api/relay/common" + "github.com/QuantumNous/new-api/types" ) type VolcengineTTSRequest struct { @@ -154,7 +155,7 @@ func handleTTSResponse(c *gin.Context, resp *http.Response, info *relaycommon.Re defer resp.Body.Close() var volcResp VolcengineTTSResponse - if unmarshalErr := json.Unmarshal(body, &volcResp); unmarshalErr != nil { + if unmarshalErr := common.Unmarshal(body, &volcResp); unmarshalErr != nil { return nil, types.NewErrorWithStatusCode( errors.New("failed to parse volcengine response"), types.ErrorCodeBadResponseBody, @@ -226,7 +227,7 @@ func handleTTSWebSocketResponse(c *gin.Context, requestURL string, volcRequest V } defer conn.Close() - payload, marshalErr := json.Marshal(volcRequest) + payload, marshalErr := common.Marshal(volcRequest) if marshalErr != nil { return nil, types.NewErrorWithStatusCode( fmt.Errorf("failed to marshal request: %w", marshalErr), diff --git a/relay/channel/xunfei/relay-xunfei.go b/relay/channel/xunfei/relay-xunfei.go index 70fde810a568..edab710fe8bd 100644 --- a/relay/channel/xunfei/relay-xunfei.go +++ b/relay/channel/xunfei/relay-xunfei.go @@ -4,19 +4,19 @@ import ( "crypto/hmac" "crypto/sha256" "encoding/base64" - "encoding/json" "fmt" "io" "net/url" "strings" "time" + "github.com/samber/lo" + "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/constant" "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/relay/helper" "github.com/QuantumNous/new-api/types" - "github.com/samber/lo" "github.com/gin-gonic/gin" "github.com/gorilla/websocket" @@ -143,7 +143,7 @@ func xunfeiStreamHandler(c *gin.Context, textRequest dto.GeneralOpenAIRequest, a usage.CompletionTokens += xunfeiResponse.Payload.Usage.Text.CompletionTokens usage.TotalTokens += xunfeiResponse.Payload.Usage.Text.TotalTokens response := streamResponseXunfei2OpenAI(&xunfeiResponse) - jsonResponse, err := json.Marshal(response) + jsonResponse, err := common.Marshal(response) if err != nil { common.SysLog("error marshalling stream response: " + err.Error()) return true @@ -191,7 +191,7 @@ func xunfeiHandler(c *gin.Context, textRequest dto.GeneralOpenAIRequest, appId s xunfeiResponse.Payload.Choices.Text[0].Content = content response := responseXunfei2OpenAI(&xunfeiResponse) - jsonResponse, err := json.Marshal(response) + jsonResponse, err := common.Marshal(response) if err != nil { return nil, types.NewError(err, types.ErrorCodeBadResponseBody) } @@ -228,7 +228,7 @@ func xunfeiMakeRequest(textRequest dto.GeneralOpenAIRequest, domain, authUrl, ap break } var response XunfeiChatResponse - err = json.Unmarshal(msg, &response) + err = common.Unmarshal(msg, &response) if err != nil { common.SysLog("error unmarshalling stream response: " + err.Error()) break diff --git a/relay/channel/zhipu/relay-zhipu.go b/relay/channel/zhipu/relay-zhipu.go index 6754c02cd934..aa1c2f19a083 100644 --- a/relay/channel/zhipu/relay-zhipu.go +++ b/relay/channel/zhipu/relay-zhipu.go @@ -2,13 +2,14 @@ package zhipu import ( "bufio" - "encoding/json" "io" "net/http" "strings" "sync" "time" + "github.com/samber/lo" + "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/constant" "github.com/QuantumNous/new-api/dto" @@ -16,7 +17,6 @@ import ( "github.com/QuantumNous/new-api/relay/helper" "github.com/QuantumNous/new-api/service" "github.com/QuantumNous/new-api/types" - "github.com/samber/lo" "github.com/gin-gonic/gin" "github.com/golang-jwt/jwt/v5" @@ -190,7 +190,7 @@ func zhipuStreamHandler(c *gin.Context, info *relaycommon.RelayInfo, resp *http. select { case data := <-dataChan: response := streamResponseZhipu2OpenAI(data) - jsonResponse, err := json.Marshal(response) + jsonResponse, err := common.Marshal(response) if err != nil { common.SysLog("error marshalling stream response: " + err.Error()) return true @@ -199,13 +199,13 @@ func zhipuStreamHandler(c *gin.Context, info *relaycommon.RelayInfo, resp *http. return true case data := <-metaChan: var zhipuResponse ZhipuStreamMetaResponse - err := json.Unmarshal([]byte(data), &zhipuResponse) + err := common.Unmarshal([]byte(data), &zhipuResponse) if err != nil { common.SysLog("error unmarshalling stream response: " + err.Error()) return true } response, zhipuUsage := streamMetaResponseZhipu2OpenAI(&zhipuResponse) - jsonResponse, err := json.Marshal(response) + jsonResponse, err := common.Marshal(response) if err != nil { common.SysLog("error marshalling stream response: " + err.Error()) return true @@ -229,7 +229,7 @@ func zhipuHandler(c *gin.Context, info *relaycommon.RelayInfo, resp *http.Respon return nil, types.NewOpenAIError(err, types.ErrorCodeReadResponseBodyFailed, http.StatusInternalServerError) } service.CloseResponseBodyGracefully(resp) - err = json.Unmarshal(responseBody, &zhipuResponse) + err = common.Unmarshal(responseBody, &zhipuResponse) if err != nil { return nil, types.NewOpenAIError(err, types.ErrorCodeBadResponseBody, http.StatusInternalServerError) } @@ -240,7 +240,7 @@ func zhipuHandler(c *gin.Context, info *relaycommon.RelayInfo, resp *http.Respon }, resp.StatusCode) } fullTextResponse := responseZhipu2OpenAI(&zhipuResponse) - jsonResponse, err := json.Marshal(fullTextResponse) + jsonResponse, err := common.Marshal(fullTextResponse) if err != nil { return nil, types.NewError(err, types.ErrorCodeBadResponseBody) } diff --git a/relay/helper/model_mapped.go b/relay/helper/model_mapped.go index 5d6efa094865..9ec8003cc7fe 100644 --- a/relay/helper/model_mapped.go +++ b/relay/helper/model_mapped.go @@ -1,16 +1,17 @@ package helper import ( - "encoding/json" "errors" "fmt" "strings" + "github.com/gin-gonic/gin" + + commonpkg "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/relay/common" relayconstant "github.com/QuantumNous/new-api/relay/constant" "github.com/QuantumNous/new-api/setting/ratio_setting" - "github.com/gin-gonic/gin" ) func ModelMappedHelper(c *gin.Context, info *common.RelayInfo, request dto.Request) error { @@ -29,7 +30,7 @@ func ModelMappedHelper(c *gin.Context, info *common.RelayInfo, request dto.Reque modelMapping := c.GetString("model_mapping") if modelMapping != "" && modelMapping != "{}" { modelMap := make(map[string]string) - err := json.Unmarshal([]byte(modelMapping), &modelMap) + err := commonpkg.Unmarshal([]byte(modelMapping), &modelMap) if err != nil { return fmt.Errorf("unmarshal_model_mapping_failed") } diff --git a/relay/mjproxy_handler.go b/relay/mjproxy_handler.go index 2bde250506ef..47473719e9ba 100644 --- a/relay/mjproxy_handler.go +++ b/relay/mjproxy_handler.go @@ -2,7 +2,6 @@ package relay import ( "bytes" - "encoding/json" "fmt" "io" "log" @@ -125,7 +124,7 @@ func RelayMidjourneyNotify(c *gin.Context) *dto.MidjourneyResponse { midjourneyTask.FinishTime = midjRequest.FinishTime midjourneyTask.ImageUrl = midjRequest.ImageUrl midjourneyTask.VideoUrl = midjRequest.VideoUrl - videoUrlsStr, _ := json.Marshal(midjRequest.VideoUrls) + videoUrlsStr, _ := common.Marshal(midjRequest.VideoUrls) midjourneyTask.VideoUrls = string(videoUrlsStr) midjourneyTask.Status = midjRequest.Status midjourneyTask.FailReason = midjRequest.FailReason @@ -167,21 +166,21 @@ func coverMidjourneyTaskDto(c *gin.Context, originTask *model.Midjourney) (midjo midjourneyTask.Prompt = originTask.Prompt if originTask.Buttons != "" { var buttons []dto.ActionButton - err := json.Unmarshal([]byte(originTask.Buttons), &buttons) + err := common.Unmarshal([]byte(originTask.Buttons), &buttons) if err == nil { midjourneyTask.Buttons = buttons } } if originTask.VideoUrls != "" { var videoUrls []dto.ImgUrls - err := json.Unmarshal([]byte(originTask.VideoUrls), &videoUrls) + err := common.Unmarshal([]byte(originTask.VideoUrls), &videoUrls) if err == nil { midjourneyTask.VideoUrls = videoUrls } } if originTask.Properties != "" { var properties dto.Properties - err := json.Unmarshal([]byte(originTask.Properties), &properties) + err := common.Unmarshal([]byte(originTask.Properties), &properties) if err == nil { midjourneyTask.Properties = &properties } @@ -281,7 +280,7 @@ func RelaySwapFace(c *gin.Context, info *relaycommon.RelayInfo) *dto.MidjourneyR return service.MidjourneyErrorWrapper(constant.MjRequestError, "insert_midjourney_task_failed") } c.Writer.WriteHeader(mjResp.StatusCode) - respBody, err := json.Marshal(midjResponse) + respBody, err := common.Marshal(midjResponse) if err != nil { return service.MidjourneyErrorWrapper(constant.MjRequestError, "unmarshal_response_body_failed") } @@ -317,7 +316,7 @@ func RelayMidjourneyTaskImageSeed(c *gin.Context) *dto.MidjourneyResponse { } midjResponse := &midjResponseWithStatus.Response c.Writer.WriteHeader(midjResponseWithStatus.StatusCode) - respBody, err := json.Marshal(midjResponse) + respBody, err := common.Marshal(midjResponse) if err != nil { return service.MidjourneyErrorWrapper(constant.MjRequestError, "unmarshal_response_body_failed") } @@ -340,7 +339,7 @@ func RelayMidjourneyTask(c *gin.Context, relayMode int) *dto.MidjourneyResponse } } midjourneyTask := coverMidjourneyTaskDto(c, originTask) - respBody, err = json.Marshal(midjourneyTask) + respBody, err = common.Marshal(midjourneyTask) if err != nil { return &dto.MidjourneyResponse{ Code: 4, @@ -369,7 +368,7 @@ func RelayMidjourneyTask(c *gin.Context, relayMode int) *dto.MidjourneyResponse if tasks == nil { tasks = make([]dto.MidjourneyDto, 0) } - respBody, err = json.Marshal(tasks) + respBody, err = common.Marshal(tasks) if err != nil { return &dto.MidjourneyResponse{ Code: 4, diff --git a/service/midjourney.go b/service/midjourney.go index c6397b7f0815..88ec60ae7732 100644 --- a/service/midjourney.go +++ b/service/midjourney.go @@ -2,7 +2,6 @@ package service import ( "context" - "encoding/json" "io" "net/http" "strconv" @@ -170,7 +169,7 @@ func DoMidjourneyHttpRequest(c *gin.Context, timeout time.Duration, fullRequestU var mapResult map[string]interface{} // if get request, no need to read request body if c.Request.Method != "GET" { - err := json.NewDecoder(c.Request.Body).Decode(&mapResult) + err := common.DecodeJson(c.Request.Body, &mapResult) if err != nil { return MidjourneyErrorWithStatusCodeWrapper(constant.MjErrorUnknown, "read_request_body_failed", http.StatusInternalServerError), nullBytes, err } @@ -192,7 +191,7 @@ func DoMidjourneyHttpRequest(c *gin.Context, timeout time.Duration, fullRequestU mapResult["prompt"] = prompt } } - reqBody, err := json.Marshal(mapResult) + reqBody, err := common.Marshal(mapResult) if err != nil { return MidjourneyErrorWithStatusCodeWrapper(constant.MjErrorUnknown, "marshal_request_body_failed", http.StatusInternalServerError), nullBytes, err } @@ -239,9 +238,9 @@ func DoMidjourneyHttpRequest(c *gin.Context, timeout time.Duration, fullRequestU if len(responseBody) == 0 { return MidjourneyErrorWithStatusCodeWrapper(constant.MjErrorUnknown, "empty_response_body", statusCode), responseBody, nil } else { - err = json.Unmarshal(responseBody, &midjResponse) + err = common.Unmarshal(responseBody, &midjResponse) if err != nil { - err2 := json.Unmarshal(responseBody, &midjourneyUploadsResponse) + err2 := common.Unmarshal(responseBody, &midjourneyUploadsResponse) if err2 != nil { return MidjourneyErrorWithStatusCodeWrapper(constant.MjErrorUnknown, "unmarshal_response_body_failed", statusCode), responseBody, err } diff --git a/service/passkey/session.go b/service/passkey/session.go index 15e61932690f..6243a8e2150c 100644 --- a/service/passkey/session.go +++ b/service/passkey/session.go @@ -1,12 +1,13 @@ package passkey import ( - "encoding/json" "errors" "github.com/gin-contrib/sessions" "github.com/gin-gonic/gin" webauthn "github.com/go-webauthn/webauthn/webauthn" + + "github.com/QuantumNous/new-api/common" ) var errSessionNotFound = errors.New("Passkey 会话不存在或已过期") @@ -17,7 +18,7 @@ func SaveSessionData(c *gin.Context, key string, data *webauthn.SessionData) err session.Delete(key) return session.Save() } - payload, err := json.Marshal(data) + payload, err := common.Marshal(data) if err != nil { return err } @@ -36,11 +37,11 @@ func PopSessionData(c *gin.Context, key string) (*webauthn.SessionData, error) { var data webauthn.SessionData switch value := raw.(type) { case string: - if err := json.Unmarshal([]byte(value), &data); err != nil { + if err := common.Unmarshal([]byte(value), &data); err != nil { return nil, err } case []byte: - if err := json.Unmarshal(value, &data); err != nil { + if err := common.Unmarshal(value, &data); err != nil { return nil, err } default: diff --git a/setting/chat.go b/setting/chat.go index bb8a99771939..c91e81988e0f 100644 --- a/setting/chat.go +++ b/setting/chat.go @@ -1,8 +1,6 @@ package setting import ( - "encoding/json" - "github.com/QuantumNous/new-api/common" ) @@ -41,11 +39,11 @@ var Chats = []map[string]string{ func UpdateChatsByJsonString(jsonString string) error { Chats = make([]map[string]string, 0) - return json.Unmarshal([]byte(jsonString), &Chats) + return common.Unmarshal([]byte(jsonString), &Chats) } func Chats2JsonString() string { - jsonBytes, err := json.Marshal(Chats) + jsonBytes, err := common.Marshal(Chats) if err != nil { common.SysLog("error marshalling chats: " + err.Error()) return "[]" diff --git a/setting/config/config.go b/setting/config/config.go index a82d934f6e5b..09745f6badf8 100644 --- a/setting/config/config.go +++ b/setting/config/config.go @@ -1,7 +1,6 @@ package config import ( - "encoding/json" "reflect" "strconv" "strings" @@ -134,7 +133,7 @@ func configToMap(config interface{}) (map[string]string, error) { case reflect.Ptr: // 处理指针类型:如果非 nil,序列化指向的值 if !field.IsNil() { - bytes, err := json.Marshal(field.Interface()) + bytes, err := common.Marshal(field.Interface()) if err != nil { return nil, err } @@ -145,7 +144,7 @@ func configToMap(config interface{}) (map[string]string, error) { } case reflect.Map, reflect.Slice, reflect.Struct: // 复杂类型使用JSON序列化 - bytes, err := json.Marshal(field.Interface()) + bytes, err := common.Marshal(field.Interface()) if err != nil { return nil, err } @@ -247,7 +246,7 @@ func updateConfigFromMap(config interface{}, configMap map[string]string) error field.Set(reflect.New(field.Type().Elem())) } // 反序列化到指针指向的值 - err := json.Unmarshal([]byte(strValue), field.Interface()) + err := common.Unmarshal([]byte(strValue), field.Interface()) if err != nil { continue } @@ -257,12 +256,12 @@ func updateConfigFromMap(config interface{}, configMap map[string]string) error // absent from the new JSON). Allocate a fresh map so removed keys // are properly cleared. fresh := reflect.New(field.Type()) - if err := json.Unmarshal([]byte(strValue), fresh.Interface()); err != nil { + if err := common.Unmarshal([]byte(strValue), fresh.Interface()); err != nil { continue } field.Set(fresh.Elem()) case reflect.Slice, reflect.Struct: - err := json.Unmarshal([]byte(strValue), field.Addr().Interface()) + err := common.Unmarshal([]byte(strValue), field.Addr().Interface()) if err != nil { continue } diff --git a/setting/console_setting/validation.go b/setting/console_setting/validation.go index d6e4342c3d8f..b77bd15d17fc 100644 --- a/setting/console_setting/validation.go +++ b/setting/console_setting/validation.go @@ -1,13 +1,14 @@ package console_setting import ( - "encoding/json" "fmt" "net/url" "regexp" "sort" "strings" "time" + + "github.com/QuantumNous/new-api/common" ) var ( @@ -24,7 +25,7 @@ var ( func parseJSONArray(jsonStr string, typeName string) ([]map[string]interface{}, error) { var list []map[string]interface{} - if err := json.Unmarshal([]byte(jsonStr), &list); err != nil { + if err := common.Unmarshal([]byte(jsonStr), &list); err != nil { return nil, fmt.Errorf("%s格式错误:%s", typeName, err.Error()) } return list, nil @@ -55,7 +56,7 @@ func getJSONList(jsonStr string) []map[string]interface{} { return []map[string]interface{}{} } var list []map[string]interface{} - json.Unmarshal([]byte(jsonStr), &list) + common.Unmarshal([]byte(jsonStr), &list) return list } diff --git a/setting/rate_limit.go b/setting/rate_limit.go index 413f3958d759..f4a8f93e7d17 100644 --- a/setting/rate_limit.go +++ b/setting/rate_limit.go @@ -1,7 +1,6 @@ package setting import ( - "encoding/json" "fmt" "math" "sync" @@ -20,7 +19,7 @@ func ModelRequestRateLimitGroup2JSONString() string { ModelRequestRateLimitMutex.RLock() defer ModelRequestRateLimitMutex.RUnlock() - jsonBytes, err := json.Marshal(ModelRequestRateLimitGroup) + jsonBytes, err := common.Marshal(ModelRequestRateLimitGroup) if err != nil { common.SysLog("error marshalling model ratio: " + err.Error()) } @@ -28,11 +27,15 @@ func ModelRequestRateLimitGroup2JSONString() string { } func UpdateModelRequestRateLimitGroupByJSONString(jsonStr string) error { - ModelRequestRateLimitMutex.RLock() - defer ModelRequestRateLimitMutex.RUnlock() + ModelRequestRateLimitMutex.Lock() + defer ModelRequestRateLimitMutex.Unlock() - ModelRequestRateLimitGroup = make(map[string][2]int) - return json.Unmarshal([]byte(jsonStr), &ModelRequestRateLimitGroup) + newGroup := make(map[string][2]int) + if err := common.Unmarshal([]byte(jsonStr), &newGroup); err != nil { + return err + } + ModelRequestRateLimitGroup = newGroup + return nil } func GetGroupRateLimit(group string) (totalCount, successCount int, found bool) { @@ -52,7 +55,7 @@ func GetGroupRateLimit(group string) (totalCount, successCount int, found bool) func CheckModelRequestRateLimitGroup(jsonStr string) error { checkModelRequestRateLimitGroup := make(map[string][2]int) - err := json.Unmarshal([]byte(jsonStr), &checkModelRequestRateLimitGroup) + err := common.Unmarshal([]byte(jsonStr), &checkModelRequestRateLimitGroup) if err != nil { return err } diff --git a/setting/ratio_setting/group_ratio.go b/setting/ratio_setting/group_ratio.go index 7d16d9283932..9c13e11b2cb6 100644 --- a/setting/ratio_setting/group_ratio.go +++ b/setting/ratio_setting/group_ratio.go @@ -1,7 +1,6 @@ package ratio_setting import ( - "encoding/json" "errors" "github.com/QuantumNous/new-api/common" @@ -107,7 +106,7 @@ func UpdateGroupGroupRatioByJSONString(jsonStr string) error { func CheckGroupRatio(jsonStr string) error { checkGroupRatio := make(map[string]float64) - err := json.Unmarshal([]byte(jsonStr), &checkGroupRatio) + err := common.Unmarshal([]byte(jsonStr), &checkGroupRatio) if err != nil { return err } diff --git a/setting/user_usable_group.go b/setting/user_usable_group.go index eb04b7f30534..3f5067f0d007 100644 --- a/setting/user_usable_group.go +++ b/setting/user_usable_group.go @@ -1,7 +1,6 @@ package setting import ( - "encoding/json" "sync" "github.com/QuantumNous/new-api/common" @@ -28,7 +27,7 @@ func UserUsableGroups2JSONString() string { userUsableGroupsMutex.RLock() defer userUsableGroupsMutex.RUnlock() - jsonBytes, err := json.Marshal(userUsableGroups) + jsonBytes, err := common.Marshal(userUsableGroups) if err != nil { common.SysLog("error marshalling user groups: " + err.Error()) } @@ -39,8 +38,12 @@ func UpdateUserUsableGroupsByJSONString(jsonStr string) error { userUsableGroupsMutex.Lock() defer userUsableGroupsMutex.Unlock() - userUsableGroups = make(map[string]string) - return json.Unmarshal([]byte(jsonStr), &userUsableGroups) + newGroups := make(map[string]string) + if err := common.Unmarshal([]byte(jsonStr), &newGroups); err != nil { + return err + } + userUsableGroups = newGroups + return nil } func GetUsableGroupDescription(groupName string) string {