mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-09-29 05:56:38 +08:00
质量优化
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -8,4 +8,9 @@ const (
|
||||
detectContentBytes = 512 // http.DetectContentType 需要的最小字节数
|
||||
uploadDirPerm = 0755 // 上传目录权限
|
||||
uploadFilePerm = 0644 // 上传文件权限
|
||||
imageQualityLow = "low"
|
||||
imageQualityMedium = "medium"
|
||||
imageQualityHigh = "high"
|
||||
imageQualityOrigin = "origin"
|
||||
storageDriverLocal = "local"
|
||||
)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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"},
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 淘汰机制",
|
||||
},
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user