质量优化

This commit is contained in:
ryan
2026-06-11 15:36:46 +08:00
parent 6b93320404
commit 533f783268
7 changed files with 70 additions and 57 deletions
+6 -4
View File
@@ -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
+5
View File
@@ -8,4 +8,9 @@ const (
detectContentBytes = 512 // http.DetectContentType 需要的最小字节数
uploadDirPerm = 0755 // 上传目录权限
uploadFilePerm = 0644 // 上传文件权限
imageQualityLow = "low"
imageQualityMedium = "medium"
imageQualityHigh = "high"
imageQualityOrigin = "origin"
storageDriverLocal = "local"
)
+5 -5
View File
@@ -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)
}
+5 -5
View File
@@ -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"},
}
+4 -4
View File
@@ -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
+3 -3
View File
@@ -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
+42 -36
View File
@@ -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 淘汰机制",
},
}