refactor: modernize Go code conventions

This commit is contained in:
CaIon committed 2026-09-08 20:55:03 +08:00
1 parent fff0635bb1
commit ebe4c368f2
214 files changed
+1257 -1422

No files matched your search

+2 -2
View File
@@ -53,7 +53,7 @@ type CustomEvent struct {
Event string
Id string
Retry uint
Data interface{}
Data any
}
func encode(writer io.Writer, event CustomEvent) error {
@@ -61,7 +61,7 @@ func encode(writer io.Writer, event CustomEvent) error {
return writeData(w, event.Data)
}
func writeData(w stringWriter, data interface{}) error {
func writeData(w stringWriter, data any) error {
dataReplacer.WriteString(w, fmt.Sprint(data))
if strings.HasPrefix(data.(string), "data") {
w.writeString("\n\n")
+1 -1
View File
@@ -10,7 +10,7 @@ import (
type digestAnchor struct{}
func modulePath() string {
return reflect.TypeOf(digestAnchor{}).PkgPath()
return reflect.TypeFor[digestAnchor]().PkgPath()
}
var digestSeed = func() (s [sha256.Size]byte) {
+1 -1
View File
@@ -12,7 +12,7 @@ var relayGoPool gopool.Pool
func init() {
relayGoPool = gopool.NewPool("gopool.RelayPool", math.MaxInt32, gopool.NewConfig())
relayGoPool.SetPanicHandler(func(ctx context.Context, i interface{}) {
relayGoPool.SetPanicHandler(func(ctx context.Context, i any) {
if stopChan, ok := ctx.Value("stop_chan").(chan bool); ok {
SafeSendBool(stopChan, true)
}
+4 -4
View File
@@ -213,8 +213,8 @@ func initConstantEnv() {
soraPatchStr := GetEnvOrDefaultString("TASK_PRICE_PATCH", "")
if soraPatchStr != "" {
var taskPricePatches []string
soraPatches := strings.Split(soraPatchStr, ",")
for _, patch := range soraPatches {
soraPatches := strings.SplitSeq(soraPatchStr, ",")
for patch := range soraPatches {
trimmedPatch := strings.TrimSpace(patch)
if trimmedPatch != "" {
taskPricePatches = append(taskPricePatches, trimmedPatch)
@@ -226,8 +226,8 @@ func initConstantEnv() {
// Initialize trusted redirect domains for URL validation
trustedDomainsStr := GetEnvOrDefaultString("TRUSTED_REDIRECT_DOMAINS", "")
var trustedDomains []string
domains := strings.Split(trustedDomainsStr, ",")
for _, domain := range domains {
domains := strings.SplitSeq(trustedDomainsStr, ",")
for domain := range domains {
trimmedDomain := strings.TrimSpace(domain)
if trimmedDomain != "" {
// Normalize domain to lowercase
+2 -2
View File
@@ -59,11 +59,11 @@ func (c *QuotaClamp) Error() string {
// AuditMap renders the clamp as the marker stored under a log's
// admin_info.quota_saturation. Centralized here so every billing path (consume
// logs, task billing logs, task compensation logs) records the same shape.
func (c *QuotaClamp) AuditMap() map[string]interface{} {
func (c *QuotaClamp) AuditMap() map[string]any {
if c == nil {
return nil
}
return map[string]interface{}{
return map[string]any{
"op": c.Op,
"kind": c.Kind,
"original": c.Original,
+7 -7
View File
@@ -104,13 +104,13 @@ func RedisDelKey(key string) error {
return RDB.Del(ctx, key).Err()
}
func RedisHSetObj(key string, obj interface{}, expiration time.Duration) error {
func RedisHSetObj(key string, obj any, expiration time.Duration) error {
if DebugEnabled {
SysLog(fmt.Sprintf("Redis HSET: key=%s, obj=%+v, expiration=%v", key, obj, expiration))
}
ctx := context.Background()
data := make(map[string]interface{})
data := make(map[string]any)
// 使用反射遍历结构体字段
v := reflect.ValueOf(obj).Elem()
@@ -125,7 +125,7 @@ func RedisHSetObj(key string, obj interface{}, expiration time.Duration) error {
}
// 处理指针类型
if value.Kind() == reflect.Ptr {
if value.Kind() == reflect.Pointer {
if value.IsNil() {
data[field.Name] = ""
continue
@@ -158,7 +158,7 @@ func RedisHSetObj(key string, obj interface{}, expiration time.Duration) error {
return nil
}
func RedisHGetObj(key string, obj interface{}) error {
func RedisHGetObj(key string, obj any) error {
if DebugEnabled {
SysLog(fmt.Sprintf("Redis HGETALL: key=%s", key))
}
@@ -175,7 +175,7 @@ func RedisHGetObj(key string, obj interface{}) error {
// Handle both pointer and non-pointer values
val := reflect.ValueOf(obj)
if val.Kind() != reflect.Ptr {
if val.Kind() != reflect.Pointer {
return fmt.Errorf("obj must be a pointer to a struct, got %T", obj)
}
@@ -192,7 +192,7 @@ func RedisHGetObj(key string, obj interface{}) error {
fieldValue := v.Field(i)
// Handle pointer types
if fieldValue.Kind() == reflect.Ptr {
if fieldValue.Kind() == reflect.Pointer {
if value == "" {
continue
}
@@ -299,7 +299,7 @@ func RedisHIncrBy(key, field string, delta int64) error {
return nil
}
func RedisHSetField(key, field string, value interface{}) error {
func RedisHSetField(key, field string, value any) error {
if DebugEnabled {
SysLog(fmt.Sprintf("Redis HSET field: key=%s, field=%s, value=%v", key, field, value))
}
+2 -2
View File
@@ -63,8 +63,8 @@ func InitSessionCookieSettings() error {
return fmt.Errorf("SESSION_COOKIE_SECURE=true requires SESSION_COOKIE_TRUSTED_URL")
}
trustedURLs := strings.Split(trustedURLsRaw, ",")
for _, trustedURL := range trustedURLs {
trustedURLs := strings.SplitSeq(trustedURLsRaw, ",")
for trustedURL := range trustedURLs {
trustedURL = strings.TrimSpace(trustedURL)
if trustedURL == "" {
return fmt.Errorf("SESSION_COOKIE_TRUSTED_URL contains an empty URL")
+4 -8
View File
@@ -4,6 +4,7 @@ import (
"fmt"
"net"
"net/url"
"slices"
"strconv"
"strings"
)
@@ -197,12 +198,7 @@ func (p *SSRFProtection) isAllowedPort(port int) bool {
return true // 如果没有配置端口限制,则允许所有端口
}
for _, allowedPort := range p.AllowedPorts {
if port == allowedPort {
return true
}
}
return false
return slices.Contains(p.AllowedPorts, port)
}
// isDomainWhitelisted 检查域名是否在白名单中
@@ -222,8 +218,8 @@ func isDomainListed(domain string, list []string) bool {
return true
}
// 通配符匹配 (*.example.com)
if strings.HasPrefix(item, "*.") {
suffix := strings.TrimPrefix(item, "*.")
if after, ok := strings.CutPrefix(item, "*."); ok {
suffix := after
if strings.HasSuffix(domain, "."+suffix) || domain == suffix {
return true
}
+14 -17
View File
@@ -4,11 +4,13 @@ import (
"encoding/base64"
"encoding/json"
"fmt"
kitutil "github.com/QuantumNous/new-api/relaykit/relayconvert/kitutil"
"slices"
"strconv"
"strings"
"unsafe"
kitutil "github.com/QuantumNous/new-api/relaykit/relayconvert/kitutil"
"github.com/samber/lo"
)
@@ -36,7 +38,7 @@ func GetRandomString(length int) string {
return lo.RandomString(length, lo.AlphanumericCharset)
}
func MapToJsonStr(m map[string]interface{}) string {
func MapToJsonStr(m map[string]any) string {
bytes, err := json.Marshal(m)
if err != nil {
return ""
@@ -44,8 +46,8 @@ func MapToJsonStr(m map[string]interface{}) string {
return string(bytes)
}
func StrToMap(str string) (map[string]interface{}, error) {
m := make(map[string]interface{})
func StrToMap(str string) (map[string]any, error) {
m := make(map[string]any)
err := Unmarshal([]byte(str), &m)
if err != nil {
return nil, err
@@ -53,8 +55,8 @@ func StrToMap(str string) (map[string]interface{}, error) {
return m, nil
}
func StrToJsonArray(str string) ([]interface{}, error) {
var js []interface{}
func StrToJsonArray(str string) ([]any, error) {
var js []any
err := json.Unmarshal([]byte(str), &js)
if err != nil {
return nil, err
@@ -63,12 +65,12 @@ func StrToJsonArray(str string) ([]interface{}, error) {
}
func IsJsonArray(str string) bool {
var js []interface{}
var js []any
return json.Unmarshal([]byte(str), &js) == nil
}
func IsJsonObject(str string) bool {
var js map[string]interface{}
var js map[string]any
return json.Unmarshal([]byte(str), &js) == nil
}
@@ -81,12 +83,7 @@ func String2Int(str string) int {
}
func StringsContains(strs []string, str string) bool {
for _, s := range strs {
if s == str {
return true
}
}
return false
return slices.Contains(strs, str)
}
// StringToByteSlice []byte only read, panic on append
@@ -126,14 +123,14 @@ func MaskEmail(email string) string {
}
// Find the @ symbol
atIndex := strings.Index(email, "@")
if atIndex == -1 {
_, after, ok := strings.Cut(email, "@")
if !ok {
// No @ symbol found, return masked
return "***masked***"
}
// Return only the domain part with @ symbol
return "***@" + email[atIndex+1:]
return "***@" + after
}
// MaskSensitiveInfo moved to the conversion kit (kitutil) because the types
+1 -1
View File
@@ -49,7 +49,7 @@ func ValidateTOTPCode(secret, code string) bool {
func GenerateBackupCodes() ([]string, error) {
codes := make([]string, BackupCodeCount)
for i := 0; i < BackupCodeCount; i++ {
for i := range BackupCodeCount {
code, err := generateRandomBackupCode()
if err != nil {
return nil, err
+2 -2
View File
@@ -188,7 +188,7 @@ func Seconds2Time(num int) (time string) {
return
}
func Interface2String(inter interface{}) string {
func Interface2String(inter any) string {
switch inter.(type) {
case string:
return inter.(string)
@@ -208,7 +208,7 @@ func Interface2String(inter interface{}) string {
return fmt.Sprintf("%v", inter)
}
func UnescapeHTML(x string) interface{} {
func UnescapeHTML(x string) any {
return template.HTML(x)
}
+2 -2
View File
@@ -44,7 +44,7 @@ func GenerateAccessToken(c *gin.Context) {
writeSecurityOperationError(c, err)
return
}
recordUserSecurityAudit(c, id, "access_token.generate", map[string]interface{}{"token_ref": model.AccessTokenFingerprint(key)})
recordUserSecurityAudit(c, id, "access_token.generate", map[string]any{"token_ref": model.AccessTokenFingerprint(key)})
c.JSON(http.StatusOK, gin.H{"success": true, "message": "", "data": key})
}
@@ -58,7 +58,7 @@ func RevokeAccessToken(c *gin.Context) {
return
}
if ref != "" {
recordUserSecurityAudit(c, c.GetInt("id"), "access_token.revoke", map[string]interface{}{"token_ref": ref})
recordUserSecurityAudit(c, c.GetInt("id"), "access_token.revoke", map[string]any{"token_ref": ref})
}
common.ApiSuccess(c, nil)
}
+11 -10
View File
@@ -4,8 +4,6 @@ import (
"context"
"errors"
"fmt"
sqlmysql "github.com/go-sql-driver/mysql"
"gorm.io/driver/clickhouse"
"net"
"net/http"
"net/http/httptest"
@@ -15,6 +13,9 @@ import (
"testing"
"time"
sqlmysql "github.com/go-sql-driver/mysql"
"gorm.io/driver/clickhouse"
"github.com/QuantumNous/new-api/common"
"github.com/QuantumNous/new-api/middleware"
"github.com/QuantumNous/new-api/model"
@@ -128,7 +129,7 @@ func TestAccessTokenAuditsResultsAndExcludesBrowserSessions(t *testing.T) {
router.GET("/rate-limited", func(c *gin.Context) { c.AbortWithStatusJSON(429, gin.H{"success": false}) }, middleware.UserAuth())
router.GET("/read/:id", middleware.UserAuth(), func(c *gin.Context) { c.JSON(200, gin.H{"success": true}) })
router.POST("/write", middleware.AdminAuth(), func(c *gin.Context) {
recordManageAudit(c, "option.update", map[string]interface{}{"key": "safe-setting"})
recordManageAudit(c, "option.update", map[string]any{"key": "safe-setting"})
c.JSON(200, gin.H{"success": true})
})
router.POST("/business-failure", middleware.AdminAuth(), func(c *gin.Context) { c.JSON(200, gin.H{"success": false, "message": "secret-response"}) })
@@ -202,7 +203,7 @@ func TestAuditIsolationVisibilityAndFailureContracts(t *testing.T) {
var payload struct {
Data struct {
Items []struct {
Other map[string]interface{} `json:"other"`
Other map[string]any `json:"other"`
}
}
}
@@ -343,7 +344,7 @@ func TestSecurityAndOperationEventsUseAuditTable(t *testing.T) {
for _, action := range []string{"user.passkey_register", "user.passkey_delete", "user.2fa_setup", "user.2fa_enable", "user.2fa_disable_self", "user.2fa_backup_codes", "user.security_verify"} {
recordUserSecurityAudit(c, user.Id, action, nil)
}
recordManageAudit(c, "option.update", map[string]interface{}{"key": "safe"})
recordManageAudit(c, "option.update", map[string]any{"key": "safe"})
recordSubscriptionResetUserLogs(c, &model.SubscriptionResetResult{ResetCount: 1, PlanId: 1, PlanTitle: "Plan", AffectedUserIds: []int{user.Id}}, &model.AuditAdminInfo{AdminID: user.Id})
for _, typ := range []int{model.LogTypeTopup, model.LogTypeConsume, model.LogTypeRefund, model.LogTypeSystem} {
model.RecordLog(user.Id, typ, "business entry")
@@ -536,7 +537,7 @@ func verifyAuditJSONStorage(t *testing.T) {
metadata := model.AuditOther{
Op: &model.AuditOperation{Action: "channel.update", Params: model.AuditFields{
"id": 42, "name": "渠道", "changed_fields": []string{}, "large_id": uint64(9007199254740993),
"extra": map[string]interface{}{"attempts": 0, "permitted": false, "ratio": 1.25, "targets": []int{1, 2}},
"extra": map[string]any{"attempts": 0, "permitted": false, "ratio": 1.25, "targets": []int{1, 2}},
}},
AdminInfo: &model.AuditAdminInfo{AdminID: 1},
AuditInfo: &model.AuditRequestInfo{Method: "PUT", Route: "/api/channel/", Path: "/api/channel/", Status: 200, Success: false},
@@ -586,7 +587,7 @@ func verifyAuditJSONStorage(t *testing.T) {
empty, err := common.Marshal(entries[0].Other)
require.NoError(t, err)
assert.JSONEq(t, `{}`, string(empty))
for i := 0; i < 2; i++ {
for range 2 {
require.NoError(t, model.InitLogDB())
}
entries, total, err = model.GetAuditLogs(filter, 0, 20, common.RoleRootUser)
@@ -600,7 +601,7 @@ func verifyAuditJSONStorage(t *testing.T) {
func TestAuditOtherDatabaseEncoding(t *testing.T) {
const payload = `{"op":{"action":"channel.update","params":{"id":9007199254740993,"nested":{"count":0,"enabled":false}}},"admin_info":{"admin_id":1,"admin_username":"root","admin_role":100,"auth_method":"session"},"audit_info":{"method":"PUT","route":"/api/channel/","path":"/api/channel/","status":200,"success":false},"root_info":{"generation":18446744073709551615}}`
for _, input := range []interface{}{payload, []byte(payload)} {
for _, input := range []any{payload, []byte(payload)} {
var other model.AuditOther
require.NoError(t, other.Scan(input))
require.NotNil(t, other.Op)
@@ -616,7 +617,7 @@ func TestAuditOtherDatabaseEncoding(t *testing.T) {
assert.JSONEq(t, payload, text)
assert.Contains(t, text, "9007199254740993")
assert.Contains(t, text, "18446744073709551615")
for _, empty := range []interface{}{nil, "", []byte(`null`), "{}"} {
for _, empty := range []any{nil, "", []byte(`null`), "{}"} {
require.NoError(t, other.Scan(input))
require.NoError(t, other.Scan(empty))
encoded, err = other.Value()
@@ -679,7 +680,7 @@ func TestAuditDatabaseMatrix(t *testing.T) {
require.NoError(t, db.Create(&releasedAuditUser{Username: "released-owner", Password: "placeholder", AccessToken: &legacy, AffCode: "released-aff", Quota: 1234}).Error)
require.NoError(t, db.Create(&releasedAuditLog{UserId: 1, Type: model.LogTypeLogin, Content: "historical login", CreatedAt: 100, RequestId: "legacy-request"}).Error)
}
for i := 0; i < 2; i++ {
for range 2 {
require.NoError(t, model.InitDB())
require.NoError(t, model.InitLogDB())
}
+6 -6
View File
@@ -66,7 +66,7 @@ var auditContentTemplates = map[string]string{
}
// auditContentEN 按 action 模板渲染英文兜底文本;未登记的 action 退回 action 本身。
func auditContentEN(action string, params map[string]interface{}) string {
func auditContentEN(action string, params map[string]any) string {
tmpl, ok := auditContentTemplates[action]
if !ok {
return action
@@ -104,15 +104,15 @@ func markAuditLogged(c *gin.Context) {
// recordManageAudit 记录一条由操作者本人归属的管理/高危审计日志(资源类操作:
// 渠道 / 系统设置 / 兑换码等)。content 由 action+params 自动渲染。
func recordManageAudit(c *gin.Context, action string, params map[string]interface{}) {
func recordManageAudit(c *gin.Context, action string, params map[string]any) {
recordManageAuditFor(c, c.GetInt("id"), action, params)
}
// recordManageAuditFor 记录一条管理审计日志,日志归属于操作者;targetUserId
// 只表示被操作用户,用于在结构化参数中保留目标上下文。
func recordManageAuditFor(c *gin.Context, targetUserId int, action string, params map[string]interface{}) {
func recordManageAuditFor(c *gin.Context, targetUserId int, action string, params map[string]any) {
if params == nil {
params = map[string]interface{}{}
params = map[string]any{}
}
operatorUserId := c.GetInt("id")
if _, ok := params["target_user_id"]; !ok && targetUserId > 0 && targetUserId != operatorUserId {
@@ -124,10 +124,10 @@ func recordManageAuditFor(c *gin.Context, targetUserId int, action string, param
// recordUserSecurityAudit 记录普通用户自己的安全敏感操作(如 passkey 绑定/解绑)。
// 这类日志没有管理员操作者,不写 admin_info;同时不依赖 AdminAuth/RootAuth 的兜底。
func recordUserSecurityAudit(c *gin.Context, userId int, action string, params map[string]interface{}) {
func recordUserSecurityAudit(c *gin.Context, userId int, action string, params map[string]any) {
if code := c.GetString("security_error_code"); code != "" {
if params == nil {
params = map[string]interface{}{}
params = map[string]any{}
}
params["code"] = code
}
+3 -5
View File
@@ -92,7 +92,7 @@ func TestSecurityLoginCodeCompletesOnce(t *testing.T) {
router := gin.New()
router.POST("/api/user/login/verify", VerifyLogin)
router.POST("/api/user/login/2fa", Verify2FALogin)
for attempt := 0; attempt < 2; attempt++ {
for attempt := range 2 {
response := httptest.NewRecorder()
router.ServeHTTP(response, httptest.NewRequest("POST", test.path, strings.NewReader(string(body))))
var result securityEnrollmentResponse
@@ -241,12 +241,10 @@ func TestSecurityLoginPasskeyConcurrentCompletionCreatesOneSession(t *testing.T)
responses := make(chan *httptest.ResponseRecorder, 2)
var workers sync.WaitGroup
for _, body := range requests {
workers.Add(1)
go func() {
defer workers.Done()
workers.Go(func() {
<-start
responses <- securityEnrollmentRequest("POST", "/api/user/login/passkey/finish", body, "", service.AuthIdentity{}, LoginPasskeyFinish)
}()
})
}
close(start)
workers.Wait()
+2 -2
View File
@@ -613,7 +613,7 @@ func detectErrorFromTestResponseBody(respBody []byte) error {
return fmt.Errorf("upstream error: %s", message)
}
for _, line := range bytes.Split(b, []byte{'\n'}) {
for line := range bytes.SplitSeq(b, []byte{'\n'}) {
line = bytes.TrimSpace(line)
if len(line) == 0 {
continue
@@ -639,7 +639,7 @@ func validateStreamTestResponseBody(respBody []byte) error {
return errors.New("stream response body is empty")
}
for _, line := range bytes.Split(b, []byte{'\n'}) {
for line := range bytes.SplitSeq(b, []byte{'\n'}) {
line = bytes.TrimSpace(line)
if len(line) == 0 || !bytes.HasPrefix(line, []byte("data:")) {
continue
+22 -31
View File
@@ -369,14 +369,8 @@ func SearchChannels(c *gin.Context) {
}
total := len(channelData)
startIdx := (page - 1) * pageSize
if startIdx > total {
startIdx = total
}
endIdx := startIdx + pageSize
if endIdx > total {
endIdx = total
}
startIdx := min((page-1)*pageSize, total)
endIdx := min(startIdx+pageSize, total)
pagedData := channelData[startIdx:endIdx]
@@ -439,7 +433,7 @@ func GetChannelKey(c *gin.Context) {
}
// 记录操作审计日志(高危:查看渠道密钥)
recordManageAudit(c, "channel.key_view", map[string]interface{}{
recordManageAudit(c, "channel.key_view", map[string]any{
"id": channelId,
"name": channel.Name,
})
@@ -448,7 +442,7 @@ func GetChannelKey(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"success": true,
"message": "获取成功",
"data": map[string]interface{}{
"data": map[string]any{
"key": channel.Key,
},
})
@@ -588,7 +582,7 @@ func getVertexArrayKeys(keys string) ([]string, error) {
if keys == "" {
return nil, nil
}
var keyArray []interface{}
var keyArray []any
err := common.Unmarshal([]byte(keys), &keyArray)
if err != nil {
return nil, fmt.Errorf("批量添加 Vertex AI 必须使用标准的JsonArray格式,例如[{key1}, {key2}...],请检查输入: %w", err)
@@ -664,7 +658,7 @@ func AddChannel(c *gin.Context) {
addChannelRequest.Channel.Key = strings.Join(array, "\n")
} else {
cleanKeys := make([]string, 0)
for _, key := range strings.Split(addChannelRequest.Channel.Key, "\n") {
for key := range strings.SplitSeq(addChannelRequest.Channel.Key, "\n") {
if key == "" {
continue
}
@@ -720,7 +714,7 @@ func AddChannel(c *gin.Context) {
common.ApiError(c, err)
return
}
createAudit := map[string]interface{}{
createAudit := map[string]any{
"name": addChannelRequest.Channel.Name,
"type": addChannelRequest.Channel.Type,
"count": len(channels),
@@ -759,7 +753,7 @@ func DeleteChannel(c *gin.Context) {
} else {
service.InvalidateProxyClient(channelProxy)
}
recordManageAudit(c, "channel.delete", map[string]interface{}{
recordManageAudit(c, "channel.delete", map[string]any{
"id": id,
"name": channelName,
})
@@ -780,7 +774,7 @@ func DeleteDisabledChannel(c *gin.Context) {
if rows > 0 {
service.ResetProxyClientCache()
}
recordManageAudit(c, "channel.delete_disabled", map[string]interface{}{
recordManageAudit(c, "channel.delete_disabled", map[string]any{
"count": rows,
})
c.JSON(http.StatusOK, gin.H{
@@ -819,7 +813,7 @@ func DisableTagChannels(c *gin.Context) {
return
}
model.InitChannelCache()
recordManageAudit(c, "channel.tag_disable", map[string]interface{}{
recordManageAudit(c, "channel.tag_disable", map[string]any{
"tag": channelTag.Tag,
})
c.JSON(http.StatusOK, gin.H{
@@ -845,7 +839,7 @@ func EnableTagChannels(c *gin.Context) {
return
}
model.InitChannelCache()
recordManageAudit(c, "channel.tag_enable", map[string]interface{}{
recordManageAudit(c, "channel.tag_enable", map[string]any{
"tag": channelTag.Tag,
})
c.JSON(http.StatusOK, gin.H{
@@ -905,7 +899,7 @@ func EditTagChannels(c *gin.Context) {
return
}
model.InitChannelCache()
recordManageAudit(c, "channel.tag_edit", map[string]interface{}{
recordManageAudit(c, "channel.tag_edit", map[string]any{
"tag": channelTag.Tag,
})
c.JSON(http.StatusOK, gin.H{
@@ -939,7 +933,7 @@ func DeleteChannelBatch(c *gin.Context) {
if deletedCount > 0 {
service.ResetProxyClientCache()
}
recordManageAudit(c, "channel.delete_batch", map[string]interface{}{
recordManageAudit(c, "channel.delete_batch", map[string]any{
"count": deletedCount,
})
c.JSON(http.StatusOK, gin.H{
@@ -1080,8 +1074,8 @@ func UpdateChannel(c *gin.Context) {
}
} else {
// 普通渠道的处理
inputKeys := strings.Split(channel.Key, "\n")
for _, key := range inputKeys {
inputKeys := strings.SplitSeq(channel.Key, "\n")
for key := range inputKeys {
key = strings.TrimSpace(key)
if key != "" {
newKeys = append(newKeys, key)
@@ -1143,7 +1137,7 @@ func UpdateChannel(c *gin.Context) {
if channel.Key != "" && channel.Key != originChannel.Key {
changedFields = append(changedFields, "key")
}
updateAudit := map[string]interface{}{
updateAudit := map[string]any{
"id": channel.Id,
"name": channel.Name,
"changed_fields": changedFields,
@@ -1177,7 +1171,7 @@ func UpdateChannelStatus(c *gin.Context) {
if changed {
model.InitChannelCache()
}
recordManageAudit(c, "channel.status_update", map[string]interface{}{
recordManageAudit(c, "channel.status_update", map[string]any{
"id": id,
"status": req.Status,
"changed": changed,
@@ -1204,7 +1198,7 @@ func BatchUpdateChannelStatus(c *gin.Context) {
if changedCount > 0 {
model.InitChannelCache()
}
recordManageAudit(c, "channel.status_update_batch", map[string]interface{}{
recordManageAudit(c, "channel.status_update_batch", map[string]any{
"count": changedCount,
"total": len(req.Ids),
"status": req.Status,
@@ -1382,7 +1376,7 @@ func BatchSetChannelTag(c *gin.Context) {
return
}
model.InitChannelCache()
recordManageAudit(c, "channel.tag_batch_set", map[string]interface{}{
recordManageAudit(c, "channel.tag_batch_set", map[string]any{
"count": len(channelBatch.Ids),
})
c.JSON(http.StatusOK, gin.H{
@@ -1493,7 +1487,7 @@ func CopyChannel(c *gin.Context) {
return
}
model.InitChannelCache()
recordManageAudit(c, "channel.copy", map[string]interface{}{
recordManageAudit(c, "channel.copy", map[string]any{
"sourceId": id,
"id": clone.Id,
"name": clone.Name,
@@ -1568,7 +1562,7 @@ func ManageMultiKeys(c *gin.Context) {
if request.Action == "get_key_status" {
markAuditLogged(c)
} else {
recordManageAudit(c, "channel.multi_key_manage", map[string]interface{}{
recordManageAudit(c, "channel.multi_key_manage", map[string]any{
"action": request.Action,
"id": channel.Id,
})
@@ -1666,10 +1660,7 @@ func ManageMultiKeys(c *gin.Context) {
// Calculate range for current page
start := (page - 1) * pageSize
end := start + pageSize
if end > filteredTotal {
end = filteredTotal
}
end := min(start+pageSize, filteredTotal)
// Get the page data
var pageKeyStatusList []KeyStatus
+1 -1
View File
@@ -197,7 +197,7 @@ func TestChannelFieldsAreClassified(t *testing.T) {
return names
}
for _, name := range collect(reflect.TypeOf(PatchChannel{})) {
for _, name := range collect(reflect.TypeFor[PatchChannel]()) {
assert.Truef(t, classified(name),
"channel field %q is not classified; add it to channelSensitiveFields, channelNonSensitiveFields, channelOperationalFields, or channelReadOnlyFields in channel_authz.go", name)
}
+4 -4
View File
@@ -498,7 +498,7 @@ func fetchAdvancedCustomUpstreamModelIDs(channel *model.Channel, baseURL string)
func updateChannelUpstreamModelSettings(channel *model.Channel, settings dto.ChannelOtherSettings, updateModels bool) error {
channel.SetOtherSettings(settings)
updates := map[string]interface{}{
updates := map[string]any{
"settings": channel.OtherSettings,
}
if updateModels {
@@ -889,7 +889,7 @@ func ApplyChannelUpstreamModelUpdates(c *gin.Context) {
refreshChannelRuntimeCache()
}
recordManageAudit(c, "channel.upstream_apply", map[string]interface{}{
recordManageAudit(c, "channel.upstream_apply", map[string]any{
"id": channel.Id,
})
c.JSON(http.StatusOK, gin.H{
@@ -1087,7 +1087,7 @@ func ApplyAllChannelUpstreamModelUpdates(c *gin.Context) {
refreshChannelRuntimeCache()
}
recordManageAudit(c, "channel.upstream_apply_all", map[string]interface{}{
recordManageAudit(c, "channel.upstream_apply_all", map[string]any{
"count": len(results),
})
c.JSON(http.StatusOK, gin.H{
@@ -1128,7 +1128,7 @@ func DetectAllChannelUpstreamModelUpdates(c *gin.Context) {
return
}
recordManageAudit(c, "channel.upstream_detect_all", map[string]interface{}{
recordManageAudit(c, "channel.upstream_detect_all", map[string]any{
"task_id": task.TaskID,
})
c.JSON(http.StatusOK, gin.H{
+1 -1
View File
@@ -534,7 +534,7 @@ func TestCollectPendingUpstreamModelChangesFromModels_WithIgnoredRegexPatterns(t
func TestBuildUpstreamModelUpdateTaskNotificationContent_OmitOverflowDetails(t *testing.T) {
channelSummaries := make([]upstreamModelUpdateChannelSummary, 0, 12)
for i := 0; i < 12; i++ {
for i := range 12 {
channelSummaries = append(channelSummaries, upstreamModelUpdateChannelSummary{
ChannelName: "channel-" + string(rune('A'+i)),
AddCount: i + 1,
+1 -1
View File
@@ -538,7 +538,7 @@ func UnbindCustomOAuth(c *gin.Context) {
succeeded, notificationFailed := false, false
defer func() {
recordUserSecurityAudit(c, identity.UserID, "user.binding_unbind", map[string]interface{}{"provider_id": providerId, "success": succeeded, "notification_failed": notificationFailed})
recordUserSecurityAudit(c, identity.UserID, "user.binding_unbind", map[string]any{"provider_id": providerId, "success": succeeded, "notification_failed": notificationFailed})
}()
context, err := common.Marshal(service.AccountUnbindingContext{ProviderID: providerId})
if err != nil {
+15 -18
View File
@@ -135,7 +135,7 @@ func requireContainerID(c *gin.Context) (string, bool) {
return containerID, true
}
func mapIoNetDeployment(d ionet.Deployment) map[string]interface{} {
func mapIoNetDeployment(d ionet.Deployment) map[string]any {
var created int64
if d.CreatedAt.IsZero() {
created = time.Now().Unix()
@@ -156,7 +156,7 @@ func mapIoNetDeployment(d ionet.Deployment) map[string]interface{} {
hardwareInfo := fmt.Sprintf("%s %s x%d", d.BrandName, d.HardwareName, d.HardwareQuantity)
return map[string]interface{}{
return map[string]any{
"id": d.ID,
"deployment_name": d.Name,
"container_name": d.Name,
@@ -176,7 +176,7 @@ func mapIoNetDeployment(d ionet.Deployment) map[string]interface{} {
"model_name": "",
"model_version": "",
"instance_count": d.HardwareQuantity,
"resource_config": map[string]interface{}{
"resource_config": map[string]any{
"cpu": "",
"memory": "",
"gpu": strconv.Itoa(d.HardwareQuantity),
@@ -225,7 +225,7 @@ func GetAllDeployments(c *gin.Context) {
return
}
items := make([]map[string]interface{}, 0, len(dl.Deployments))
items := make([]map[string]any, 0, len(dl.Deployments))
for _, d := range dl.Deployments {
items = append(items, mapIoNetDeployment(d))
}
@@ -274,7 +274,7 @@ func SearchDeployments(c *gin.Context) {
}
}
items := make([]map[string]interface{}, 0, len(filtered))
items := make([]map[string]any, 0, len(filtered))
for _, d := range filtered {
items = append(items, mapIoNetDeployment(d))
}
@@ -310,7 +310,7 @@ func GetDeployment(c *gin.Context) {
return
}
data := map[string]interface{}{
data := map[string]any{
"id": details.ID,
"deployment_name": details.ID,
"model_name": "",
@@ -318,7 +318,7 @@ func GetDeployment(c *gin.Context) {
"status": strings.ToLower(details.Status),
"instance_count": details.TotalContainers,
"hardware_id": details.HardwareID,
"resource_config": map[string]interface{}{
"resource_config": map[string]any{
"cpu": "",
"memory": "",
"gpu": strconv.Itoa(details.TotalGPUs),
@@ -668,10 +668,7 @@ func GetDeploymentLogs(c *gin.Context) {
var limit int = 100
if limitStr != "" {
if parsedLimit, err := strconv.Atoi(limitStr); err == nil && parsedLimit > 0 {
limit = parsedLimit
if limit > 1000 {
limit = 1000
}
limit = min(parsedLimit, 1000)
}
}
@@ -720,19 +717,19 @@ func ListDeploymentContainers(c *gin.Context) {
return
}
items := make([]map[string]interface{}, 0)
items := make([]map[string]any, 0)
if containers != nil {
items = make([]map[string]interface{}, 0, len(containers.Workers))
items = make([]map[string]any, 0, len(containers.Workers))
for _, ctr := range containers.Workers {
events := make([]map[string]interface{}, 0, len(ctr.ContainerEvents))
events := make([]map[string]any, 0, len(ctr.ContainerEvents))
for _, event := range ctr.ContainerEvents {
events = append(events, map[string]interface{}{
events = append(events, map[string]any{
"time": event.Time.Unix(),
"message": event.Message,
})
}
items = append(items, map[string]interface{}{
items = append(items, map[string]any{
"container_id": ctr.ContainerID,
"device_id": ctr.DeviceID,
"status": strings.ToLower(strings.TrimSpace(ctr.Status)),
@@ -784,9 +781,9 @@ func GetContainerDetails(c *gin.Context) {
return
}
events := make([]map[string]interface{}, 0, len(details.ContainerEvents))
events := make([]map[string]any, 0, len(details.ContainerEvents))
for _, event := range details.ContainerEvents {
events = append(events, map[string]interface{}{
events = append(events, map[string]any{
"time": event.Time.Unix(),
"message": event.Message,
})
+3 -3
View File
@@ -25,7 +25,7 @@ func EmailBindStart(c *gin.Context) {
}
succeeded, notificationFailed := false, false
defer func() {
recordUserSecurityAudit(c, identity.UserID, "user.binding_start", map[string]interface{}{"provider": "email", "success": succeeded, "notification_failed": notificationFailed})
recordUserSecurityAudit(c, identity.UserID, "user.binding_start", map[string]any{"provider": "email", "success": succeeded, "notification_failed": notificationFailed})
}()
var request emailBindRequest
if err := common.DecodeJson(c.Request.Body, &request); err != nil {
@@ -63,7 +63,7 @@ func EmailBindResend(c *gin.Context) {
}
succeeded := false
defer func() {
recordUserSecurityAudit(c, identity.UserID, "user.email_binding_resend", map[string]interface{}{"success": succeeded})
recordUserSecurityAudit(c, identity.UserID, "user.email_binding_resend", map[string]any{"success": succeeded})
}()
var request emailBindRequest
if err := common.DecodeJson(c.Request.Body, &request); err != nil || request.FlowToken == "" {
@@ -87,7 +87,7 @@ func EmailBind(c *gin.Context) {
}
succeeded, notificationFailed := false, false
defer func() {
recordUserSecurityAudit(c, identity.UserID, "user.binding_bind", map[string]interface{}{"provider": "email", "success": succeeded, "notification_failed": notificationFailed})
recordUserSecurityAudit(c, identity.UserID, "user.binding_bind", map[string]any{"provider": "email", "success": succeeded, "notification_failed": notificationFailed})
}()
var request emailBindRequest
if err := common.DecodeJson(c.Request.Body, &request); err != nil || request.FlowToken == "" {
+3 -3
View File
@@ -24,7 +24,7 @@ func GetGroups(c *gin.Context) {
}
func GetUserGroups(c *gin.Context) {
usableGroups := make(map[string]map[string]interface{})
usableGroups := make(map[string]map[string]any)
userGroup := ""
userId := c.GetInt("id")
userGroup, _ = model.GetUserGroup(userId, false)
@@ -32,14 +32,14 @@ func GetUserGroups(c *gin.Context) {
for groupName, _ := range ratio_setting.GetGroupRatioCopy() {
// UserUsableGroups contains the groups that the user can use
if desc, ok := userUsableGroups[groupName]; ok {
usableGroups[groupName] = map[string]interface{}{
usableGroups[groupName] = map[string]any{
"ratio": service.GetUserGroupRatio(userGroup, groupName),
"desc": desc,
}
}
}
if _, ok := userUsableGroups["auto"]; ok {
usableGroups["auto"] = map[string]interface{}{
usableGroups["auto"] = map[string]any{
"ratio": "自动",
"desc": setting.GetUsableGroupDescription("auto"),
}
+1 -1
View File
@@ -34,7 +34,7 @@ var channelId2Models map[int][]string
func init() {
// https://platform.openai.com/docs/models/model-endpoint-compatibility
for i := 0; i < constant.APITypeDummy; i++ {
for i := range constant.APITypeDummy {
if i == constant.APITypeAIProxyLibrary {
continue
}
+3 -5
View File
@@ -512,13 +512,11 @@ export function parseTaskResult() { return {}; }
update := model.MetadataSyncUpdate{MetadataSyncSelection: model.MetadataSyncSelection{ModelName: "matrix-concurrent-import", RecordVersion: model.MetadataRecordVersion(nil, nil, nil), Create: true}, Values: model.MetadataValues{Description: "Imported", Status: 1}}
var wg sync.WaitGroup
results := make(chan error, 2)
for i := 0; i < 2; i++ {
wg.Add(1)
go func() {
defer wg.Done()
for range 2 {
wg.Go(func() {
_, err := model.ApplyMetadataSync([]model.MetadataSyncUpdate{update}, nil)
results <- err
}()
})
}
wg.Wait()
close(results)
+2 -2
View File
@@ -215,7 +215,7 @@ func DeleteModelMeta(c *gin.Context) {
common.ApiError(c, err)
return
}
recordManageAudit(c, "model.delete", map[string]interface{}{"model_ids": []int{id}, "remove_from_channels": removeFromChannels, "remove_pricing": removePricing, "updated_channels": result.UpdatedChannels})
recordManageAudit(c, "model.delete", map[string]any{"model_ids": []int{id}, "remove_from_channels": removeFromChannels, "remove_pricing": removePricing, "updated_channels": result.UpdatedChannels})
common.ApiSuccess(c, result)
}
@@ -238,7 +238,7 @@ func BatchDeleteModelMeta(c *gin.Context) {
common.ApiError(c, err)
return
}
recordManageAudit(c, "model.delete_batch", map[string]interface{}{"model_ids": request.ModelIDs, "remove_from_channels": request.RemoveFromChannels, "remove_pricing": request.RemovePricing, "updated_channels": result.UpdatedChannels})
recordManageAudit(c, "model.delete_batch", map[string]any{"model_ids": request.ModelIDs, "remove_from_channels": request.RemoveFromChannels, "remove_pricing": request.RemovePricing, "updated_channels": result.UpdatedChannels})
common.ApiSuccess(c, result)
}
+1 -1
View File
@@ -38,6 +38,6 @@ func UpdateModelPricingConfig(c *gin.Context) {
for _, change := range request.Changes {
names = append(names, change.ModelName)
}
recordManageAudit(c, "model.pricing.update", map[string]interface{}{"models": names})
recordManageAudit(c, "model.pricing.update", map[string]any{"models": names})
common.ApiSuccess(c, gin.H{"updated_models": names})
}
+2 -5
View File
@@ -126,10 +126,7 @@ func getHTTPClient() *http.Client {
func fetchJSON[T any](ctx context.Context, url string, out *upstreamEnvelope[T]) error {
var lastErr error
attempts := common.GetEnvOrDefault("SYNC_HTTP_RETRY", 3)
if attempts < 1 {
attempts = 1
}
attempts := max(common.GetEnvOrDefault("SYNC_HTTP_RETRY", 3), 1)
baseDelay := 200 * time.Millisecond
maxMB := common.GetEnvOrDefault("SYNC_HTTP_MAX_MB", 10)
maxBytes := int64(maxMB) << 20
@@ -450,6 +447,6 @@ func SyncUpstreamModels(c *gin.Context) {
c.JSON(status, gin.H{"success": false, "message": err.Error()})
return
}
recordManageAudit(c, "model.metadata.sync", map[string]interface{}{"created_models": result.CreatedModels, "updated_models": result.UpdatedModels, "created_vendors": result.CreatedVendors})
recordManageAudit(c, "model.metadata.sync", map[string]any{"created_models": result.CreatedModels, "updated_models": result.UpdatedModels, "created_vendors": result.CreatedVendors})
common.ApiSuccess(c, result)
}
+4 -4
View File
@@ -82,7 +82,7 @@ func GenerateOAuthCode(c *gin.Context) {
sessionID = identity.SessionID
if request.Intent == model.AuthFlowIntentBind {
defer func() {
recordUserSecurityAudit(c, userID, "user.binding_start", map[string]interface{}{"provider": request.Provider, "success": bindingStarted})
recordUserSecurityAudit(c, userID, "user.binding_start", map[string]any{"provider": request.Provider, "success": bindingStarted})
}()
context, err := common.Marshal(service.AccountBindingContext{Provider: request.Provider})
if err != nil {
@@ -176,7 +176,7 @@ func HandleOAuth(c *gin.Context) {
bindSucceeded, notificationFailed := false, false
if pendingFlow.Intent == model.AuthFlowIntentBind {
defer func() {
recordUserSecurityAudit(c, pendingFlow.UserId, "user.binding_bind", map[string]interface{}{"provider": providerName, "success": bindSucceeded, "notification_failed": notificationFailed})
recordUserSecurityAudit(c, pendingFlow.UserId, "user.binding_bind", map[string]any{"provider": providerName, "success": bindSucceeded, "notification_failed": notificationFailed})
}()
}
// Bind and verification callbacks must use the dashboard session that started them.
@@ -310,7 +310,7 @@ func handleOAuthVerification(c *gin.Context, provider string, oauthUser *oauth.O
writeSecurityOperationError(c, err)
return
}
recordUserSecurityAudit(c, identity.UserID, "user.security_verify", map[string]interface{}{"method": proof.Method, "scope": proof.Scope, "provider": provider})
recordUserSecurityAudit(c, identity.UserID, "user.security_verify", map[string]any{"method": proof.Method, "scope": proof.Scope, "provider": provider})
common.ApiSuccess(c, proof)
}
@@ -527,7 +527,7 @@ func findOrCreateOAuthUser(c *gin.Context, provider oauth.Provider, oauthUser *o
// Set the provider user ID on the user model and update
provider.SetProviderUserID(user, oauthUser.ProviderUserID)
if err := tx.Model(user).Updates(map[string]interface{}{
if err := tx.Model(user).Updates(map[string]any{
"github_id": user.GitHubId,
"discord_id": user.DiscordId,
"oidc_id": user.OidcId,
+1 -1
View File
@@ -421,7 +421,7 @@ func UpdateOption(c *gin.Context) {
return
}
// 出于安全考虑只记录被修改的配置项名称,不记录配置值(可能含密钥等敏感信息)。
recordManageAudit(c, "option.update", map[string]interface{}{
recordManageAudit(c, "option.update", map[string]any{
"key": option.Key,
})
c.JSON(http.StatusOK, gin.H{
+2 -2
View File
@@ -463,7 +463,7 @@ func AdminResetPasskey(c *gin.Context) {
return
}
recordManageAuditFor(c, user.Id, "user.reset_passkey", map[string]interface{}{
recordManageAuditFor(c, user.Id, "user.reset_passkey", map[string]any{
"username": user.Username,
"id": user.Id,
})
@@ -628,7 +628,7 @@ func PasskeyVerifyFinish(c *gin.Context) {
return
}
recordUserSecurityAudit(c, user.Id, "user.security_verify", map[string]interface{}{"method": proof.Method, "scope": proof.Scope})
recordUserSecurityAudit(c, user.Id, "user.security_verify", map[string]any{"method": proof.Method, "scope": proof.Scope})
common.ApiSuccess(c, proof)
}
+3 -3
View File
@@ -1,6 +1,8 @@
package controller
import (
"maps"
"github.com/QuantumNous/new-api/common"
"github.com/QuantumNous/new-api/model"
"github.com/QuantumNous/new-api/service"
@@ -38,9 +40,7 @@ func GetPricing(c *gin.Context) {
userId, exists := c.Get("id")
usableGroup := map[string]string{}
groupRatio := map[string]float64{}
for s, f := range ratio_setting.GetGroupRatioCopy() {
groupRatio[s] = f
}
maps.Copy(groupRatio, ratio_setting.GetGroupRatioCopy())
var group string
if exists {
user, err := model.GetUserCache(userId.(int))
+5 -5
View File
@@ -52,7 +52,7 @@ func nearlyEqual(a, b float64) bool {
return b-a < floatEpsilon
}
func valuesEqual(a, b interface{}) bool {
func valuesEqual(a, b any) bool {
af, aok := a.(float64)
bf, bok := b.(float64)
if aok && bok {
@@ -352,7 +352,7 @@ func FetchUpstreamRatios(c *gin.Context) {
// 简单重试:最多 3 次,指数退避
var resp *http.Response
var lastErr error
for attempt := 0; attempt < 3; attempt++ {
for attempt := range 3 {
resp, lastErr = client.Do(httpReq)
if lastErr == nil {
break
@@ -709,12 +709,12 @@ func buildDifferences(localData map[string]any, successfulChannels []struct {
if expressionPriority && numericPricingSyncFields[ratioType] {
continue
}
var localValue interface{} = nil
var localValue any = nil
if val, exists := valueMap(localData[ratioType])[modelName]; exists {
localValue = normalizeSyncValue(ratioType, val)
}
upstreamValues := make(map[string]interface{})
upstreamValues := make(map[string]any)
confidenceValues := make(map[string]bool)
hasUpstreamValue := false
hasDifference := false
@@ -723,7 +723,7 @@ func buildDifferences(localData map[string]any, successfulChannels []struct {
if expressionPriority && valueMap(channel.data[billing_setting.BillingModeField])[modelName] != billing_setting.BillingModeTieredExpr {
continue
}
var upstreamValue interface{} = nil
var upstreamValue any = nil
if val, exists := valueMap(channel.data[ratioType])[modelName]; exists {
upstreamValue = normalizeSyncValue(ratioType, val)
+1 -1
View File
@@ -121,7 +121,7 @@ func AddRedemption(c *gin.Context) {
}
keys = append(keys, key)
}
recordManageAudit(c, "redemption.create", map[string]interface{}{
recordManageAudit(c, "redemption.create", map[string]any{
"name": redemption.Name,
"count": redemption.Count,
"quota": logger.LogQuota(redemption.Quota),
+2 -2
View File
@@ -80,9 +80,9 @@ func TestProcessChannelErrorUsesSnapshotWithoutLeakingChannelMetadata(t *testing
for _, key := range []string{"channel_id", "channel_name", "channel_type"} {
assert.NotContains(t, storedOther, key)
}
adminInfo, ok := storedOther["admin_info"].(map[string]interface{})
adminInfo, ok := storedOther["admin_info"].(map[string]any)
require.True(t, ok)
assert.Equal(t, []interface{}{"101"}, adminInfo["use_channel"])
assert.Equal(t, []any{"101"}, adminInfo["use_channel"])
logs, total, err := model.GetUserLogs(7, model.LogTypeError, 0, 0, "", "", 0, 10, "", "", "")
require.NoError(t, err)
+1 -1
View File
@@ -131,6 +131,6 @@ func UniversalVerify(c *gin.Context) {
writeSecurityOperationError(c, err)
return
}
recordUserSecurityAudit(c, identity.UserID, "user.security_verify", map[string]interface{}{"method": proof.Method, "scope": proof.Scope})
recordUserSecurityAudit(c, identity.UserID, "user.security_verify", map[string]any{"method": proof.Method, "scope": proof.Scope})
common.ApiSuccess(c, proof)
}
+4 -4
View File
@@ -411,7 +411,7 @@ func TestSecurityAccountProfileReadsPasswordStatusInOneQuery(t *testing.T) {
}
t.Run(name, func(t *testing.T) {
user, identity := setupSecurityEnrollmentTest(t)
updates := map[string]interface{}{"access_token": "private-profile-token", "remark": "private-profile-remark"}
updates := map[string]any{"access_token": "private-profile-token", "remark": "private-profile-remark"}
if !hasPassword {
updates["password"] = ""
}
@@ -433,8 +433,8 @@ func TestSecurityAccountProfileReadsPasswordStatusInOneQuery(t *testing.T) {
response := securityEnrollmentRequest(http.MethodGet, "/api/user/self", "", "", identity, GetSelf)
var result struct {
Success bool `json:"success"`
Data map[string]interface{} `json:"data"`
Success bool `json:"success"`
Data map[string]any `json:"data"`
}
require.NoError(t, common.Unmarshal(response.Body.Bytes(), &result))
require.True(t, result.Success, response.Body.String())
@@ -825,7 +825,7 @@ func TestSecurityAccountUnbindPreservesUsableLoginMethod(t *testing.T) {
common.PasswordLoginEnabled = scenario != "disabled password"
require.NoError(t, model.DB.Create(&model.UserOAuthBinding{UserId: user.Id, ProviderId: 31, ProviderUserId: "linked-subject"}).Error)
if scenario != "password" && scenario != "disabled password" {
require.NoError(t, model.DB.Model(user).Updates(map[string]interface{}{"password": "", "email": "verified@example.com"}).Error)
require.NoError(t, model.DB.Model(user).Updates(map[string]any{"password": "", "email": "verified@example.com"}).Error)
}
if scenario == "passkey" || scenario == "disabled passkey" {
require.NoError(t, model.DB.Create(&model.PasskeyCredential{UserID: user.Id, CredentialID: "existing-key", PublicKey: "public-key"}).Error)
+1 -1
View File
@@ -1059,7 +1059,7 @@ func TestSecurityEnrollmentOAuthVerificationNeverChangesLoginOrBindings(t *testi
for _, scenario := range []string{"success", "different account", "binding changed", "session changed", "auth version changed", "provider disabled", "cancelled"} {
t.Run(scenario, func(t *testing.T) {
user, identity := setupSecurityEnrollmentTest(t)
require.NoError(t, model.DB.Model(user).Updates(map[string]interface{}{"password": "", "github_id": "linked-user", "email": "same@example.com"}).Error)
require.NoError(t, model.DB.Model(user).Updates(map[string]any{"password": "", "github_id": "linked-user", "email": "same@example.com"}).Error)
provider := &enrollmentOAuthProvider{externalID: "linked-user"}
oauth.Register("enrollment-oauth", provider)
t.Cleanup(func() { oauth.Unregister("enrollment-oauth") })
+3 -3
View File
@@ -281,7 +281,7 @@ func AdminUpdateSubscriptionPlan(c *gin.Context) {
err := model.DB.Transaction(func(tx *gorm.DB) error {
// update plan (allow zero values updates with map)
updateMap := map[string]interface{}{
updateMap := map[string]any{
"title": req.Plan.Title,
"subtitle": req.Plan.Subtitle,
"price_amount": req.Plan.PriceAmount,
@@ -467,7 +467,7 @@ func AdminResetUserSubscriptionsByPlan(c *gin.Context) {
return
}
recordSubscriptionResetUserLogs(c, result, auditOperatorInfo(c))
recordManageAuditFor(c, userId, "subscription.user_plan_reset", map[string]interface{}{
recordManageAuditFor(c, userId, "subscription.user_plan_reset", map[string]any{
"target_user_id": userId,
"plan_id": result.PlanId,
"plan_title": result.PlanTitle,
@@ -498,7 +498,7 @@ func AdminResetPlanSubscriptions(c *gin.Context) {
recordSubscriptionResetUserLogs(c, result, auditOperatorInfo(c))
common.SysLog(fmt.Sprintf("admin reset subscription plan %d quota: reset_count=%d user_count=%d advance_reset_time=%t",
result.PlanId, result.ResetCount, result.UserCount, result.AdvanceResetTime))
recordManageAudit(c, "subscription.plan_reset", map[string]interface{}{
recordManageAudit(c, "subscription.plan_reset", map[string]any{
"plan_id": result.PlanId,
"plan_title": result.PlanTitle,
"reset_count": result.ResetCount,
+4 -9
View File
@@ -6,6 +6,7 @@ import (
"encoding/json"
"errors"
"fmt"
"maps"
"net/http"
"net/url"
"sort"
@@ -167,9 +168,7 @@ func ListTaskPlugins(c *gin.Context) {
runtimeErrors := jsplugin.DefaultRegistry.RoutingErrors()
taskPluginSyncState.Lock()
for key, message := range taskPluginSyncState.errors {
runtimeErrors[key] = message
}
maps.Copy(runtimeErrors, taskPluginSyncState.errors)
taskPluginSyncState.Unlock()
items := make([]taskPluginListItem, 0, len(keys))
@@ -249,9 +248,7 @@ func GetTaskPluginRuntime(c *gin.Context) {
pluginErrors := routingStatus.Errors
taskPluginSyncState.Lock()
for key, message := range taskPluginSyncState.errors {
pluginErrors[key] = message
}
maps.Copy(pluginErrors, taskPluginSyncState.errors)
lastRebuild := taskPluginSyncState.lastRebuild
lastDatabaseRevision := lastRebuild.DatabaseRevision
taskPluginSyncState.Unlock()
@@ -797,9 +794,7 @@ func syncTaskPluginsOnceContext(ctx context.Context) error {
}
}
pluginErrors := jsplugin.DefaultRegistry.RoutingErrors()
for key, message := range taskPluginSyncState.errors {
pluginErrors[key] = message
}
maps.Copy(pluginErrors, taskPluginSyncState.errors)
pluginErrorCount := len(pluginErrors)
status := "success"
if pluginErrorCount > 0 {
+2 -3
View File
@@ -7,6 +7,7 @@ import (
"encoding/json"
"errors"
"fmt"
"maps"
"math/big"
"net/http"
"net/http/httptest"
@@ -67,9 +68,7 @@ func setupTelegramOAuthTest(t *testing.T) *telegramOAuthFixture {
common.OptionMapRWMutex.Lock()
previousOptions := common.OptionMap
common.OptionMap = make(map[string]string)
for key, value := range previousOptions {
common.OptionMap[key] = value
}
maps.Copy(common.OptionMap, previousOptions)
common.OptionMapRWMutex.Unlock()
common.TelegramOAuthEnabled = true
*system_setting.GetTelegramSettings() = system_setting.TelegramSettings{ClientID: "12345", ClientSecret: "telegram-client-secret"}
+1 -1
View File
@@ -105,7 +105,7 @@ func GetTopUpInfo(c *gin.Context) {
"enable_redemption": complianceConfirmed,
"payment_compliance_confirmed": complianceConfirmed,
"payment_compliance_terms_version": operation_setting.CurrentComplianceTermsVersion,
"waffo_pay_methods": func() interface{} {
"waffo_pay_methods": func() any {
if enableWaffo {
return setting.GetWaffoPayMethods()
}
+1 -4
View File
@@ -217,10 +217,7 @@ func RequestWaffoPay(c *gin.Context) {
// Token 模式下归一化 Amount(存等价美元/CNY 数量,避免 RechargeWaffo 双重放大)
amount := req.Amount
if operation_setting.GetQuotaDisplayType() == operation_setting.QuotaDisplayTypeTokens {
amount = int64(float64(req.Amount) / common.QuotaPerUnit)
if amount < 1 {
amount = 1
}
amount = max(int64(float64(req.Amount)/common.QuotaPerUnit), 1)
}
// 创建本地订单
+2 -3
View File
@@ -1,6 +1,7 @@
package controller
import (
"maps"
"testing"
"github.com/QuantumNous/new-api/common"
@@ -31,9 +32,7 @@ func TestGetWaffoPancakePayMoney(t *testing.T) {
originalUnitPrice := setting.WaffoPancakeUnitPrice
originalQuotaDisplayType := operation_setting.GetGeneralSetting().QuotaDisplayType
originalDiscounts := make(map[int]float64, len(operation_setting.GetPaymentSetting().AmountDiscount))
for k, v := range operation_setting.GetPaymentSetting().AmountDiscount {
originalDiscounts[k] = v
}
maps.Copy(originalDiscounts, operation_setting.GetPaymentSetting().AmountDiscount)
originalTopupGroupRatio := common.TopupGroupRatio2JSONString()
t.Cleanup(func() {
+1 -1
View File
@@ -96,7 +96,7 @@ func Get2FAStatus(c *gin.Context) {
return
}
status := map[string]interface{}{
status := map[string]any{
"enabled": false,
"locked": false,
}
+2 -3
View File
@@ -35,7 +35,7 @@ type UptimeGroupResult struct {
Monitors []Monitor `json:"monitors"`
}
func getAndDecode(ctx context.Context, client *http.Client, url string, dest interface{}) error {
func getAndDecode(ctx context.Context, client *http.Client, url string, dest any) error {
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
return err
@@ -54,7 +54,7 @@ func getAndDecode(ctx context.Context, client *http.Client, url string, dest int
return json.NewDecoder(resp.Body).Decode(dest)
}
func fetchGroupData(ctx context.Context, client *http.Client, groupConfig map[string]interface{}) UptimeGroupResult {
func fetchGroupData(ctx context.Context, client *http.Client, groupConfig map[string]any) UptimeGroupResult {
url, _ := groupConfig["url"].(string)
slug, _ := groupConfig["slug"].(string)
categoryName, _ := groupConfig["categoryName"].(string)
@@ -143,7 +143,6 @@ func GetUptimeKumaStatus(c *gin.Context) {
g, gCtx := errgroup.WithContext(ctx)
for i, group := range groups {
i, group := i, group
g.Go(func() error {
results[i] = fetchGroupData(gCtx, client, group)
return nil
+24 -24
View File
@@ -134,7 +134,7 @@ func recordLoginAudit(user *model.User, c *gin.Context) {
UserAgent: c.Request.UserAgent(),
}
content := fmt.Sprintf("Logged in successfully via %s", method)
params := map[string]interface{}{
params := map[string]any{
"method": method,
}
if verifiedMethod := c.GetString("login_verification_method"); verifiedMethod != "" {
@@ -486,11 +486,11 @@ func GetSelf(c *gin.Context) {
// buildSelfUserData is the single safe dashboard-user DTO used by GetSelf,
// login and refresh. It intentionally excludes password, management PAT and
// administrator-only remarks.
func buildSelfUserData(user *model.User) map[string]interface{} {
func buildSelfUserData(user *model.User) map[string]any {
userSetting := user.GetSetting()
permissions := calculateUserPermissions(user.Role)
permissions["admin_permissions"] = authz.Capabilities(user.Id, user.Role)
return map[string]interface{}{
return map[string]any{
"id": user.Id,
"username": user.Username,
"display_name": user.DisplayName,
@@ -521,26 +521,26 @@ func buildSelfUserData(user *model.User) map[string]interface{} {
}
// 计算用户权限的辅助函数
func calculateUserPermissions(userRole int) map[string]interface{} {
permissions := map[string]interface{}{}
func calculateUserPermissions(userRole int) map[string]any {
permissions := map[string]any{}
// 根据用户角色计算权限
if userRole == common.RoleRootUser {
// 超级管理员不需要边栏设置功能
permissions["sidebar_settings"] = false
permissions["sidebar_modules"] = map[string]interface{}{}
permissions["sidebar_modules"] = map[string]any{}
} else if userRole == common.RoleAdminUser {
// 管理员可以设置边栏,但不包含系统设置功能
permissions["sidebar_settings"] = true
permissions["sidebar_modules"] = map[string]interface{}{
"admin": map[string]interface{}{
permissions["sidebar_modules"] = map[string]any{
"admin": map[string]any{
"setting": false, // 管理员不能访问系统设置
},
}
} else {
// 普通用户只能设置个人功能,不包含管理员区域
permissions["sidebar_settings"] = true
permissions["sidebar_modules"] = map[string]interface{}{
permissions["sidebar_modules"] = map[string]any{
"admin": false, // 普通用户不能访问管理员区域
}
}
@@ -550,17 +550,17 @@ func calculateUserPermissions(userRole int) map[string]interface{} {
// 根据用户角色生成默认的边栏配置
func generateDefaultSidebarConfig(userRole int) string {
defaultConfig := map[string]interface{}{}
defaultConfig := map[string]any{}
// 聊天区域 - 所有用户都可以访问
defaultConfig["chat"] = map[string]interface{}{
defaultConfig["chat"] = map[string]any{
"enabled": true,
"playground": true,
"chat": true,
}
// 控制台区域 - 所有用户都可以访问
defaultConfig["console"] = map[string]interface{}{
defaultConfig["console"] = map[string]any{
"enabled": true,
"detail": true,
"token": true,
@@ -570,7 +570,7 @@ func generateDefaultSidebarConfig(userRole int) string {
}
// 个人中心区域 - 所有用户都可以访问
defaultConfig["personal"] = map[string]interface{}{
defaultConfig["personal"] = map[string]any{
"enabled": true,
"topup": true,
"personal": true,
@@ -579,7 +579,7 @@ func generateDefaultSidebarConfig(userRole int) string {
// 管理员区域 - 根据角色决定
if userRole == common.RoleAdminUser {
// 管理员可以访问管理员区域,但不能访问系统设置
defaultConfig["admin"] = map[string]interface{}{
defaultConfig["admin"] = map[string]any{
"enabled": true,
"channel": true,
"models": true,
@@ -589,7 +589,7 @@ func generateDefaultSidebarConfig(userRole int) string {
}
} else if userRole == common.RoleRootUser {
// 超级管理员可以访问所有功能
defaultConfig["admin"] = map[string]interface{}{
defaultConfig["admin"] = map[string]any{
"enabled": true,
"channel": true,
"models": true,
@@ -704,7 +704,7 @@ func UpdateUser(c *gin.Context) {
common.ApiError(c, err)
return
}
recordManageAuditFor(c, updatedUser.Id, "user.update", map[string]interface{}{
recordManageAuditFor(c, updatedUser.Id, "user.update", map[string]any{
"username": originUser.Username,
"id": updatedUser.Id,
})
@@ -745,7 +745,7 @@ func AdminClearUserBinding(c *gin.Context) {
return
}
recordManageAuditFor(c, user.Id, "user.binding_clear", map[string]interface{}{
recordManageAuditFor(c, user.Id, "user.binding_clear", map[string]any{
"bindingType": bindingType,
"username": user.Username,
})
@@ -757,7 +757,7 @@ func AdminClearUserBinding(c *gin.Context) {
}
func UpdateSelf(c *gin.Context) {
var requestData map[string]interface{}
var requestData map[string]any
if err := common.DecodeJson(c.Request.Body, &requestData); err != nil {
common.ApiErrorI18n(c, i18n.MsgInvalidParams)
return
@@ -771,7 +771,7 @@ func UpdateSelf(c *gin.Context) {
succeeded, notificationFailed := false, false
if passwordRequested {
defer func() {
recordUserSecurityAudit(c, c.GetInt("id"), "user.password_change", map[string]interface{}{"success": succeeded, "notification_failed": notificationFailed})
recordUserSecurityAudit(c, c.GetInt("id"), "user.password_change", map[string]any{"success": succeeded, "notification_failed": notificationFailed})
}()
}
// 检查是否是用户设置更新请求 (sidebar_modules 或 language)
@@ -928,7 +928,7 @@ func DeleteUser(c *gin.Context) {
common.ApiError(c, err)
return
}
recordManageAuditFor(c, originUser.Id, "user.delete", map[string]interface{}{
recordManageAuditFor(c, originUser.Id, "user.delete", map[string]any{
"username": originUser.Username,
"id": originUser.Id,
})
@@ -943,7 +943,7 @@ func DeleteSelf(c *gin.Context) {
setAuthNoStore(c)
succeeded := false
defer func() {
recordUserSecurityAudit(c, c.GetInt("id"), "user.account_delete", map[string]interface{}{"success": succeeded})
recordUserSecurityAudit(c, c.GetInt("id"), "user.account_delete", map[string]any{"success": succeeded})
}()
if middleware.RequireSecurityProof(c, service.VerificationOperation{Scope: service.VerificationScopeAccountDelete}) == nil {
return
@@ -1013,7 +1013,7 @@ func CreateUser(c *gin.Context) {
}
cleanUser.FinishInsert(0)
recordManageAuditFor(c, cleanUser.Id, "user.create", map[string]interface{}{
recordManageAuditFor(c, cleanUser.Id, "user.create", map[string]any{
"username": cleanUser.Username,
"role": cleanUser.Role,
})
@@ -1100,7 +1100,7 @@ func ManageUser(c *gin.Context) {
if err := model.InvalidateUserTokensCache(user.Id); err != nil {
common.SysLog(fmt.Sprintf("failed to invalidate tokens cache for user %d: %s", user.Id, err.Error()))
}
recordManageAuditFor(c, user.Id, "user.manage", map[string]interface{}{
recordManageAuditFor(c, user.Id, "user.manage", map[string]any{
"action": req.Action,
"username": user.Username,
"id": user.Id,
@@ -1170,7 +1170,7 @@ func ManageUser(c *gin.Context) {
if err := model.InvalidateUserTokensCache(user.Id); err != nil {
common.SysLog(fmt.Sprintf("failed to invalidate tokens cache for user %d: %s", user.Id, err.Error()))
}
recordManageAuditFor(c, user.Id, "user.manage", map[string]interface{}{
recordManageAuditFor(c, user.Id, "user.manage", map[string]any{
"action": req.Action,
"username": user.Username,
"id": user.Id,
+1 -1
View File
@@ -136,7 +136,7 @@ func WeChatBind(c *gin.Context) {
}
succeeded, notificationFailed := false, false
defer func() {
recordUserSecurityAudit(c, identity.UserID, "user.binding_bind", map[string]interface{}{"provider": "wechat", "success": succeeded, "notification_failed": notificationFailed})
recordUserSecurityAudit(c, identity.UserID, "user.binding_bind", map[string]any{"provider": "wechat", "success": succeeded, "notification_failed": notificationFailed})
}()
if !common.WeChatAuthEnabled {
c.JSON(http.StatusOK, gin.H{
+4 -4
View File
@@ -27,10 +27,10 @@ type MidjourneyRequest struct {
}
type MidjourneyResponse struct {
Code int `json:"code"`
Description string `json:"description"`
Properties interface{} `json:"properties"`
Result string `json:"result"`
Code int `json:"code"`
Description string `json:"description"`
Properties any `json:"properties"`
Result string `json:"result"`
}
type MidjourneyUploadResponse struct {
+7 -7
View File
@@ -42,13 +42,13 @@ type SunoSong struct {
}
type SunoMetadata struct {
Tags string `json:"tags"`
Prompt string `json:"prompt"`
GPTDescriptionPrompt interface{} `json:"gpt_description_prompt"`
AudioPromptID interface{} `json:"audio_prompt_id"`
Duration interface{} `json:"duration"`
ErrorType interface{} `json:"error_type"`
ErrorMessage interface{} `json:"error_message"`
Tags string `json:"tags"`
Prompt string `json:"prompt"`
GPTDescriptionPrompt any `json:"gpt_description_prompt"`
AudioPromptID any `json:"audio_prompt_id"`
Duration any `json:"duration"`
ErrorType any `json:"error_type"`
ErrorMessage any `json:"error_message"`
}
type SunoLyrics struct {
+1 -1
View File
@@ -151,7 +151,7 @@ func finishAdminAudit(c *gin.Context, writer *auditResponseWriter) {
}
// op.params 为语言无关参数,供前端 i18n 渲染;generic 时携带 method/route。
opParams := map[string]interface{}{}
opParams := map[string]any{}
if action == "generic" {
opParams["method"] = method
opParams["route"] = route
+2 -2
View File
@@ -365,8 +365,8 @@ func TokenAuth() func(c *gin.Context) {
// Sec-WebSocket-Protocol: realtime, openai-insecure-api-key.sk-xxx, openai-beta.realtime-v1
// read sk from Sec-WebSocket-Protocol
key := c.Request.Header.Get("Sec-WebSocket-Protocol")
parts := strings.Split(key, ",")
for _, part := range parts {
parts := strings.SplitSeq(key, ",")
for part := range parts {
part = strings.TrimSpace(part)
if strings.HasPrefix(part, "openai-insecure-api-key") {
key = strings.TrimPrefix(part, "openai-insecure-api-key.")
+1 -1
View File
@@ -49,7 +49,7 @@ func redisUserRateLimitKey(mark string, userID int) string {
return fmt.Sprintf("%s:user:%s:%d", redisRateLimitNamespace, mark, userID)
}
func redisReplyInteger(value interface{}) (int64, error) {
func redisReplyInteger(value any) (int64, error) {
switch typed := value.(type) {
case int64:
return typed, nil
+1 -1
View File
@@ -139,7 +139,7 @@ func popTaskArtifactAccessQuery(request *http.Request) (string, bool, bool) {
count := 0
invalid := false
kept := make([]string, 0)
for _, part := range strings.Split(request.URL.RawQuery, "&") {
for part := range strings.SplitSeq(request.URL.RawQuery, "&") {
rawKey, rawValue, _ := strings.Cut(part, "=")
key, err := url.QueryUnescape(rawKey)
if err != nil || key != service.TaskArtifactAccessQueryParameter {
+1 -1
View File
@@ -116,7 +116,7 @@ func RecordAuditLog(c *gin.Context, entry AuditLog) {
logger.LogError(ctx, fmt.Sprintf("audit log write failed (request_id=%s): log database unavailable", entry.RequestId))
return
}
var row interface{} = &entry
var row any = &entry
if common.UsingLogDatabase(common.DatabaseTypeClickHouse) {
encoded, err := common.Marshal(entry.Other)
if err != nil {
+10 -10
View File
@@ -173,7 +173,7 @@ func (c ChannelInfo) Value() (driver.Value, error) {
}
// Scan implements sql.Scanner interface
func (c *ChannelInfo) Scan(value interface{}) error {
func (c *ChannelInfo) Scan(value any) error {
return common.Unmarshal(jsonScanBytes(value), c)
}
@@ -271,7 +271,7 @@ func (channel *Channel) GetNextEnabledKey() (string, int, *types.NewAPIError) {
if start < 0 || start >= len(keys) {
start = 0
}
for i := 0; i < len(keys); i++ {
for i := range keys {
idx := (start + i) % len(keys)
if getStatus(idx) == common.ChannelStatusEnabled {
// update polling index for next call (point to the next position)
@@ -309,8 +309,8 @@ func (channel *Channel) GetGroups() []string {
return groups
}
func (channel *Channel) GetOtherInfo() map[string]interface{} {
otherInfo := make(map[string]interface{})
func (channel *Channel) GetOtherInfo() map[string]any {
otherInfo := make(map[string]any)
if channel.OtherInfo != "" {
err := common.Unmarshal([]byte(channel.OtherInfo), &otherInfo)
if err != nil {
@@ -320,7 +320,7 @@ func (channel *Channel) GetOtherInfo() map[string]interface{} {
return otherInfo
}
func (channel *Channel) SetOtherInfo(otherInfo map[string]interface{}) {
func (channel *Channel) SetOtherInfo(otherInfo map[string]any) {
otherInfoBytes, err := json.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))
@@ -659,7 +659,7 @@ func CleanupChannelPollingLocks() {
activeChannelSet[id] = true
}
channelPollingLocks.Range(func(key, value interface{}) bool {
channelPollingLocks.Range(func(key, value any) bool {
channelId := key.(int)
if !activeChannelSet[channelId] {
channelPollingLocks.Delete(channelId)
@@ -1054,8 +1054,8 @@ func (channel *Channel) SetOtherSettings(setting dto.ChannelOtherSettings) {
channel.OtherSettings = string(settingBytes)
}
func (channel *Channel) GetParamOverride() map[string]interface{} {
paramOverride := make(map[string]interface{})
func (channel *Channel) GetParamOverride() map[string]any {
paramOverride := make(map[string]any)
if channel.ParamOverride != nil && *channel.ParamOverride != "" {
err := common.Unmarshal([]byte(*channel.ParamOverride), &paramOverride)
if err != nil {
@@ -1065,8 +1065,8 @@ func (channel *Channel) GetParamOverride() map[string]interface{} {
return paramOverride
}
func (channel *Channel) GetHeaderOverride() map[string]interface{} {
headerOverride := make(map[string]interface{})
func (channel *Channel) GetHeaderOverride() map[string]any {
headerOverride := make(map[string]any)
if channel.HeaderOverride != nil && *channel.HeaderOverride != "" {
err := common.Unmarshal([]byte(*channel.HeaderOverride), &headerOverride)
if err != nil {
+2 -2
View File
@@ -56,8 +56,8 @@ func InitChannelCache() {
if channel.Status != common.ChannelStatusEnabled {
continue // skip disabled channels
}
groups := strings.Split(channel.Group, ",")
for _, group := range groups {
groups := strings.SplitSeq(channel.Group, ",")
for group := range groups {
models := channel.GetModels()
for _, model := range models {
if _, ok := newGroup2model2channels[group][model]; !ok {
+3 -6
View File
@@ -1,6 +1,8 @@
package model
import (
"slices"
"github.com/QuantumNous/new-api/common"
"github.com/QuantumNous/new-api/setting/ratio_setting"
)
@@ -62,10 +64,5 @@ func isChannelEnabledForGroupModelDB(group string, modelName string, channelID i
}
func isChannelIDInList(list []int, channelID int) bool {
for _, id := range list {
if id == channelID {
return true
}
}
return false
return slices.Contains(list, channelID)
}
+1 -1
View File
@@ -82,7 +82,7 @@ func TestSaveStatusStateFromSingleKeySnapshotPreservesUnownedColumns(t *testing.
}).Error)
stale.Status = common.ChannelStatusManuallyDisabled
stale.SetOtherInfo(map[string]interface{}{
stale.SetOtherInfo(map[string]any{
"status_reason": "manual operation",
"status_time": int64(1234),
})
+2 -2
View File
@@ -141,7 +141,7 @@ func userCheckinWithoutTransaction(checkin *Checkin, userId int, quotaAwarded in
}
// GetUserCheckinStats 获取用户签到统计信息
func GetUserCheckinStats(userId int, month string) (map[string]interface{}, error) {
func GetUserCheckinStats(userId int, month string) (map[string]any, error) {
// 获取指定月份的所有签到记录
startDate := month + "-01"
endDate := month + "-31"
@@ -169,7 +169,7 @@ func GetUserCheckinStats(userId int, month string) (map[string]interface{}, erro
DB.Model(&Checkin{}).Where("user_id = ?", userId).Count(&totalCheckins)
DB.Model(&Checkin{}).Where("user_id = ?", userId).Select("COALESCE(SUM(quota_awarded), 0)").Scan(&totalQuota)
return map[string]interface{}{
return map[string]any{
"total_quota": totalQuota, // 所有时间累计获得的额度
"total_checkins": totalCheckins, // 所有时间累计签到次数
"checkin_count": len(records), // 本月签到次数
+1 -1
View File
@@ -52,7 +52,7 @@ type sanitizedLogWriter struct {
delegate *log.Logger
}
func (s *sanitizedLogWriter) Printf(format string, args ...interface{}) {
func (s *sanitizedLogWriter) Printf(format string, args ...any) {
if !common.DebugEnabled {
for i, arg := range args {
if err, ok := arg.(error); ok {
+1 -1
View File
@@ -68,7 +68,7 @@ func TestJSONColumnValuersZeroValueIsNil(t *testing.T) {
// 保护契约:json 列的 Scan 必须同时接受 []byte 与 string——不同驱动/协议
// 模式返回类型不同,静默丢弃 string 会把已有数据清零。
func TestJSONColumnScannersAcceptStringAndBytes(t *testing.T) {
toInput := func(kind string, payload string) interface{} {
toInput := func(kind string, payload string) any {
if kind == "bytes" {
return []byte(payload)
}
+3 -3
View File
@@ -211,7 +211,7 @@ func RecordLogWithAdminInfo(userId int, logType int, content string, adminInfo *
// username 由调用方传入(登录流程已持有用户对象),避免额外的数据库查询。
// content 为英文兜底文本(用于导出);action+params 供前端本地化渲染。
// other 包含 login_method、user_agent 等结构化信息。
func RecordLoginLog(userId, actorRole int, username string, content string, ip string, action string, params map[string]interface{}, other AuditOther, request ...*gin.Context) {
func RecordLoginLog(userId, actorRole int, username string, content string, ip string, action string, params map[string]any, other AuditOther, request ...*gin.Context) {
other.Op = &AuditOperation{Action: action, Params: params}
var c *gin.Context
if len(request) > 0 {
@@ -226,7 +226,7 @@ func RecordLoginLog(userId, actorRole int, username string, content string, ip s
// action+params 写入 Other.op,供前端本地化渲染(普通用户可见,不含敏感信息)。
// adminInfo 存放操作者身份(写入 Other.admin_info,普通用户查询时剥离);
// auditInfo 存放路由/方法/结果等中间件兜底信息(写入 Other.audit_info,普通用户查询时剥离)。
func RecordOperationAuditLog(logUserId, actorRole int, content string, ip string, action string, params map[string]interface{}, adminInfo *AuditAdminInfo, auditInfo *AuditRequestInfo, request ...*gin.Context) {
func RecordOperationAuditLog(logUserId, actorRole int, content string, ip string, action string, params map[string]any, adminInfo *AuditAdminInfo, auditInfo *AuditRequestInfo, request ...*gin.Context) {
username, _ := GetUsernameById(logUserId, false)
other := AuditOther{
Op: &AuditOperation{Action: action, Params: params},
@@ -252,7 +252,7 @@ func RecordOperationAuditLog(logUserId, actorRole int, content string, ip string
func RecordTopupLog(userId int, content string, callerIp string, paymentMethod string, callbackPaymentMethod string) {
username, _ := GetUsernameById(userId, false)
other := NewLogOther()
other.MergeAdmin(map[string]interface{}{
other.MergeAdmin(map[string]any{
"server_ip": common.GetIp(),
"node_name": common.NodeName,
"caller_ip": callerIp,
+18 -18
View File
@@ -13,10 +13,10 @@ import (
// saturation marker (nested under other.admin_info) is removed for non-admin
// log views, since formatUserLogs strips the whole admin_info object.
func TestFormatUserLogsStripsQuotaSaturation(t *testing.T) {
other := common.MapToJsonStr(map[string]interface{}{
other := common.MapToJsonStr(map[string]any{
"model_price": 0.004,
"admin_info": map[string]interface{}{
"quota_saturation": map[string]interface{}{
"admin_info": map[string]any{
"quota_saturation": map[string]any{
"op": "QuotaFromDecimal",
"kind": "overflow",
"clamped": common.MaxQuota,
@@ -36,18 +36,18 @@ func TestFormatUserLogsStripsQuotaSaturation(t *testing.T) {
}
func TestTaskPluginLogVisibilityIsRoleSeparated(t *testing.T) {
other := common.MapToJsonStr(map[string]interface{}{
other := common.MapToJsonStr(map[string]any{
"model_price": 1.25,
"admin_info": map[string]interface{}{
"task_plugin": map[string]interface{}{
"admin_info": map[string]any{
"task_plugin": map[string]any{
"key": "document-parser",
"name": "Document Parser",
"version": "1.2.3",
},
},
"root_info": map[string]interface{}{
"root_info": map[string]any{
"upstream_task_id": "upstream-private",
"task_plugin": map[string]interface{}{
"task_plugin": map[string]any{
"generation": 42,
},
},
@@ -86,19 +86,19 @@ func TestTaskPluginLogVisibilityIsRoleSeparated(t *testing.T) {
}
func TestLegacyLogOtherVisibilityIsRoleSeparated(t *testing.T) {
other := common.MapToJsonStr(map[string]interface{}{
other := common.MapToJsonStr(map[string]any{
"request_path": "/v1/chat/completions",
"channel_id": 202,
"channel_name": "legacy-secret-channel",
"channel_type": 1,
"reject_reason": "legacy-policy-rejection",
"admin_info": map[string]interface{}{
"admin_info": map[string]any{
"existing_admin_field": "preserved",
},
"root_info": map[string]interface{}{
"root_info": map[string]any{
"upstream_request_id": "upstream-private",
},
"audit_info": map[string]interface{}{
"audit_info": map[string]any{
"method": "POST",
},
})
@@ -143,7 +143,7 @@ func TestLegacyLogOtherVisibilityIsRoleSeparated(t *testing.T) {
assert.NotContains(t, parsed, "reject_reason")
assert.NotContains(t, parsed, "root_info")
assert.Contains(t, parsed, "audit_info")
adminInfo, ok := parsed["admin_info"].(map[string]interface{})
adminInfo, ok := parsed["admin_info"].(map[string]any)
require.True(t, ok)
assert.Equal(t, "preserved", adminInfo["existing_admin_field"])
assert.Equal(t, "legacy-policy-rejection", adminInfo["reject_reason"])
@@ -160,7 +160,7 @@ func TestLegacyLogOtherVisibilityIsRoleSeparated(t *testing.T) {
assert.NotContains(t, parsed, "reject_reason")
assert.Contains(t, parsed, "root_info")
assert.Contains(t, parsed, "audit_info")
adminInfo, ok := parsed["admin_info"].(map[string]interface{})
adminInfo, ok := parsed["admin_info"].(map[string]any)
require.True(t, ok)
assert.Equal(t, "preserved", adminInfo["existing_admin_field"])
assert.Equal(t, "legacy-policy-rejection", adminInfo["reject_reason"])
@@ -168,9 +168,9 @@ func TestLegacyLogOtherVisibilityIsRoleSeparated(t *testing.T) {
}
func TestLegacyRejectReasonDoesNotOverrideScopedValue(t *testing.T) {
other := common.MapToJsonStr(map[string]interface{}{
other := common.MapToJsonStr(map[string]any{
"reject_reason": "legacy-value",
"admin_info": map[string]interface{}{
"admin_info": map[string]any{
"reject_reason": "scoped-value",
},
})
@@ -181,7 +181,7 @@ func TestLegacyRejectReasonDoesNotOverrideScopedValue(t *testing.T) {
parsed, err := common.StrToMap(logs[0].Other)
require.NoError(t, err)
assert.NotContains(t, parsed, "reject_reason")
adminInfo, ok := parsed["admin_info"].(map[string]interface{})
adminInfo, ok := parsed["admin_info"].(map[string]any)
require.True(t, ok)
assert.Equal(t, "scoped-value", adminInfo["reject_reason"])
}
@@ -194,7 +194,7 @@ func TestLegacyRejectReasonHandlesNullAdminInfo(t *testing.T) {
parsed, err := common.StrToMap(logs[0].Other)
require.NoError(t, err)
assert.NotContains(t, parsed, "reject_reason")
adminInfo, ok := parsed["admin_info"].(map[string]interface{})
adminInfo, ok := parsed["admin_info"].(map[string]any)
require.True(t, ok)
assert.Equal(t, "legacy-value", adminInfo["reject_reason"])
}
+6 -6
View File
@@ -11,19 +11,19 @@ func TestLogOtherScopesAndMerges(t *testing.T) {
var other LogOther
assert.True(t, other.SetPublic("request_path", "/v1/chat/completions"))
other.MergePublic(map[string]interface{}{
other.MergePublic(map[string]any{
"zero": 0,
})
assert.True(t, other.SetAdmin("use_channel", []string{"channel-a"}))
other.MergeAdmin(map[string]interface{}{
other.MergeAdmin(map[string]any{
"rejected": false,
})
assert.True(t, other.SetRoot("upstream_request_id", "upstream-private"))
other.MergeRoot(map[string]interface{}{
other.MergeRoot(map[string]any{
"generation": 0,
})
assert.True(t, other.SetAudit("method", "POST"))
other.MergeAudit(map[string]interface{}{
other.MergeAudit(map[string]any{
"success": false,
})
@@ -59,10 +59,10 @@ func TestLogOtherRejectsSensitivePublicFields(t *testing.T) {
} {
assert.False(t, other.SetPublic(key, "must-not-leak"), key)
}
other.MergePublic(map[string]interface{}{
other.MergePublic(map[string]any{
"request_path": "/v1/responses",
"channel_name": "still-must-not-leak",
"admin_info": map[string]interface{}{"secret": true},
"admin_info": map[string]any{"secret": true},
})
require.JSONEq(t, `{"request_path":"/v1/responses"}`, other.JSONString())
+1 -1
View File
@@ -29,7 +29,7 @@ var logGroupCol string
// jsonScanBytes 归一化 json 列的驱动返回值:不同驱动/协议模式下同一列可能
// 以 []byte 或 string 返回,静默丢弃 string 会导致字段被清零而不报错。
func jsonScanBytes(value interface{}) []byte {
func jsonScanBytes(value any) []byte {
switch v := value.(type) {
case []byte:
return v
+2 -6
View File
@@ -5,6 +5,7 @@ import (
"errors"
"fmt"
"math"
"slices"
"sort"
"strings"
"sync"
@@ -56,12 +57,7 @@ var modelPricingOptionKeys = []string{
var modelPricingMutationMu sync.Mutex
func IsModelPricingOption(key string) bool {
for _, candidate := range modelPricingOptionKeys {
if key == candidate {
return true
}
}
return false
return slices.Contains(modelPricingOptionKeys, key)
}
func ModelPricingVersion(values PricingValues) string {
+2 -3
View File
@@ -1,6 +1,7 @@
package model
import (
"maps"
"strconv"
"strings"
"time"
@@ -187,9 +188,7 @@ func InitOptionMap() {
// 自动添加所有注册的模型配置
modelConfigs := config.GlobalConfig.ExportAllConfigs()
for k, v := range modelConfigs {
common.OptionMap[k] = v
}
maps.Copy(common.OptionMap, modelConfigs)
common.OptionMapRWMutex.Unlock()
loadOptionsFromDatabase()
+1 -1
View File
@@ -168,7 +168,7 @@ func UpdatePasskeyAssertionState(userID int, credential *webauthn.Credential, la
credentialID := base64.StdEncoding.EncodeToString(credential.ID)
result := DB.Model(&PasskeyCredential{}).
Where("user_id = ? AND credential_id = ?", userID, credentialID).
Updates(map[string]interface{}{
Updates(map[string]any{
"sign_count": credential.Authenticator.SignCount,
"clone_warning": credential.Authenticator.CloneWarning,
"user_present": credential.Flags.UserPresent,
+1 -1
View File
@@ -36,7 +36,7 @@ func UpsertPerfMetric(metric *PerfMetric) error {
{Name: "group"},
{Name: "bucket_ts"},
},
DoUpdates: clause.Assignments(map[string]interface{}{
DoUpdates: clause.Assignments(map[string]any{
"request_count": gorm.Expr("perf_metrics.request_count + ?", metric.RequestCount),
"success_count": gorm.Expr("perf_metrics.success_count + ?", metric.SuccessCount),
"total_latency_ms": gorm.Expr("perf_metrics.total_latency_ms + ?", metric.TotalLatencyMs),
+1 -1
View File
@@ -30,7 +30,7 @@ func (j JSONValue) Value() (driver.Value, error) {
}
// Scan 实现 sql.Scanner 接口,兼容不同驱动返回的类型
func (j *JSONValue) Scan(value interface{}) error {
func (j *JSONValue) Scan(value any) error {
switch v := value.(type) {
case nil:
*j = nil
+5 -7
View File
@@ -251,12 +251,12 @@ func updatePricing() {
if strings.TrimSpace(meta.Endpoints) == "" {
continue
}
var raw map[string]interface{}
var raw map[string]any
if err := common.Unmarshal([]byte(meta.Endpoints), &raw); err == nil {
endpoints := modelSupportEndpointsStr[modelName]
for k, v := range raw {
switch v.(type) {
case string, map[string]interface{}:
case string, map[string]any:
endpoints = appendPricingEndpoint(endpoints, k)
}
}
@@ -293,13 +293,13 @@ func updatePricing() {
if strings.TrimSpace(meta.Endpoints) == "" {
continue
}
var raw map[string]interface{}
var raw map[string]any
if err := common.Unmarshal([]byte(meta.Endpoints), &raw); err == nil {
for k, v := range raw {
switch val := v.(type) {
case string:
supportedEndpointMap[k] = common.EndpointInfo{Path: val, Method: "POST"}
case map[string]interface{}:
case map[string]any:
ep := common.EndpointInfo{Method: "POST"}
if p, ok := val["path"].(string); ok {
ep.Path = p
@@ -399,9 +399,7 @@ func updatePricing() {
pricing.BillingUsageExamples = make([]jsplugin.UsageExample, len(plugin.Meta.UsageExamples))
for index, example := range plugin.Meta.UsageExamples {
facts := make(map[string]any, len(example.Facts))
for key, value := range example.Facts {
facts[key] = value
}
maps.Copy(facts, example.Facts)
pricing.BillingUsageExamples[index] = jsplugin.UsageExample{
Label: example.Label,
Facts: facts,
+2 -2
View File
@@ -126,7 +126,7 @@ func persistTokenQuotaDelta(id int, delta int) error {
return nil
}
result := DB.Model(&Token{}).Where("id = ?", id).Updates(
map[string]interface{}{
map[string]any{
"remain_quota": gorm.Expr("remain_quota + ?", delta),
"used_quota": gorm.Expr("used_quota - ?", delta),
"accessed_time": common.GetTimestamp(),
@@ -151,7 +151,7 @@ func reserveUserQuotaDB(id int, quota int) (bool, error) {
func reserveTokenQuotaDB(id int, quota int) (bool, error) {
result := DB.Model(&Token{}).
Where("id = ? AND remain_quota >= ?", id, quota).
Updates(map[string]interface{}{
Updates(map[string]any{
"remain_quota": gorm.Expr("remain_quota - ?", quota),
"used_quota": gorm.Expr("used_quota + ?", quota),
"accessed_time": common.GetTimestamp(),
+2 -2
View File
@@ -59,14 +59,14 @@ func resetBatchUpdateTestState(t *testing.T) {
t.Helper()
oldBatchEnabled := common.BatchUpdateEnabled
common.BatchUpdateEnabled = false
for i := 0; i < BatchUpdateTypeCount; i++ {
for i := range BatchUpdateTypeCount {
batchUpdateLocks[i].Lock()
batchUpdateStores[i] = make(map[int]int)
batchUpdateLocks[i].Unlock()
}
t.Cleanup(func() {
common.BatchUpdateEnabled = oldBatchEnabled
for i := 0; i < BatchUpdateTypeCount; i++ {
for i := range BatchUpdateTypeCount {
batchUpdateLocks[i].Lock()
batchUpdateStores[i] = make(map[int]int)
batchUpdateLocks[i].Unlock()
+1 -1
View File
@@ -164,7 +164,7 @@ func Redeem(key string, userId int) (quota int, err error) {
// same code loses here even without a row lock (e.g. on SQLite).
result := tx.Model(&Redemption{}).
Where("id = ? AND status = ?", redemption.Id, common.RedemptionCodeStatusEnabled).
Updates(map[string]interface{}{
Updates(map[string]any{
"redeemed_time": common.GetTimestamp(),
"status": common.RedemptionCodeStatusUsed,
"used_user_id": userId,
+1 -1
View File
@@ -186,7 +186,7 @@ func TestRedeemConcurrentSingleSuccess(t *testing.T) {
successes := make([]bool, goroutines)
var wg sync.WaitGroup
wg.Add(goroutines)
for i := 0; i < goroutines; i++ {
for i := range goroutines {
go func(idx int) {
defer wg.Done()
if _, err := Redeem(key, userId); err == nil {
+3 -6
View File
@@ -939,7 +939,7 @@ func AdminInvalidateUserSubscription(userSubscriptionId int) (string, error) {
return err
}
userId = sub.UserId
if err := tx.Model(&sub).Updates(map[string]interface{}{
if err := tx.Model(&sub).Updates(map[string]any{
"status": "cancelled",
"end_time": now,
"updated_at": now,
@@ -1164,7 +1164,7 @@ func ExpireDueSubscriptions(limit int) (int, error) {
err := DB.Transaction(func(tx *gorm.DB) error {
res := tx.Model(&UserSubscription{}).
Where("user_id = ? AND status = ? AND end_time > 0 AND end_time <= ?", userId, "active", now).
Updates(map[string]interface{}{
Updates(map[string]any{
"status": "expired",
"updated_at": common.GetTimestamp(),
})
@@ -1521,10 +1521,7 @@ func PostConsumeUserSubscriptionDelta(userSubscriptionId int, delta int64) error
First(&sub).Error; err != nil {
return err
}
newUsed := sub.AmountUsed + delta
if newUsed < 0 {
newUsed = 0
}
newUsed := max(sub.AmountUsed+delta, 0)
if sub.AmountTotal > 0 && newUsed > sub.AmountTotal {
return fmt.Errorf("subscription used exceeds total, used=%d total=%d", newUsed, sub.AmountTotal)
}
+2 -2
View File
@@ -86,7 +86,7 @@ type Properties struct {
OriginModelName string `json:"origin_model_name,omitempty"`
}
func (m *Properties) Scan(val interface{}) error {
func (m *Properties) Scan(val any) error {
bytesValue := jsonScanBytes(val)
if len(bytesValue) == 0 {
*m = Properties{}
@@ -190,7 +190,7 @@ func GenerateTaskID() string {
return "task_" + key
}
func (p *TaskPrivateData) Scan(val interface{}) error {
func (p *TaskPrivateData) Scan(val any) error {
bytesValue := jsonScanBytes(val)
if len(bytesValue) == 0 {
return nil
+1 -1
View File
@@ -289,7 +289,7 @@ func TestUpdateWithStatus_ConcurrentWinner(t *testing.T) {
var wg sync.WaitGroup
wg.Add(goroutines)
for i := 0; i < goroutines; i++ {
for i := range goroutines {
go func(idx int) {
defer wg.Done()
t := &Task{}
+4 -4
View File
@@ -92,8 +92,8 @@ func (token *Token) GetIpLimits() []string {
if cleanIps == "" {
return ipLimits
}
ips := strings.Split(cleanIps, "\n")
for _, ip := range ips {
ips := strings.SplitSeq(cleanIps, "\n")
for ip := range ips {
ip = strings.TrimSpace(ip)
ip = strings.ReplaceAll(ip, ",", "")
if ip != "" {
@@ -396,7 +396,7 @@ func IncreaseTokenQuota(tokenId int, key string, quota int) (err error) {
func increaseTokenQuota(id int, quota int) (err error) {
err = DB.Model(&Token{}).Where("id = ?", id).Updates(
map[string]interface{}{
map[string]any{
"remain_quota": gorm.Expr("remain_quota + ?", quota),
"used_quota": gorm.Expr("used_quota - ?", quota),
"accessed_time": common.GetTimestamp(),
@@ -425,7 +425,7 @@ func DecreaseTokenQuota(id int, key string, quota int) (err error) {
func decreaseTokenQuota(id int, quota int) (err error) {
err = DB.Model(&Token{}).Where("id = ?", id).Updates(
map[string]interface{}{
map[string]any{
"remain_quota": gorm.Expr("remain_quota - ?", quota),
"used_quota": gorm.Expr("used_quota + ?", quota),
"accessed_time": common.GetTimestamp(),
+6 -7
View File
@@ -3,6 +3,7 @@ package model
import (
"errors"
"fmt"
"maps"
"github.com/QuantumNous/new-api/common"
"github.com/QuantumNous/new-api/logger"
@@ -85,16 +86,14 @@ func ValidateTopUpQuotaCapacity(userId int, creditedQuota int) error {
// creditTopUpQuota atomically enforces the wallet ceiling while adding quota.
// Keeping the predicate and increment in one UPDATE prevents two
// concurrent callbacks from both passing a separate read/check.
func creditTopUpQuota(tx *gorm.DB, userId int, creditedQuota int, updates map[string]interface{}) error {
func creditTopUpQuota(tx *gorm.DB, userId int, creditedQuota int, updates map[string]any) error {
maxCurrentQuota, err := topUpQuotaMaxCurrent(creditedQuota)
if err != nil {
return err
}
updateFields := make(map[string]interface{}, len(updates)+1)
for key, value := range updates {
updateFields[key] = value
}
updateFields := make(map[string]any, len(updates)+1)
maps.Copy(updateFields, updates)
updateFields["quota"] = gorm.Expr("quota + ?", creditedQuota)
result := tx.Model(&User{}).
@@ -273,7 +272,7 @@ func Recharge(referenceId string, customerId string, callerIp string) (err error
if err != nil || quota <= 0 {
return ErrInvalidTopUpQuota
}
return creditTopUpQuota(tx, topUp.UserId, quota, map[string]interface{}{
return creditTopUpQuota(tx, topUp.UserId, quota, map[string]any{
"stripe_customer": customerId,
})
})
@@ -563,7 +562,7 @@ func RechargeCreem(referenceId string, customerEmail string, customerName string
}
// 构建更新字段,优先使用邮箱,如果邮箱为空则使用用户名
updateFields := map[string]interface{}{}
updateFields := map[string]any{}
// 如果有客户邮箱,尝试更新用户邮箱(仅当用户邮箱为空时)
if customerEmail != "" {
+5 -5
View File
@@ -66,7 +66,7 @@ func (t *TwoFA) updateUsageState() error {
if t.Id == 0 {
return errors.New("2FA记录ID不能为空")
}
return DB.Model(&TwoFA{}).Where("id = ?", t.Id).Updates(map[string]interface{}{
return DB.Model(&TwoFA{}).Where("id = ?", t.Id).Updates(map[string]any{
"failed_attempts": t.FailedAttempts,
"locked_until": t.LockedUntil,
"last_used_at": t.LastUsedAt,
@@ -109,7 +109,7 @@ func (t *TwoFA) IncrementFailedAttempts() error {
result := DB.Model(&TwoFA{}).
Where("id = ? AND failed_attempts = ? AND (locked_until IS NULL OR locked_until <= ?)", current.Id, current.FailedAttempts, now).
Updates(map[string]interface{}{
Updates(map[string]any{
"failed_attempts": nextFailedAttempts,
"locked_until": nextLockedUntil,
})
@@ -208,7 +208,7 @@ func ValidateBackupCode(userId int, code string) (bool, error) {
now := time.Now()
result := DB.Model(&TwoFABackupCode{}).
Where("id = ? AND is_used = ?", bc.Id, false).
Updates(map[string]interface{}{
Updates(map[string]any{
"is_used": true,
"used_at": now,
})
@@ -332,7 +332,7 @@ func (t *TwoFA) ValidateBackupCodeAndUpdateUsage(code string) (bool, error) {
}
// GetTwoFAStats 获取2FA统计信息(管理员使用)
func GetTwoFAStats() (map[string]interface{}, error) {
func GetTwoFAStats() (map[string]any, error) {
var totalUsers, enabledUsers int64
// 总用户数
@@ -350,7 +350,7 @@ func GetTwoFAStats() (map[string]interface{}, error) {
enabledRate = float64(enabledUsers) / float64(totalUsers) * 100
}
return map[string]interface{}{
return map[string]any{
"total_users": totalUsers,
"enabled_users": enabledUsers,
"enabled_rate": fmt.Sprintf("%.1f%%", enabledRate),
+1 -1
View File
@@ -128,7 +128,7 @@ func increaseQuotaData(quotaData *QuotaData) {
err := DB.Table("quota_data").
Where("user_id = ? and username = ? and model_name = ? and created_at = ? and use_group = ? and token_id = ? and channel_id = ? and node_name = ?",
quotaData.UserID, quotaData.Username, quotaData.ModelName, quotaData.CreatedAt, quotaData.UseGroup, quotaData.TokenID, quotaData.ChannelID, quotaData.NodeName).
Updates(map[string]interface{}{
Updates(map[string]any{
"count": gorm.Expr("count + ?", quotaData.Count),
"quota": gorm.Expr("quota + ?", quotaData.Quota),
"token_used": gorm.Expr("token_used + ?", quotaData.TokenUsed),
+14 -14
View File
@@ -147,7 +147,7 @@ func UpdateUserAccessToken(id int, token string) error {
if id == 0 {
return errors.New("id 为空!")
}
result := DB.Model(&User{}).Where("id = ?", id).Updates(map[string]interface{}{
result := DB.Model(&User{}).Where("id = ?", id).Updates(map[string]any{
"access_token": token, "access_token_created_at": common.GetTimestamp(),
})
if result.Error != nil {
@@ -171,7 +171,7 @@ func RevokeUserAccessToken(id int) (string, error) {
if tokenRef == "" {
return nil
}
return tx.Model(&User{}).Where("id = ?", id).Updates(map[string]interface{}{"access_token": nil, "access_token_created_at": nil}).Error
return tx.Model(&User{}).Where("id = ?", id).Updates(map[string]any{"access_token": nil, "access_token_created_at": nil}).Error
})
return tokenRef, err
}
@@ -237,17 +237,17 @@ func UpdateUserBindColumn(userId int, column string, value string) error {
// 根据用户角色生成默认的边栏配置
func generateDefaultSidebarConfigForRole(userRole int) string {
defaultConfig := map[string]interface{}{}
defaultConfig := map[string]any{}
// 聊天区域 - 所有用户都可以访问
defaultConfig["chat"] = map[string]interface{}{
defaultConfig["chat"] = map[string]any{
"enabled": true,
"playground": true,
"chat": true,
}
// 控制台区域 - 所有用户都可以访问
defaultConfig["console"] = map[string]interface{}{
defaultConfig["console"] = map[string]any{
"enabled": true,
"detail": true,
"token": true,
@@ -257,7 +257,7 @@ func generateDefaultSidebarConfigForRole(userRole int) string {
}
// 个人中心区域 - 所有用户都可以访问
defaultConfig["personal"] = map[string]interface{}{
defaultConfig["personal"] = map[string]any{
"enabled": true,
"topup": true,
"personal": true,
@@ -266,7 +266,7 @@ func generateDefaultSidebarConfigForRole(userRole int) string {
// 管理员区域 - 根据角色决定
if userRole == common.RoleAdminUser {
// 管理员可以访问管理员区域,但不能访问系统设置
defaultConfig["admin"] = map[string]interface{}{
defaultConfig["admin"] = map[string]any{
"enabled": true,
"channel": true,
"models": true,
@@ -276,7 +276,7 @@ func generateDefaultSidebarConfigForRole(userRole int) string {
}
} else if userRole == common.RoleRootUser {
// 超级管理员可以访问所有功能
defaultConfig["admin"] = map[string]interface{}{
defaultConfig["admin"] = map[string]any{
"enabled": true,
"channel": true,
"models": true,
@@ -470,14 +470,14 @@ func SearchUsers(keyword string, group string, role *int, status *int, startIdx
// 构建搜索条件
likeCondition := "username LIKE ? OR email LIKE ? OR display_name LIKE ?"
likeArgs := []interface{}{"%" + keyword + "%", "%" + keyword + "%", "%" + keyword + "%"}
likeArgs := []any{"%" + keyword + "%", "%" + keyword + "%", "%" + keyword + "%"}
// 尝试将关键字转换为整数ID
keywordInt, err := strconv.Atoi(keyword)
if err == nil {
// 如果是数字,同时搜索ID和其他字段
likeCondition = "id = ? OR " + likeCondition
likeArgs = append([]interface{}{keywordInt}, likeArgs...)
likeArgs = append([]any{keywordInt}, likeArgs...)
}
query = query.Where("("+likeCondition+")", likeArgs...)
@@ -580,7 +580,7 @@ func HardDeleteUserById(id int) error {
}
func inviteUser(inviterId int) error {
result := DB.Model(&User{}).Where("id = ?", inviterId).Updates(map[string]interface{}{
result := DB.Model(&User{}).Where("id = ?", inviterId).Updates(map[string]any{
"aff_count": gorm.Expr("aff_count + ?", 1),
"aff_quota": gorm.Expr("aff_quota + ?", common.QuotaForInviter),
"aff_history": gorm.Expr("aff_history + ?", common.QuotaForInviter),
@@ -887,7 +887,7 @@ func (user *User) EditWithTx(tx *gorm.DB, updatePassword bool) error {
}
newUser := *user
updates := map[string]interface{}{
updates := map[string]any{
"username": newUser.Username,
"display_name": newUser.DisplayName,
"group": newUser.Group,
@@ -1459,7 +1459,7 @@ func UpdateUserUsedQuota(id int, quota int) {
func updateUserUsedQuotaAndRequestCount(id int, quota int, count int) {
err := DB.Model(&User{}).Where("id = ?", id).Updates(
map[string]interface{}{
map[string]any{
"used_quota": gorm.Expr("used_quota + ?", quota),
"request_count": gorm.Expr("request_count + ?", count),
},
@@ -1481,7 +1481,7 @@ func updateUserQuotaUsedQuotaAndRequestCount(id int, quota int, usedQuota int, r
}
err := DB.Model(&User{}).Where("id = ?", id).Updates(
map[string]interface{}{
map[string]any{
"quota": gorm.Expr("quota + ?", quota),
"used_quota": gorm.Expr("used_quota + ?", usedQuota),
"request_count": gorm.Expr("request_count + ?", requestCount),
+3 -9
View File
@@ -38,10 +38,7 @@ func getUserAuthVersionKey(userId int) string {
// recover without an operator repairing Redis.
func userAuthFenceTTLSeconds() int {
cacheTTL := userCacheTTLSeconds()
extra := cacheTTL
if extra < 60 {
extra = 60
}
extra := max(cacheTTL, 60)
return cacheTTL + extra
}
@@ -186,10 +183,7 @@ func IncrementUserAuthVersionWithTx(tx *gorm.DB, userId int) (int64, error) {
if err := lockForUpdate(tx.Unscoped()).Select("id", "auth_version").Where("id = ?", userId).First(&user).Error; err != nil {
return 0, err
}
current := user.AuthVersion
if current < 1 {
current = 1
}
current := max(user.AuthVersion, 1)
next := current + 1
if err := SetUserAuthVersionFence(userId, next); err != nil {
return 0, err
@@ -240,7 +234,7 @@ func InitializeUserAuthVersions() error {
return DB.Model(&User{}).Where("auth_version IS NULL OR auth_version < ?", 1).Update("auth_version", 1).Error
}
func updateUserCacheFieldAtVersion(userId int, field string, value interface{}, authVersion int64) error {
func updateUserCacheFieldAtVersion(userId int, field string, value any, authVersion int64) error {
if !common.RedisEnabled {
return nil
}
+4 -8
View File
@@ -142,11 +142,9 @@ func TestIncrementFailedAttemptsCountsConcurrentFailures(t *testing.T) {
errs := make(chan error, attempts)
var wg sync.WaitGroup
for range attempts {
wg.Add(1)
go func() {
defer wg.Done()
wg.Go(func() {
errs <- (&TwoFA{Id: twoFA.Id}).IncrementFailedAttempts()
}()
})
}
wg.Wait()
close(errs)
@@ -175,13 +173,11 @@ func TestValidateBackupCodeCanOnlySucceedOnce(t *testing.T) {
errs := make(chan error, attempts)
var wg sync.WaitGroup
for range attempts {
wg.Add(1)
go func() {
defer wg.Done()
wg.Go(func() {
valid, err := ValidateBackupCode(123, code)
results <- valid
errs <- err
}()
})
}
wg.Wait()
close(results)
+1 -1
View File
@@ -253,7 +253,7 @@ func updateUserSettingCache(userId int, setting string) error {
// updateUserCacheField prevents individual cache refreshes from bypassing the
// auth-version fence. It intentionally does nothing when the complete hash is
// absent; the next GetUserCache call will repopulate it from the database.
func updateUserCacheField(userId int, field string, value interface{}) error {
func updateUserCacheField(userId int, field string, value any) error {
if !common.RedisEnabled {
return nil
}
+8 -14
View File
@@ -481,7 +481,7 @@ func RotateUserSessionRefresh(userID int, sid, presentedHash, nextHash string, n
result := DB.Model(&UserSession{}).
Where("sid = ? AND user_id = ? AND status = ? AND revoked_at = ? AND expires_at > ? AND refresh_hash = ?",
sid, userID, UserSessionStatusActive, 0, now, presentedHash).
Updates(map[string]interface{}{
Updates(map[string]any{
"previous_refresh_hash": session.RefreshHash,
"previous_valid_until": now + graceSeconds,
"refresh_hash": nextHash,
@@ -527,7 +527,7 @@ func RotateUserSessionRefresh(userID int, sid, presentedHash, nextHash string, n
result := DB.Model(&UserSession{}).
Where("sid = ? AND user_id = ? AND status = ? AND revoked_at = ? AND expires_at > ?",
sid, userID, UserSessionStatusActive, 0, now).
Updates(map[string]interface{}{
Updates(map[string]any{
"status": UserSessionStatusRevoked,
"revoked_at": now,
"revoked_reason": "refresh_reuse",
@@ -577,7 +577,7 @@ func RevokeUserSession(userID int, sid, reason string) (bool, error) {
if current.Status != UserSessionStatusActive || current.RevokedAt != 0 || current.ExpiresAt <= now {
return nil
}
result := tx.Model(&UserSession{}).Where("sid = ? AND status = ?", sid, UserSessionStatusActive).Updates(map[string]interface{}{
result := tx.Model(&UserSession{}).Where("sid = ? AND status = ?", sid, UserSessionStatusActive).Updates(map[string]any{
"status": UserSessionStatusRevoked,
"revoked_at": now,
"revoked_reason": reason,
@@ -631,7 +631,7 @@ func RevokeUserSessionByRefreshHash(sid, presentedHash, reason string) (bool, er
if err := writeUserSessionDenyFence(&session, UserSessionStatusRevoking, now, reason); err != nil {
return err
}
result := tx.Model(&UserSession{}).Where("sid = ? AND status = ?", sid, UserSessionStatusActive).Updates(map[string]interface{}{
result := tx.Model(&UserSession{}).Where("sid = ? AND status = ?", sid, UserSessionStatusActive).Updates(map[string]any{
"status": UserSessionStatusRevoked,
"revoked_at": now,
"revoked_reason": reason,
@@ -681,7 +681,7 @@ func AdvanceUserSessionAuthVersion(userID int, sid string, expectedSessionVersio
session.LastActiveAt = now
result := tx.Model(&UserSession{}).
Where("sid = ? AND status = ? AND version = ? AND user_auth_version = ?", sid, UserSessionStatusActive, expectedSessionVersion, expectedUserAuthVersion).
Updates(map[string]interface{}{
Updates(map[string]any{
"version": session.Version,
"user_auth_version": session.UserAuthVersion,
"last_active_at": session.LastActiveAt,
@@ -758,7 +758,7 @@ func revokeUserSessions(userID int, excludedSID, reason string) (int64, error) {
for i := range revoked {
lockedSIDs = append(lockedSIDs, revoked[i].SID)
}
result := tx.Model(&UserSession{}).Where("sid IN ? AND status = ?", lockedSIDs, UserSessionStatusActive).Updates(map[string]interface{}{
result := tx.Model(&UserSession{}).Where("sid IN ? AND status = ?", lockedSIDs, UserSessionStatusActive).Updates(map[string]any{
"status": UserSessionStatusRevoked,
"revoked_at": now,
"revoked_reason": reason,
@@ -823,10 +823,7 @@ func deleteExpiredUserSessionsBefore(expiredBefore, issuanceCutoff, revokedBefor
return nil
}
for start := 0; start < len(sids); start += userSessionCleanupBatchSize {
end := start + userSessionCleanupBatchSize
if end > len(sids) {
end = len(sids)
}
end := min(start+userSessionCleanupBatchSize, len(sids))
if err := DB.Where("sid IN ?", sids[start:end]).
Where(
"expires_at < ? AND created_at <= ? AND (status <> ? OR revoked_at <= 0 OR revoked_at < ?)",
@@ -859,10 +856,7 @@ func deleteRevokedUserSessionsBefore(revokedBefore, issuanceCutoff int64) error
return nil
}
for start := 0; start < len(sids); start += userSessionCleanupBatchSize {
end := start + userSessionCleanupBatchSize
if end > len(sids) {
end = len(sids)
}
end := min(start+userSessionCleanupBatchSize, len(sids))
if err := DB.Where("sid IN ?", sids[start:end]).
Where(
"status = ? AND revoked_at > 0 AND revoked_at < ? AND created_at <= ?",
+3 -3
View File
@@ -432,7 +432,7 @@ func TestListActiveUserSessionsKeepsCurrentAndBoundsOtherSessions(t *testing.T)
current.UserAuthVersion = 7
rows := make([]UserSession, 0, 107)
rows = append(rows, *current)
for i := 0; i < 105; i++ {
for i := range 105 {
session := newTestUserSession(fmt.Sprintf("list-other-%03d", i), 1007, now-int64(i))
session.UserAuthVersion = 7
rows = append(rows, *session)
@@ -461,7 +461,7 @@ func TestRevokeUserSessionsReturnsCumulativeProgressAndSupportsRetry(t *testing.
now := time.Now().Unix()
createUserSessionTestUser(t, 1008, 1)
rows := make([]UserSession, 0, userSessionRevokeBatchSize+1)
for i := 0; i < userSessionRevokeBatchSize+1; i++ {
for i := range userSessionRevokeBatchSize + 1 {
rows = append(rows, *newTestUserSession(fmt.Sprintf("batch-revoke-%03d", i), 1008, now))
}
require.NoError(t, DB.CreateInBatches(rows, 100).Error)
@@ -509,7 +509,7 @@ func TestDeleteExpiredUserSessionsLoopsInChunksAndRechecksPredicate(t *testing.T
race.CreatedAt = oldCreatedAt
race.ExpiresAt = now - 1000
rows = append(rows, *race)
for i := 0; i < userSessionCleanupScanLimit+1; i++ {
for i := range userSessionCleanupScanLimit + 1 {
session := newTestUserSession(fmt.Sprintf("cleanup-expired-%04d", i), 1009, now-100)
session.CreatedAt = oldCreatedAt - int64(i)
session.ExpiresAt = now - 100
+4 -4
View File
@@ -64,7 +64,7 @@ func TestUserUpdateDoesNotOverwriteConcurrentAccountingOrTokenChanges(t *testing
staleUser, err := GetUserById(user.Id, true)
require.NoError(t, err)
require.NoError(t, DB.Model(&User{}).Where("id = ?", user.Id).Updates(map[string]interface{}{
require.NoError(t, DB.Model(&User{}).Where("id = ?", user.Id).Updates(map[string]any{
"quota": gorm.Expr("quota - ?", 400),
"used_quota": gorm.Expr("used_quota + ?", 400),
"request_count": gorm.Expr("request_count + ?", 1),
@@ -159,7 +159,7 @@ func TestUpdateUserAccessTokenOnlyUpdatesAccessToken(t *testing.T) {
}
require.NoError(t, DB.Create(&user).Error)
require.NoError(t, DB.Model(&User{}).Where("id = ?", user.Id).Updates(map[string]interface{}{
require.NoError(t, DB.Model(&User{}).Where("id = ?", user.Id).Updates(map[string]any{
"quota": gorm.Expr("quota + ?", 500),
"aff_quota": gorm.Expr("aff_quota - ?", 500),
"display_name": "concurrent-update",
@@ -211,7 +211,7 @@ func TestUpdateUserSettingOnlyUpdatesSetting(t *testing.T) {
}
require.NoError(t, DB.Create(&user).Error)
require.NoError(t, DB.Model(&User{}).Where("id = ?", user.Id).Updates(map[string]interface{}{
require.NoError(t, DB.Model(&User{}).Where("id = ?", user.Id).Updates(map[string]any{
"quota": gorm.Expr("quota - ?", 250),
"used_quota": gorm.Expr("used_quota + ?", 250),
"request_count": gorm.Expr("request_count + ?", 1),
@@ -292,7 +292,7 @@ func TestUpdateUserBindColumnOnlyTouchesTheBindingColumn(t *testing.T) {
truncateTables(t)
user := createUserBindTestUser(t)
require.NoError(t, DB.Model(&User{}).Where("id = ?", user.Id).Updates(map[string]interface{}{
require.NoError(t, DB.Model(&User{}).Where("id = ?", user.Id).Updates(map[string]any{
"role": common.RoleAdminUser,
"status": common.UserStatusEnabled,
"group": "vip",
+3 -3
View File
@@ -26,7 +26,7 @@ var batchUpdateStores []map[int]int
var batchUpdateLocks []sync.Mutex
func init() {
for i := 0; i < BatchUpdateTypeCount; i++ {
for range BatchUpdateTypeCount {
batchUpdateStores = append(batchUpdateStores, make(map[int]int))
batchUpdateLocks = append(batchUpdateLocks, sync.Mutex{})
}
@@ -65,7 +65,7 @@ func addNewRecord(type_ int, id int, value int) {
func batchUpdate() {
// check if there's any data to update
hasData := false
for i := 0; i < BatchUpdateTypeCount; i++ {
for i := range BatchUpdateTypeCount {
batchUpdateLocks[i].Lock()
if len(batchUpdateStores[i]) > 0 {
hasData = true
@@ -81,7 +81,7 @@ func batchUpdate() {
common.SysLog("batch update started")
stores := make([]map[int]int, BatchUpdateTypeCount)
for i := 0; i < BatchUpdateTypeCount; i++ {
for i := range BatchUpdateTypeCount {
batchUpdateLocks[i].Lock()
stores[i] = batchUpdateStores[i]
batchUpdateStores[i] = make(map[int]int)
+2 -3
View File
@@ -2,6 +2,7 @@ package oauth
import (
"fmt"
"maps"
"sync"
"github.com/QuantumNous/new-api/common"
@@ -62,9 +63,7 @@ func GetAllProviders() map[string]Provider {
mu.RLock()
defer mu.RUnlock()
result := make(map[string]Provider, len(providers))
for k, v := range providers {
result[k] = v
}
maps.Copy(result, providers)
return result
}
+5 -5
View File
@@ -122,7 +122,7 @@ var (
)
// compileEnvPrototypeV1 is the v1 type-checking prototype used at compile time.
var compileEnvPrototypeV1 = map[string]interface{}{
var compileEnvPrototypeV1 = map[string]any{
"p": float64(0),
"c": float64(0),
"len": float64(0),
@@ -137,9 +137,9 @@ var compileEnvPrototypeV1 = map[string]interface{}{
"_trace": func(int, bool, float64) float64 { return 1 },
"_trace_int": func(int, bool, int) int { return 1 },
"header": func(string) string { return "" },
"param": func(string) interface{} { return nil },
"u": func(string) interface{} { return nil },
"has": func(interface{}, string) bool { return false },
"param": func(string) any { return nil },
"u": func(string) any { return nil },
"has": func(any, string) bool { return false },
"hour": func(string) int { return 0 },
"minute": func(string) int { return 0 },
"weekday": func(string) int { return 0 },
@@ -152,7 +152,7 @@ var compileEnvPrototypeV1 = map[string]interface{}{
"floor": math.Floor,
}
func getCompileEnv(version int) map[string]interface{} {
func getCompileEnv(version int) map[string]any {
switch version {
default:
return compileEnvPrototypeV1
+4 -4
View File
@@ -54,7 +54,7 @@ func runProgram(prog *vm.Program, requestRules []RequestRuleTrace, params TokenP
}
headers := normalizeHeaders(request.Headers)
env := map[string]interface{}{
env := map[string]any{
"p": params.P,
"c": params.C,
"len": params.Len,
@@ -91,7 +91,7 @@ func runProgram(prog *vm.Program, requestRules []RequestRuleTrace, params TokenP
"header": func(key string) string {
return headers[strings.ToLower(strings.TrimSpace(key))]
},
"param": func(path string) interface{} {
"param": func(path string) any {
path = strings.TrimSpace(path)
if path == "" || len(request.Body) == 0 {
return nil
@@ -102,13 +102,13 @@ func runProgram(prog *vm.Program, requestRules []RequestRuleTrace, params TokenP
}
return result.Value()
},
"u": func(name string) interface{} {
"u": func(name string) any {
if request.Usage == nil {
return nil
}
return request.Usage[strings.TrimSpace(name)]
},
"has": func(source interface{}, substr string) bool {
"has": func(source any, substr string) bool {
if source == nil || substr == "" {
return false
}
+4 -9
View File
@@ -5,6 +5,7 @@ import (
"errors"
"fmt"
"regexp"
"slices"
"strings"
"sync"
"time"
@@ -137,7 +138,7 @@ func Compile(source string, options Options) (*Engine, error) {
return nil, fmt.Errorf("unsupported plugin syntax %q: plugins must be synchronous and cannot import modules", strings.TrimSpace(match))
}
resolve := func(_ interface{}, specifier string) (sobek.ModuleRecord, error) {
resolve := func(_ any, specifier string) (sobek.ModuleRecord, error) {
return nil, fmt.Errorf("plugin imports are disabled: %s", specifier)
}
// Plugin source is untrusted; without this option a sourceMappingURL
@@ -446,13 +447,7 @@ func resolveExportPath(instance *runtimeInstance, exportName string, members []s
for _, member := range members {
hookName += "." + member
object := value.ToObject(instance.runtime)
own := false
for _, name := range object.GetOwnPropertyNames() {
if name == member {
own = true
break
}
}
own := slices.Contains(object.GetOwnPropertyNames(), member)
if !own {
return nil, hookName, false
}
@@ -500,7 +495,7 @@ func (e *Engine) newRuntime(ctx context.Context) (instance *runtimeInstance, err
panic(recovered)
}
}()
promise := runtime.CyclicModuleRecordEvaluate(e.module, func(_ interface{}, specifier string) (sobek.ModuleRecord, error) {
promise := runtime.CyclicModuleRecordEvaluate(e.module, func(_ any, specifier string) (sobek.ModuleRecord, error) {
return nil, fmt.Errorf("plugin imports are disabled: %s", specifier)
})
if promise.State() != sobek.PromiseStateFulfilled {
+2 -4
View File
@@ -1077,7 +1077,7 @@ func normalizeV1Meta(meta *Meta) error {
if len(host) > 253 || host == "" {
return fmt.Errorf("plugin meta website must have a valid hostname")
}
for _, label := range strings.Split(host, ".") {
for label := range strings.SplitSeq(host, ".") {
if !websiteHostLabelPattern.MatchString(label) {
return fmt.Errorf("plugin meta website must have a valid ASCII hostname; use punycode for internationalized domains")
}
@@ -1848,9 +1848,7 @@ func validateLocalizedText(text LocalizedText, name string, maxRunes int) error
for locale := range text {
delete(text, locale)
}
for locale, value := range canonical {
text[locale] = value
}
maps.Copy(text, canonical)
return nil
}
+1 -3
View File
@@ -243,9 +243,7 @@ type RouteRequestContext struct {
func (r RouteRequestContext) JSValue() map[string]any {
params := make(map[string]string, len(r.Params))
for key, value := range r.Params {
params[key] = value
}
maps.Copy(params, r.Params)
query := make(map[string][]string, len(r.Query))
for key, values := range r.Query {
query[key] = append([]string(nil), values...)
+2 -2
View File
@@ -205,7 +205,7 @@ func TestHailuoH3BuildSubmitRequest(t *testing.T) {
func TestHailuoH3RejectsOutOfContractRequests(t *testing.T) {
plugin := loadHailuoPlugin(t)
tenReferenceImages := make([]any, 0, 10)
for i := 0; i < 10; i++ {
for range 10 {
tenReferenceImages = append(tenReferenceImages, map[string]any{
"type": "image_url", "role": "reference_image", "image_url": map[string]any{"url": "u"},
})
@@ -372,7 +372,7 @@ func TestHailuoParseTaskResult(t *testing.T) {
func TestHailuoExtractUsageFacts(t *testing.T) {
plugin := loadHailuoPlugin(t)
nineReferenceImages := make([]any, 0, 9)
for i := 0; i < 9; i++ {
for range 9 {
nineReferenceImages = append(nineReferenceImages, map[string]any{
"type": "image_url", "role": "reference_image", "image_url": map[string]any{"url": "image"},
})
+5 -5
View File
@@ -35,8 +35,8 @@ type AliParameters struct {
type AliChatRequest struct {
Model string `json:"model"`
Input AliInput `json:"input,omitempty"`
Parameters AliParameters `json:"parameters,omitempty"`
Input AliInput `json:"input"`
Parameters AliParameters `json:"parameters"`
}
type AliEmbeddingRequest struct {
@@ -96,7 +96,7 @@ type AliOutput struct {
Role string `json:"role,omitempty"`
Content []AliMediaContent `json:"content,omitempty"`
ReasoningContent string `json:"reasoning_content,omitempty"`
} `json:"message,omitempty"`
} `json:"message"`
} `json:"choices,omitempty"`
}
@@ -166,7 +166,7 @@ type AliResponse struct {
type AliImageRequest struct {
Model string `json:"model"`
Input any `json:"input"`
Parameters AliImageParameters `json:"parameters,omitempty"`
Parameters AliImageParameters `json:"parameters"`
ResponseFormat string `json:"response_format,omitempty"`
}
@@ -223,7 +223,7 @@ type AliRerankInput struct {
type AliRerankRequest struct {
Model string `json:"model"`
Input AliRerankInput `json:"input"`
Parameters AliRerankParameters `json:"parameters,omitempty"`
Parameters AliRerankParameters `json:"parameters"`
}
type AliRerankResponse struct {
Loaded 100 of 214 files, more files were not shown because too many files have changed in this diff. Show more