From 533f7832689e362f70fa7d8ae69b053886a3605b Mon Sep 17 00:00:00 2001 From: ryan Date: Thu, 11 Jun 2026 15:36:46 +0800 Subject: [PATCH] =?UTF-8?q?=E8=B4=A8=E9=87=8F=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/apps/admin/system_config/routers.go | 10 ++- internal/apps/upload/constants.go | 5 ++ internal/apps/upload/file_server.go | 10 +-- internal/apps/upload/file_server_test.go | 10 +-- internal/apps/upload/routers.go | 8 +- internal/apps/upload/utils.go | 6 +- internal/testhelper/test_helper.go | 78 +++++++++++--------- 7 files changed, 70 insertions(+), 57 deletions(-) diff --git a/internal/apps/admin/system_config/routers.go b/internal/apps/admin/system_config/routers.go index 275ed5c6..764d0eac 100644 --- a/internal/apps/admin/system_config/routers.go +++ b/internal/apps/admin/system_config/routers.go @@ -16,6 +16,8 @@ import ( "gorm.io/gorm" ) +const maskedConfigValue = "******" + // CreateSystemConfigRequest 创建系统配置请求 type CreateSystemConfigRequest struct { Key string `json:"key" binding:"required,max=64"` @@ -117,7 +119,7 @@ func ListSystemConfigs(c *gin.Context) { for i := range configs { if configs[i].Key == model.ConfigKeySMTPPassword && configs[i].Value != "" { - configs[i].Value = "******" + configs[i].Value = maskedConfigValue } } @@ -149,7 +151,7 @@ func GetSystemConfig(c *gin.Context) { } if config.Key == model.ConfigKeySMTPPassword && config.Value != "" { - config.Value = "******" + config.Value = maskedConfigValue } c.JSON(http.StatusOK, util.OK(config)) @@ -200,7 +202,7 @@ func UpdateSystemConfig(c *gin.Context) { updates["visibility"] = *req.Visibility config.Visibility = *req.Visibility } - if key != model.ConfigKeySMTPPassword || req.Value != "******" { + if key != model.ConfigKeySMTPPassword || req.Value != maskedConfigValue { updates["value"] = req.Value config.Value = req.Value } @@ -256,7 +258,7 @@ func TestSMTP(c *gin.Context) { } password := req.SMTPPassword - if password == "******" { + if password == maskedConfigValue { var sc model.SystemConfig if err := sc.GetByKey(c.Request.Context(), model.ConfigKeySMTPPassword); err == nil { password = sc.Value diff --git a/internal/apps/upload/constants.go b/internal/apps/upload/constants.go index ad1c41f3..2276d8fc 100644 --- a/internal/apps/upload/constants.go +++ b/internal/apps/upload/constants.go @@ -8,4 +8,9 @@ const ( detectContentBytes = 512 // http.DetectContentType 需要的最小字节数 uploadDirPerm = 0755 // 上传目录权限 uploadFilePerm = 0644 // 上传文件权限 + imageQualityLow = "low" + imageQualityMedium = "medium" + imageQualityHigh = "high" + imageQualityOrigin = "origin" + storageDriverLocal = "local" ) diff --git a/internal/apps/upload/file_server.go b/internal/apps/upload/file_server.go index f8264455..4e00168e 100644 --- a/internal/apps/upload/file_server.go +++ b/internal/apps/upload/file_server.go @@ -91,7 +91,7 @@ func ServeUpload(c *gin.Context, upload *model.Upload) { quality := normalizeImageQuality(c.Query("quality")) isImage := strings.HasPrefix(strings.ToLower(upload.MimeType), "image/") || isImageExtension(strings.ToLower(upload.Extension)) - if quality == "origin" || !isImage { + if quality == imageQualityOrigin || !isImage { serveOriginal(c, upload) return } @@ -142,16 +142,16 @@ func imageCompressionCacheKey(upload *model.Upload, quality string) string { func normalizeImageQuality(quality string) string { switch strings.ToLower(quality) { - case "low", "medium", "high": + case imageQualityLow, imageQualityMedium, imageQualityHigh: return strings.ToLower(quality) default: - return "origin" + return imageQualityOrigin } } // serveOriginal 原始文件的流式响应逻辑 func serveOriginal(c *gin.Context, upload *model.Upload) { - if upload.StorageDriver == "local" || (upload.StorageDriver == "" && !storage.IsEnabled()) { + if upload.StorageDriver == storageDriverLocal || (upload.StorageDriver == "" && !storage.IsEnabled()) { c.File(upload.FilePath) return } @@ -178,7 +178,7 @@ func serveOriginal(c *gin.Context, upload *model.Upload) { // getOriginalFileBytes 获取原始文件所有字节 func getOriginalFileBytes(ctx context.Context, upload *model.Upload) ([]byte, error) { - if upload.StorageDriver == "local" || (upload.StorageDriver == "" && !storage.IsEnabled()) { + if upload.StorageDriver == storageDriverLocal || (upload.StorageDriver == "" && !storage.IsEnabled()) { return os.ReadFile(upload.FilePath) } diff --git a/internal/apps/upload/file_server_test.go b/internal/apps/upload/file_server_test.go index 76835b64..bc38573d 100644 --- a/internal/apps/upload/file_server_test.go +++ b/internal/apps/upload/file_server_test.go @@ -292,7 +292,7 @@ func TestImageCompression(t *testing.T) { t.Errorf("expected Content-Type image/webp, got %s", w.Header().Get("Content-Type")) } - cacheKey := imageCompressionCacheKey(&uploadRecord, "medium") + cacheKey := imageCompressionCacheKey(&uploadRecord, imageQualityMedium) cachedBytes, err := cache.Get(cacheKey) if err != nil { t.Fatalf("disk cache Get(%q) returned error: %v", cacheKey, err) @@ -343,11 +343,11 @@ func TestNormalizeImageQuality(t *testing.T) { quality string want string }{ - {name: "low", quality: "low", want: "low"}, - {name: "medium", quality: "medium", want: "medium"}, - {name: "high", quality: "high", want: "high"}, + {name: imageQualityLow, quality: imageQualityLow, want: imageQualityLow}, + {name: imageQualityMedium, quality: imageQualityMedium, want: imageQualityMedium}, + {name: imageQualityHigh, quality: imageQualityHigh, want: imageQualityHigh}, {name: "origin", quality: "origin", want: "origin"}, - {name: "uppercase", quality: "LOW", want: "low"}, + {name: "uppercase", quality: "LOW", want: imageQualityLow}, {name: "empty", quality: "", want: "origin"}, {name: "invalid", quality: "maximum", want: "origin"}, } diff --git a/internal/apps/upload/routers.go b/internal/apps/upload/routers.go index 024e98bf..3d985d1e 100644 --- a/internal/apps/upload/routers.go +++ b/internal/apps/upload/routers.go @@ -200,7 +200,7 @@ func DownloadFile(c *gin.Context) { quality := normalizeImageQuality(c.Query("quality")) isImage := strings.HasPrefix(strings.ToLower(upload.MimeType), "image/") || isImageExtension(strings.ToLower(upload.Extension)) - if quality != "origin" && isImage { + if quality != imageQualityOrigin && isImage { ext := filepath.Ext(fileName) if ext != "" { fileName = strings.TrimSuffix(fileName, ext) + ".webp" @@ -290,7 +290,7 @@ func BatchDownloadFiles(c *gin.Context) { // 打开底层文件数据源 var rc io.ReadCloser - if upload.StorageDriver == "local" || (upload.StorageDriver == "" && !storage.IsEnabled()) { + if upload.StorageDriver == storageDriverLocal || (upload.StorageDriver == "" && !storage.IsEnabled()) { fileSrc, err := os.Open(upload.FilePath) if err != nil { logger.ErrorF(ctx, "打包时读取本地文件失败: %v", err) @@ -516,7 +516,7 @@ func storeUploadFile(ctx context.Context, id uint64, ext, subPath string, size i logger.ErrorF(ctx, "本地磁盘写入文件失败: %v", err) return "", "", ErrSaveFileFailed } - return "local", localPath, "" + return storageDriverLocal, localPath, "" } // isImageExtension 判断文件扩展名是否属于常见图片格式 @@ -556,7 +556,7 @@ func detectMimeType(buf *bytes.Buffer, header *multipart.FileHeader, size int64) // saveUploadRecord 保存上传记录到数据库,失败时清理本地垃圾文件 func saveUploadRecord(ctx context.Context, upload *model.Upload, storageDriver, filePath string) string { if err := db.DB(ctx).Create(upload).Error; err != nil { - if storageDriver == "local" { + if storageDriver == storageDriverLocal { _ = os.Remove(filePath) } return ErrSaveUploadRecordFailed diff --git a/internal/apps/upload/utils.go b/internal/apps/upload/utils.go index bdb87ffb..5f65aa4c 100644 --- a/internal/apps/upload/utils.go +++ b/internal/apps/upload/utils.go @@ -54,11 +54,11 @@ func CompressImageToWebP(srcReader io.Reader, quality string) ([]byte, error) { // Determine quality var qualityScore float32 switch strings.ToLower(quality) { - case "low": + case imageQualityLow: qualityScore = 60 - case "medium": + case imageQualityMedium: qualityScore = 75 - case "high", "": + case imageQualityHigh, "": qualityScore = 85 default: qualityScore = 85 diff --git a/internal/testhelper/test_helper.go b/internal/testhelper/test_helper.go index 8a68d6c4..23f19027 100644 --- a/internal/testhelper/test_helper.go +++ b/internal/testhelper/test_helper.go @@ -17,6 +17,12 @@ import ( "gorm.io/gorm" ) +const ( + configTypeSystem = "system" + configValueTrue = "true" + configValueFalse = "false" +) + // SetupTestEnvironment initializes an in-memory SQLite DB, seeds default configurations, // starts miniredis, and overrides the global db/Redis clients. It returns a cleanup function. func SetupTestEnvironment(t *testing.T) (*gorm.DB, *miniredis.Miniredis, func()) { @@ -79,37 +85,37 @@ func getSeedConfigsPart1() []model.SystemConfig { { Key: model.ConfigKeyUploadAllowedExtensions, Value: "jpg,png,webp", - Type: "system", + Type: configTypeSystem, Description: "允许上传的图片扩展名(逗号分隔)", }, { Key: model.ConfigKeySiteName, Value: "Wavelet", - Type: "system", + Type: configTypeSystem, Description: "系统平台的展示名称", }, { Key: model.ConfigKeyPasswordLoginEnabled, - Value: "true", - Type: "system", + Value: configValueTrue, + Type: configTypeSystem, Description: "是否允许使用账号密码登录", }, { Key: model.ConfigKeyRegistrationEnabled, - Value: "true", - Type: "system", + Value: configValueTrue, + Type: configTypeSystem, Description: "控制普通用户是否可以自主注册(true/false)", }, { Key: model.ConfigKeyPasswordRegisterEnabled, - Value: "true", - Type: "system", + Value: configValueTrue, + Type: configTypeSystem, Description: "是否允许通过密码创建本地账号", }, { Key: model.ConfigKeyOIDCLoginEnabled, - Value: "true", - Type: "system", + Value: configValueTrue, + Type: configTypeSystem, Description: "是否允许使用第三方 OIDC 认证源登录", }, { @@ -120,44 +126,44 @@ func getSeedConfigsPart1() []model.SystemConfig { }, { Key: model.ConfigKeyCapLoginEnabled, - Value: "false", - Type: "system", + Value: configValueFalse, + Type: configTypeSystem, Description: "是否启用登录人机验证(true/false)", }, { Key: model.ConfigKeyCapAutoSolve, - Value: "true", - Type: "system", + Value: configValueTrue, + Type: configTypeSystem, Description: "打开页面后是否自动开始计算,关闭则需用户手动点击触发", }, { Key: model.ConfigKeyCapChallengeCount, Value: "1", - Type: "system", + Type: configTypeSystem, Description: "客户端需求解的 PoW 难题总数,默认 1,推荐 1~5", }, { Key: model.ConfigKeyCapChallengeSize, Value: "32", - Type: "system", + Type: configTypeSystem, Description: "人机验证盐值长度", }, { Key: model.ConfigKeyCapChallengeDifficulty, Value: "4", - Type: "system", + Type: configTypeSystem, Description: "人机验证 PoW 难度(目标前缀长度)", }, { Key: model.ConfigKeyCapChallengeTTL, Value: "600", - Type: "system", + Type: configTypeSystem, Description: "人机验证难题有效时间(秒)", }, { Key: model.ConfigKeyCapTokenTTL, Value: "1200", - Type: "system", + Type: configTypeSystem, Description: "人机验证兑换凭证有效时间(秒)", }, } @@ -168,79 +174,79 @@ func getSeedConfigsPart2() []model.SystemConfig { { Key: model.ConfigKeyServerAddress, Value: "", - Type: "system", + Type: configTypeSystem, Description: "服务器地址(用于跨域源控制,不设定则允许任意源)", }, { Key: model.ConfigKeySMTPHost, Value: "", - Type: "system", + Type: configTypeSystem, Description: "SMTP 服务器地址(例如 smtp.example.com)", }, { Key: model.ConfigKeySMTPPort, Value: "587", - Type: "system", + Type: configTypeSystem, Description: "SMTP 端口(例如 587 或 465)", }, { Key: model.ConfigKeySMTPUsername, Value: "", - Type: "system", + Type: configTypeSystem, Description: "SMTP 账户(如 sender@example.com)", }, { Key: model.ConfigKeySMTPPassword, Value: "", - Type: "system", + Type: configTypeSystem, Description: "SMTP 访问凭证(授权码/密码)", }, { Key: model.ConfigKeyEmailLoginVerificationEnabled, - Value: "false", - Type: "system", + Value: configValueFalse, + Type: configTypeSystem, Description: "是否开启邮箱登录验证(true/false)", }, { Key: model.ConfigKeyEmailRegisterVerificationEnabled, - Value: "false", - Type: "system", + Value: configValueFalse, + Type: configTypeSystem, Description: "是否开启邮箱注册验证(true/false)", }, { Key: model.ConfigKeyMenuDisplayConfig, Value: "{}", - Type: "system", + Type: configTypeSystem, Description: "目录显示配置(JSON 字符串,格式为 {url: enabled})", }, { Key: model.ConfigKeySearchEngineIndexingEnabled, - Value: "false", - Type: "system", + Value: configValueFalse, + Type: configTypeSystem, Description: "是否允许搜索引擎检索", }, { Key: model.ConfigKeyFileAccessWhitelist, Value: `["avatar"]`, - Type: "system", + Type: configTypeSystem, Description: "免登录访问的文件业务类型白名单", }, { Key: model.ConfigKeyDiskCacheMaxSizeMB, Value: "100", - Type: "system", + Type: configTypeSystem, Description: "磁盘缓存最大空间大小 (MB)", }, { Key: model.ConfigKeyDiskCacheTTLMinutes, Value: "60", - Type: "system", + Type: configTypeSystem, Description: "磁盘缓存默认有效期 (分钟)", }, { Key: model.ConfigKeyDiskCacheLRUEnabled, - Value: "true", - Type: "system", + Value: configValueTrue, + Type: configTypeSystem, Description: "是否启用 LRU 淘汰机制", }, }