mirror of
https://github.com/QuantumNous/new-api.git
synced 2026-10-28 08:35:33 +08:00
refactor: modernize Go code conventions
This commit is contained in:
214 files changed
+1257
-1422
No files matched your search
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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))
|
||||
}
|
||||
|
||||
@@ -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,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
@@ -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
@@ -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
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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{
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
@@ -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,
|
||||
})
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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})
|
||||
}
|
||||
@@ -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
@@ -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,
|
||||
|
||||
@@ -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{
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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") })
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
// 创建本地订单
|
||||
|
||||
@@ -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
@@ -96,7 +96,7 @@ func Get2FAStatus(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
status := map[string]interface{}{
|
||||
status := map[string]any{
|
||||
"enabled": false,
|
||||
"locked": false,
|
||||
}
|
||||
|
||||
@@ -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
@@ -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,
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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.")
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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), ¶mOverride)
|
||||
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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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
@@ -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), // 本月签到次数
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
@@ -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
@@ -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"])
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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,
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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
@@ -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,
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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
@@ -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,
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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),
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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 <= ?",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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...)
|
||||
|
||||
@@ -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"},
|
||||
})
|
||||
|
||||
@@ -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
Reference in new issue
Block a user