diff --git a/common/utils.go b/common/utils.go index 17aecd950..f82538138 100644 --- a/common/utils.go +++ b/common/utils.go @@ -257,32 +257,32 @@ func GetAudioDuration(ctx context.Context, filename string, ext string) (float64 if err != nil { return 0, errors.Wrap(err, "failed to get audio duration") } - durationStr := string(bytes.TrimSpace(output)) - if durationStr == "N/A" { - // Create a temporary output file name - tmpFp, err := os.CreateTemp("", "audio-*"+ext) - if err != nil { - return 0, errors.Wrap(err, "failed to create temporary file") - } - tmpName := tmpFp.Name() - // Close immediately so ffmpeg can open the file on Windows. - _ = tmpFp.Close() - defer os.Remove(tmpName) + durationStr := string(bytes.TrimSpace(output)) + if durationStr == "N/A" { + // Create a temporary output file name + tmpFp, err := os.CreateTemp("", "audio-*"+ext) + if err != nil { + return 0, errors.Wrap(err, "failed to create temporary file") + } + tmpName := tmpFp.Name() + // Close immediately so ffmpeg can open the file on Windows. + _ = tmpFp.Close() + defer os.Remove(tmpName) - // ffmpeg -y -i filename -vcodec copy -acodec copy - ffmpegCmd := exec.CommandContext(ctx, "ffmpeg", "-y", "-i", filename, "-vcodec", "copy", "-acodec", "copy", tmpName) - if err := ffmpegCmd.Run(); err != nil { - return 0, errors.Wrap(err, "failed to run ffmpeg") - } + // ffmpeg -y -i filename -vcodec copy -acodec copy + ffmpegCmd := exec.CommandContext(ctx, "ffmpeg", "-y", "-i", filename, "-vcodec", "copy", "-acodec", "copy", tmpName) + if err := ffmpegCmd.Run(); err != nil { + return 0, errors.Wrap(err, "failed to run ffmpeg") + } - // Recalculate the duration of the new file - c = exec.CommandContext(ctx, "ffprobe", "-v", "error", "-show_entries", "format=duration", "-of", "default=noprint_wrappers=1:nokey=1", tmpName) - output, err := c.Output() - if err != nil { - return 0, errors.Wrap(err, "failed to get audio duration after ffmpeg") - } - durationStr = string(bytes.TrimSpace(output)) - } + // Recalculate the duration of the new file + c = exec.CommandContext(ctx, "ffprobe", "-v", "error", "-show_entries", "format=duration", "-of", "default=noprint_wrappers=1:nokey=1", tmpName) + output, err := c.Output() + if err != nil { + return 0, errors.Wrap(err, "failed to get audio duration after ffmpeg") + } + durationStr = string(bytes.TrimSpace(output)) + } return strconv.ParseFloat(durationStr, 64) } diff --git a/controller/ratio_config.go b/controller/ratio_config.go index 6ddc3d9ef..0cb4aa73b 100644 --- a/controller/ratio_config.go +++ b/controller/ratio_config.go @@ -1,24 +1,24 @@ package controller import ( - "net/http" - "one-api/setting/ratio_setting" + "net/http" + "one-api/setting/ratio_setting" - "github.com/gin-gonic/gin" + "github.com/gin-gonic/gin" ) func GetRatioConfig(c *gin.Context) { - if !ratio_setting.IsExposeRatioEnabled() { - c.JSON(http.StatusForbidden, gin.H{ - "success": false, - "message": "倍率配置接口未启用", - }) - return - } + if !ratio_setting.IsExposeRatioEnabled() { + c.JSON(http.StatusForbidden, gin.H{ + "success": false, + "message": "倍率配置接口未启用", + }) + return + } - c.JSON(http.StatusOK, gin.H{ - "success": true, - "message": "", - "data": ratio_setting.GetExposedData(), - }) -} \ No newline at end of file + c.JSON(http.StatusOK, gin.H{ + "success": true, + "message": "", + "data": ratio_setting.GetExposedData(), + }) +} diff --git a/controller/task_video.go b/controller/task_video.go index ffb6728ba..84b78f901 100644 --- a/controller/task_video.go +++ b/controller/task_video.go @@ -113,7 +113,7 @@ func updateVideoSingleTask(ctx context.Context, adaptor channel.TaskAdaptor, cha task.StartTime = now } case model.TaskStatusSuccess: - task.Progress = "100%" + task.Progress = "100%" if task.FinishTime == 0 { task.FinishTime = now } diff --git a/controller/uptime_kuma.go b/controller/uptime_kuma.go index 05d6297eb..41b9695c3 100644 --- a/controller/uptime_kuma.go +++ b/controller/uptime_kuma.go @@ -31,7 +31,7 @@ type Monitor struct { type UptimeGroupResult struct { CategoryName string `json:"categoryName"` - Monitors []Monitor `json:"monitors"` + Monitors []Monitor `json:"monitors"` } func getAndDecode(ctx context.Context, client *http.Client, url string, dest interface{}) error { @@ -57,29 +57,29 @@ func fetchGroupData(ctx context.Context, client *http.Client, groupConfig map[st url, _ := groupConfig["url"].(string) slug, _ := groupConfig["slug"].(string) categoryName, _ := groupConfig["categoryName"].(string) - + result := UptimeGroupResult{ CategoryName: categoryName, - Monitors: []Monitor{}, + Monitors: []Monitor{}, } - + if url == "" || slug == "" { return result } baseURL := strings.TrimSuffix(url, "/") - + var statusData struct { PublicGroupList []struct { - ID int `json:"id"` - Name string `json:"name"` + ID int `json:"id"` + Name string `json:"name"` MonitorList []struct { ID int `json:"id"` Name string `json:"name"` } `json:"monitorList"` } `json:"publicGroupList"` } - + var heartbeatData struct { HeartbeatList map[string][]struct { Status int `json:"status"` @@ -88,11 +88,11 @@ func fetchGroupData(ctx context.Context, client *http.Client, groupConfig map[st } g, gCtx := errgroup.WithContext(ctx) - g.Go(func() error { - return getAndDecode(gCtx, client, baseURL+apiStatusPath+slug, &statusData) + g.Go(func() error { + return getAndDecode(gCtx, client, baseURL+apiStatusPath+slug, &statusData) }) - g.Go(func() error { - return getAndDecode(gCtx, client, baseURL+apiHeartbeatPath+slug, &heartbeatData) + g.Go(func() error { + return getAndDecode(gCtx, client, baseURL+apiHeartbeatPath+slug, &heartbeatData) }) if g.Wait() != nil { @@ -139,7 +139,7 @@ func GetUptimeKumaStatus(c *gin.Context) { client := &http.Client{Timeout: httpTimeout} results := make([]UptimeGroupResult, len(groups)) - + g, gCtx := errgroup.WithContext(ctx) for i, group := range groups { i, group := i, group @@ -148,7 +148,7 @@ func GetUptimeKumaStatus(c *gin.Context) { return nil }) } - + g.Wait() c.JSON(http.StatusOK, gin.H{"success": true, "message": "", "data": results}) -} \ No newline at end of file +} diff --git a/dto/ratio_sync.go b/dto/ratio_sync.go index 6315f31ae..d6bbf68e1 100644 --- a/dto/ratio_sync.go +++ b/dto/ratio_sync.go @@ -1,23 +1,23 @@ package dto type UpstreamDTO struct { - ID int `json:"id,omitempty"` - Name string `json:"name" binding:"required"` - BaseURL string `json:"base_url" binding:"required"` - Endpoint string `json:"endpoint"` + ID int `json:"id,omitempty"` + Name string `json:"name" binding:"required"` + BaseURL string `json:"base_url" binding:"required"` + Endpoint string `json:"endpoint"` } type UpstreamRequest struct { - ChannelIDs []int64 `json:"channel_ids"` - Upstreams []UpstreamDTO `json:"upstreams"` - Timeout int `json:"timeout"` + ChannelIDs []int64 `json:"channel_ids"` + Upstreams []UpstreamDTO `json:"upstreams"` + Timeout int `json:"timeout"` } // TestResult 上游测试连通性结果 type TestResult struct { - Name string `json:"name"` - Status string `json:"status"` - Error string `json:"error,omitempty"` + Name string `json:"name"` + Status string `json:"status"` + Error string `json:"error,omitempty"` } // DifferenceItem 差异项 @@ -25,14 +25,14 @@ type TestResult struct { // Upstreams 为各渠道的上游值,具体数值 / "same" / nil type DifferenceItem struct { - Current interface{} `json:"current"` - Upstreams map[string]interface{} `json:"upstreams"` - Confidence map[string]bool `json:"confidence"` + Current interface{} `json:"current"` + Upstreams map[string]interface{} `json:"upstreams"` + Confidence map[string]bool `json:"confidence"` } type SyncableChannel struct { - ID int `json:"id"` - Name string `json:"name"` - BaseURL string `json:"base_url"` - Status int `json:"status"` -} \ No newline at end of file + ID int `json:"id"` + Name string `json:"name"` + BaseURL string `json:"base_url"` + Status int `json:"status"` +} diff --git a/middleware/stats.go b/middleware/stats.go index 1c97983f7..e49e56991 100644 --- a/middleware/stats.go +++ b/middleware/stats.go @@ -18,12 +18,12 @@ func StatsMiddleware() gin.HandlerFunc { return func(c *gin.Context) { // 增加活跃连接数 atomic.AddInt64(&globalStats.activeConnections, 1) - + // 确保在请求结束时减少连接数 defer func() { atomic.AddInt64(&globalStats.activeConnections, -1) }() - + c.Next() } } @@ -38,4 +38,4 @@ func GetStats() StatsInfo { return StatsInfo{ ActiveConnections: atomic.LoadInt64(&globalStats.activeConnections), } -} \ No newline at end of file +} diff --git a/relay/channel/mokaai/constants.go b/relay/channel/mokaai/constants.go index 415d83b7f..385a0876b 100644 --- a/relay/channel/mokaai/constants.go +++ b/relay/channel/mokaai/constants.go @@ -6,4 +6,4 @@ var ModelList = []string{ "m3e-small", } -var ChannelName = "mokaai" \ No newline at end of file +var ChannelName = "mokaai" diff --git a/setting/console_setting/config.go b/setting/console_setting/config.go index 6327e5584..8cfcd0ed6 100644 --- a/setting/console_setting/config.go +++ b/setting/console_setting/config.go @@ -3,37 +3,37 @@ package console_setting import "one-api/setting/config" type ConsoleSetting struct { - ApiInfo string `json:"api_info"` // 控制台 API 信息 (JSON 数组字符串) - UptimeKumaGroups string `json:"uptime_kuma_groups"` // Uptime Kuma 分组配置 (JSON 数组字符串) - Announcements string `json:"announcements"` // 系统公告 (JSON 数组字符串) - FAQ string `json:"faq"` // 常见问题 (JSON 数组字符串) - ApiInfoEnabled bool `json:"api_info_enabled"` // 是否启用 API 信息面板 - UptimeKumaEnabled bool `json:"uptime_kuma_enabled"` // 是否启用 Uptime Kuma 面板 - AnnouncementsEnabled bool `json:"announcements_enabled"` // 是否启用系统公告面板 - FAQEnabled bool `json:"faq_enabled"` // 是否启用常见问答面板 + ApiInfo string `json:"api_info"` // 控制台 API 信息 (JSON 数组字符串) + UptimeKumaGroups string `json:"uptime_kuma_groups"` // Uptime Kuma 分组配置 (JSON 数组字符串) + Announcements string `json:"announcements"` // 系统公告 (JSON 数组字符串) + FAQ string `json:"faq"` // 常见问题 (JSON 数组字符串) + ApiInfoEnabled bool `json:"api_info_enabled"` // 是否启用 API 信息面板 + UptimeKumaEnabled bool `json:"uptime_kuma_enabled"` // 是否启用 Uptime Kuma 面板 + AnnouncementsEnabled bool `json:"announcements_enabled"` // 是否启用系统公告面板 + FAQEnabled bool `json:"faq_enabled"` // 是否启用常见问答面板 } // 默认配置 var defaultConsoleSetting = ConsoleSetting{ - ApiInfo: "", - UptimeKumaGroups: "", - Announcements: "", - FAQ: "", - ApiInfoEnabled: true, - UptimeKumaEnabled: true, - AnnouncementsEnabled: true, - FAQEnabled: true, + ApiInfo: "", + UptimeKumaGroups: "", + Announcements: "", + FAQ: "", + ApiInfoEnabled: true, + UptimeKumaEnabled: true, + AnnouncementsEnabled: true, + FAQEnabled: true, } // 全局实例 var consoleSetting = defaultConsoleSetting func init() { - // 注册到全局配置管理器,键名为 console_setting - config.GlobalConfig.Register("console_setting", &consoleSetting) + // 注册到全局配置管理器,键名为 console_setting + config.GlobalConfig.Register("console_setting", &consoleSetting) } // GetConsoleSetting 获取 ConsoleSetting 配置实例 func GetConsoleSetting() *ConsoleSetting { - return &consoleSetting -} \ No newline at end of file + return &consoleSetting +} diff --git a/setting/console_setting/validation.go b/setting/console_setting/validation.go index fda6453df..529457761 100644 --- a/setting/console_setting/validation.go +++ b/setting/console_setting/validation.go @@ -1,304 +1,304 @@ package console_setting import ( - "encoding/json" - "fmt" - "net/url" - "regexp" - "strings" - "time" - "sort" + "encoding/json" + "fmt" + "net/url" + "regexp" + "sort" + "strings" + "time" ) var ( - urlRegex = regexp.MustCompile(`^https?://(?:(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)*[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?|(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))(?:\:[0-9]{1,5})?(?:/.*)?$`) - dangerousChars = []string{" 50 { - return fmt.Errorf("API信息数量不能超过50个") - } + if len(apiInfoList) > 50 { + return fmt.Errorf("API信息数量不能超过50个") + } - for i, apiInfo := range apiInfoList { - urlStr, ok := apiInfo["url"].(string) - if !ok || urlStr == "" { - return fmt.Errorf("第%d个API信息缺少URL字段", i+1) - } - route, ok := apiInfo["route"].(string) - if !ok || route == "" { - return fmt.Errorf("第%d个API信息缺少线路描述字段", i+1) - } - description, ok := apiInfo["description"].(string) - if !ok || description == "" { - return fmt.Errorf("第%d个API信息缺少说明字段", i+1) - } - color, ok := apiInfo["color"].(string) - if !ok || color == "" { - return fmt.Errorf("第%d个API信息缺少颜色字段", i+1) - } + for i, apiInfo := range apiInfoList { + urlStr, ok := apiInfo["url"].(string) + if !ok || urlStr == "" { + return fmt.Errorf("第%d个API信息缺少URL字段", i+1) + } + route, ok := apiInfo["route"].(string) + if !ok || route == "" { + return fmt.Errorf("第%d个API信息缺少线路描述字段", i+1) + } + description, ok := apiInfo["description"].(string) + if !ok || description == "" { + return fmt.Errorf("第%d个API信息缺少说明字段", i+1) + } + color, ok := apiInfo["color"].(string) + if !ok || color == "" { + return fmt.Errorf("第%d个API信息缺少颜色字段", i+1) + } - if err := validateURL(urlStr, i+1, "API信息"); err != nil { - return err - } + if err := validateURL(urlStr, i+1, "API信息"); err != nil { + return err + } - if len(urlStr) > 500 { - return fmt.Errorf("第%d个API信息的URL长度不能超过500字符", i+1) - } - if len(route) > 100 { - return fmt.Errorf("第%d个API信息的线路描述长度不能超过100字符", i+1) - } - if len(description) > 200 { - return fmt.Errorf("第%d个API信息的说明长度不能超过200字符", i+1) - } + if len(urlStr) > 500 { + return fmt.Errorf("第%d个API信息的URL长度不能超过500字符", i+1) + } + if len(route) > 100 { + return fmt.Errorf("第%d个API信息的线路描述长度不能超过100字符", i+1) + } + if len(description) > 200 { + return fmt.Errorf("第%d个API信息的说明长度不能超过200字符", i+1) + } - if !validColors[color] { - return fmt.Errorf("第%d个API信息的颜色值不合法", i+1) - } + if !validColors[color] { + return fmt.Errorf("第%d个API信息的颜色值不合法", i+1) + } - if err := checkDangerousContent(description, i+1, "API信息"); err != nil { - return err - } - if err := checkDangerousContent(route, i+1, "API信息"); err != nil { - return err - } - } - return nil + if err := checkDangerousContent(description, i+1, "API信息"); err != nil { + return err + } + if err := checkDangerousContent(route, i+1, "API信息"); err != nil { + return err + } + } + return nil } func GetApiInfo() []map[string]interface{} { - return getJSONList(GetConsoleSetting().ApiInfo) + return getJSONList(GetConsoleSetting().ApiInfo) } func validateAnnouncements(announcementsStr string) error { - list, err := parseJSONArray(announcementsStr, "系统公告") - if err != nil { - return err - } - if len(list) > 100 { - return fmt.Errorf("系统公告数量不能超过100个") - } - validTypes := map[string]bool{ - "default": true, "ongoing": true, "success": true, "warning": true, "error": true, - } - for i, ann := range list { - content, ok := ann["content"].(string) - if !ok || content == "" { - return fmt.Errorf("第%d个公告缺少内容字段", i+1) - } - publishDateAny, exists := ann["publishDate"] - if !exists { - return fmt.Errorf("第%d个公告缺少发布日期字段", i+1) - } - publishDateStr, ok := publishDateAny.(string) - if !ok || publishDateStr == "" { - return fmt.Errorf("第%d个公告的发布日期不能为空", i+1) - } - if _, err := time.Parse(time.RFC3339, publishDateStr); err != nil { - return fmt.Errorf("第%d个公告的发布日期格式错误", i+1) - } - if t, exists := ann["type"]; exists { - if typeStr, ok := t.(string); ok { - if !validTypes[typeStr] { - return fmt.Errorf("第%d个公告的类型值不合法", i+1) - } - } - } - if len(content) > 500 { - return fmt.Errorf("第%d个公告的内容长度不能超过500字符", i+1) - } - if extra, exists := ann["extra"]; exists { - if extraStr, ok := extra.(string); ok && len(extraStr) > 200 { - return fmt.Errorf("第%d个公告的说明长度不能超过200字符", i+1) - } - } - } - return nil + list, err := parseJSONArray(announcementsStr, "系统公告") + if err != nil { + return err + } + if len(list) > 100 { + return fmt.Errorf("系统公告数量不能超过100个") + } + validTypes := map[string]bool{ + "default": true, "ongoing": true, "success": true, "warning": true, "error": true, + } + for i, ann := range list { + content, ok := ann["content"].(string) + if !ok || content == "" { + return fmt.Errorf("第%d个公告缺少内容字段", i+1) + } + publishDateAny, exists := ann["publishDate"] + if !exists { + return fmt.Errorf("第%d个公告缺少发布日期字段", i+1) + } + publishDateStr, ok := publishDateAny.(string) + if !ok || publishDateStr == "" { + return fmt.Errorf("第%d个公告的发布日期不能为空", i+1) + } + if _, err := time.Parse(time.RFC3339, publishDateStr); err != nil { + return fmt.Errorf("第%d个公告的发布日期格式错误", i+1) + } + if t, exists := ann["type"]; exists { + if typeStr, ok := t.(string); ok { + if !validTypes[typeStr] { + return fmt.Errorf("第%d个公告的类型值不合法", i+1) + } + } + } + if len(content) > 500 { + return fmt.Errorf("第%d个公告的内容长度不能超过500字符", i+1) + } + if extra, exists := ann["extra"]; exists { + if extraStr, ok := extra.(string); ok && len(extraStr) > 200 { + return fmt.Errorf("第%d个公告的说明长度不能超过200字符", i+1) + } + } + } + return nil } func validateFAQ(faqStr string) error { - list, err := parseJSONArray(faqStr, "FAQ信息") - if err != nil { - return err - } - if len(list) > 100 { - return fmt.Errorf("FAQ数量不能超过100个") - } - for i, faq := range list { - question, ok := faq["question"].(string) - if !ok || question == "" { - return fmt.Errorf("第%d个FAQ缺少问题字段", i+1) - } - answer, ok := faq["answer"].(string) - if !ok || answer == "" { - return fmt.Errorf("第%d个FAQ缺少答案字段", i+1) - } - if len(question) > 200 { - return fmt.Errorf("第%d个FAQ的问题长度不能超过200字符", i+1) - } - if len(answer) > 1000 { - return fmt.Errorf("第%d个FAQ的答案长度不能超过1000字符", i+1) - } - } - return nil + list, err := parseJSONArray(faqStr, "FAQ信息") + if err != nil { + return err + } + if len(list) > 100 { + return fmt.Errorf("FAQ数量不能超过100个") + } + for i, faq := range list { + question, ok := faq["question"].(string) + if !ok || question == "" { + return fmt.Errorf("第%d个FAQ缺少问题字段", i+1) + } + answer, ok := faq["answer"].(string) + if !ok || answer == "" { + return fmt.Errorf("第%d个FAQ缺少答案字段", i+1) + } + if len(question) > 200 { + return fmt.Errorf("第%d个FAQ的问题长度不能超过200字符", i+1) + } + if len(answer) > 1000 { + return fmt.Errorf("第%d个FAQ的答案长度不能超过1000字符", i+1) + } + } + return nil } func getPublishTime(item map[string]interface{}) time.Time { - if v, ok := item["publishDate"]; ok { - if s, ok2 := v.(string); ok2 { - if t, err := time.Parse(time.RFC3339, s); err == nil { - return t - } - } - } - return time.Time{} + if v, ok := item["publishDate"]; ok { + if s, ok2 := v.(string); ok2 { + if t, err := time.Parse(time.RFC3339, s); err == nil { + return t + } + } + } + return time.Time{} } func GetAnnouncements() []map[string]interface{} { - list := getJSONList(GetConsoleSetting().Announcements) - sort.SliceStable(list, func(i, j int) bool { - return getPublishTime(list[i]).After(getPublishTime(list[j])) - }) - return list + list := getJSONList(GetConsoleSetting().Announcements) + sort.SliceStable(list, func(i, j int) bool { + return getPublishTime(list[i]).After(getPublishTime(list[j])) + }) + return list } func GetFAQ() []map[string]interface{} { - return getJSONList(GetConsoleSetting().FAQ) + return getJSONList(GetConsoleSetting().FAQ) } func validateUptimeKumaGroups(groupsStr string) error { - groups, err := parseJSONArray(groupsStr, "Uptime Kuma分组配置") - if err != nil { - return err - } + groups, err := parseJSONArray(groupsStr, "Uptime Kuma分组配置") + if err != nil { + return err + } - if len(groups) > 20 { - return fmt.Errorf("Uptime Kuma分组数量不能超过20个") - } + if len(groups) > 20 { + return fmt.Errorf("Uptime Kuma分组数量不能超过20个") + } - nameSet := make(map[string]bool) + nameSet := make(map[string]bool) - for i, group := range groups { - categoryName, ok := group["categoryName"].(string) - if !ok || categoryName == "" { - return fmt.Errorf("第%d个分组缺少分类名称字段", i+1) - } - if nameSet[categoryName] { - return fmt.Errorf("第%d个分组的分类名称与其他分组重复", i+1) - } - nameSet[categoryName] = true - urlStr, ok := group["url"].(string) - if !ok || urlStr == "" { - return fmt.Errorf("第%d个分组缺少URL字段", i+1) - } - slug, ok := group["slug"].(string) - if !ok || slug == "" { - return fmt.Errorf("第%d个分组缺少Slug字段", i+1) - } - description, ok := group["description"].(string) - if !ok { - description = "" - } + for i, group := range groups { + categoryName, ok := group["categoryName"].(string) + if !ok || categoryName == "" { + return fmt.Errorf("第%d个分组缺少分类名称字段", i+1) + } + if nameSet[categoryName] { + return fmt.Errorf("第%d个分组的分类名称与其他分组重复", i+1) + } + nameSet[categoryName] = true + urlStr, ok := group["url"].(string) + if !ok || urlStr == "" { + return fmt.Errorf("第%d个分组缺少URL字段", i+1) + } + slug, ok := group["slug"].(string) + if !ok || slug == "" { + return fmt.Errorf("第%d个分组缺少Slug字段", i+1) + } + description, ok := group["description"].(string) + if !ok { + description = "" + } - if err := validateURL(urlStr, i+1, "分组"); err != nil { - return err - } + if err := validateURL(urlStr, i+1, "分组"); err != nil { + return err + } - if len(categoryName) > 50 { - return fmt.Errorf("第%d个分组的分类名称长度不能超过50字符", i+1) - } - if len(urlStr) > 500 { - return fmt.Errorf("第%d个分组的URL长度不能超过500字符", i+1) - } - if len(slug) > 100 { - return fmt.Errorf("第%d个分组的Slug长度不能超过100字符", i+1) - } - if len(description) > 200 { - return fmt.Errorf("第%d个分组的描述长度不能超过200字符", i+1) - } + if len(categoryName) > 50 { + return fmt.Errorf("第%d个分组的分类名称长度不能超过50字符", i+1) + } + if len(urlStr) > 500 { + return fmt.Errorf("第%d个分组的URL长度不能超过500字符", i+1) + } + if len(slug) > 100 { + return fmt.Errorf("第%d个分组的Slug长度不能超过100字符", i+1) + } + if len(description) > 200 { + return fmt.Errorf("第%d个分组的描述长度不能超过200字符", i+1) + } - if !slugRegex.MatchString(slug) { - return fmt.Errorf("第%d个分组的Slug只能包含字母、数字、下划线和连字符", i+1) - } + if !slugRegex.MatchString(slug) { + return fmt.Errorf("第%d个分组的Slug只能包含字母、数字、下划线和连字符", i+1) + } - if err := checkDangerousContent(description, i+1, "分组"); err != nil { - return err - } - if err := checkDangerousContent(categoryName, i+1, "分组"); err != nil { - return err - } - } - return nil + if err := checkDangerousContent(description, i+1, "分组"); err != nil { + return err + } + if err := checkDangerousContent(categoryName, i+1, "分组"); err != nil { + return err + } + } + return nil } func GetUptimeKumaGroups() []map[string]interface{} { - return getJSONList(GetConsoleSetting().UptimeKumaGroups) -} \ No newline at end of file + return getJSONList(GetConsoleSetting().UptimeKumaGroups) +} diff --git a/setting/ratio_setting/expose_ratio.go b/setting/ratio_setting/expose_ratio.go index 8fca0bcb0..783d9778e 100644 --- a/setting/ratio_setting/expose_ratio.go +++ b/setting/ratio_setting/expose_ratio.go @@ -5,13 +5,13 @@ import "sync/atomic" var exposeRatioEnabled atomic.Bool func init() { - exposeRatioEnabled.Store(false) + exposeRatioEnabled.Store(false) } func SetExposeRatioEnabled(enabled bool) { - exposeRatioEnabled.Store(enabled) + exposeRatioEnabled.Store(enabled) } func IsExposeRatioEnabled() bool { - return exposeRatioEnabled.Load() -} \ No newline at end of file + return exposeRatioEnabled.Load() +} diff --git a/setting/ratio_setting/exposed_cache.go b/setting/ratio_setting/exposed_cache.go index 9e5b6c300..2fe2cd09b 100644 --- a/setting/ratio_setting/exposed_cache.go +++ b/setting/ratio_setting/exposed_cache.go @@ -1,55 +1,55 @@ package ratio_setting import ( - "sync" - "sync/atomic" - "time" + "sync" + "sync/atomic" + "time" - "github.com/gin-gonic/gin" + "github.com/gin-gonic/gin" ) const exposedDataTTL = 30 * time.Second type exposedCache struct { - data gin.H - expiresAt time.Time + data gin.H + expiresAt time.Time } var ( - exposedData atomic.Value - rebuildMu sync.Mutex + exposedData atomic.Value + rebuildMu sync.Mutex ) func InvalidateExposedDataCache() { - exposedData.Store((*exposedCache)(nil)) + exposedData.Store((*exposedCache)(nil)) } func cloneGinH(src gin.H) gin.H { - dst := make(gin.H, len(src)) - for k, v := range src { - dst[k] = v - } - return dst + dst := make(gin.H, len(src)) + for k, v := range src { + dst[k] = v + } + return dst } func GetExposedData() gin.H { - if c, ok := exposedData.Load().(*exposedCache); ok && c != nil && time.Now().Before(c.expiresAt) { - return cloneGinH(c.data) - } - rebuildMu.Lock() - defer rebuildMu.Unlock() - if c, ok := exposedData.Load().(*exposedCache); ok && c != nil && time.Now().Before(c.expiresAt) { - return cloneGinH(c.data) - } - newData := gin.H{ - "model_ratio": GetModelRatioCopy(), - "completion_ratio": GetCompletionRatioCopy(), - "cache_ratio": GetCacheRatioCopy(), - "model_price": GetModelPriceCopy(), - } - exposedData.Store(&exposedCache{ - data: newData, - expiresAt: time.Now().Add(exposedDataTTL), - }) - return cloneGinH(newData) -} \ No newline at end of file + if c, ok := exposedData.Load().(*exposedCache); ok && c != nil && time.Now().Before(c.expiresAt) { + return cloneGinH(c.data) + } + rebuildMu.Lock() + defer rebuildMu.Unlock() + if c, ok := exposedData.Load().(*exposedCache); ok && c != nil && time.Now().Before(c.expiresAt) { + return cloneGinH(c.data) + } + newData := gin.H{ + "model_ratio": GetModelRatioCopy(), + "completion_ratio": GetCompletionRatioCopy(), + "cache_ratio": GetCacheRatioCopy(), + "model_price": GetModelPriceCopy(), + } + exposedData.Store(&exposedCache{ + data: newData, + expiresAt: time.Now().Add(exposedDataTTL), + }) + return cloneGinH(newData) +}