mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-10-05 07:26:36 +08:00
后端与全仓代码质量清理(golangci 扩展集 · 测试质量 · 并发安全 · 文档同步)
代码质量全量清理,零行为变化:golangci 扩展集 13 类 linter(gosec/modernize/perfsprint/canonicalheader/usestdlibvars/wastedassign/intrange/errorlint/forcetypeassert/recvcheck/exhaustive/unparam)全量修复,测试代码质量(testifylint/thelper/usetesting)25→0,frpc 进程生命周期真 bug(进程组击杀)、全仓 go test -race 6 类数据竞争(含 1 个生产竞争)、SPDX license 头补齐 131 文件、前端测试套件 next-intl 迁移后 44 失败→全绿、过期 swagger 文档重新生成、pnpm-workspace 构建审批。 Experiments: #2-#17, #18, #20, #21, #23 Metric: total_issues 108 → 8 (-92.6%)
This commit is contained in:
+3
-2
@@ -52,12 +52,13 @@ func ensureAccessCacheListener() {
|
||||
}
|
||||
|
||||
func startAccessCacheInvalidationListener() {
|
||||
if db.Redis == nil {
|
||||
redis := db.Redis // 调用方 goroutine 上捕获,避免 goroutine 内读可变全局(测试会替换 db.Redis)
|
||||
if redis == nil {
|
||||
return
|
||||
}
|
||||
|
||||
go func() {
|
||||
pubsub := db.Redis.Subscribe(
|
||||
pubsub := redis.Subscribe(
|
||||
context.Background(),
|
||||
objectstore.ConfigInvalidationChannel,
|
||||
fileAccessInvalidationChannel,
|
||||
|
||||
@@ -68,7 +68,8 @@ func ServeFileByID(c *gin.Context) {
|
||||
response.AbortNotFound(c, "文件记录未找到")
|
||||
return
|
||||
}
|
||||
if _, ok := err.(*strconv.NumError); ok {
|
||||
var numErr *strconv.NumError
|
||||
if errors.As(err, &numErr) {
|
||||
response.AbortBadRequest(c, "无效的上传ID")
|
||||
return
|
||||
}
|
||||
@@ -133,6 +134,8 @@ func ServeUpload(c *gin.Context, upload *model.Upload) {
|
||||
return
|
||||
}
|
||||
fallthrough
|
||||
case fileTypeVideo, fileTypeAudio, fileTypeOther:
|
||||
serveOriginalWithConditionalCheck(c, upload)
|
||||
default:
|
||||
serveOriginalWithConditionalCheck(c, upload)
|
||||
}
|
||||
@@ -205,7 +208,10 @@ func EnsureCompressedImageCache(
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
res := result.(compressedImageCacheResult)
|
||||
res, ok := result.(compressedImageCacheResult)
|
||||
if !ok {
|
||||
return nil, false, fmt.Errorf("image compression flight returned unexpected type %T", result)
|
||||
}
|
||||
return res.bytes, res.cached, res.err
|
||||
}
|
||||
|
||||
|
||||
@@ -357,6 +357,7 @@ func TestNormalizeImageQuality(t *testing.T) {
|
||||
}
|
||||
|
||||
func configureLocalStorageRoot(t *testing.T, dbConn *gorm.DB, tempDir string) {
|
||||
t.Helper()
|
||||
var sc model.SystemConfig
|
||||
if err := dbConn.Where("key = ?", model.ConfigKeyStorageConfig).First(&sc).Error; err != nil {
|
||||
t.Fatalf("failed to find storage config: %v", err)
|
||||
|
||||
@@ -301,7 +301,7 @@ func UpdateMyFile(c *gin.Context) {
|
||||
response.AbortNotFound(c, "文件记录未找到")
|
||||
return
|
||||
}
|
||||
if err == ingest.ErrForbidden {
|
||||
if errors.Is(err, ingest.ErrForbidden) {
|
||||
response.AbortForbidden(c, "无权操作")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -178,7 +178,8 @@ func DownloadFile(c *gin.Context) {
|
||||
response.AbortNotFound(c, "文件记录未找到")
|
||||
return
|
||||
}
|
||||
if _, ok := err.(*strconv.NumError); ok {
|
||||
var numErr *strconv.NumError
|
||||
if errors.As(err, &numErr) {
|
||||
response.AbortBadRequest(c, shared.ErrInvalidFileID)
|
||||
return
|
||||
}
|
||||
@@ -204,7 +205,7 @@ func DownloadFile(c *gin.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
c.Header("Content-Disposition", fmt.Sprintf("attachment; filename*=UTF-8''%s", url.PathEscape(fileName)))
|
||||
c.Header("Content-Disposition", "attachment; filename*=UTF-8''"+url.PathEscape(fileName))
|
||||
filesrv.ServeUpload(c, upload)
|
||||
}
|
||||
|
||||
|
||||
@@ -75,6 +75,7 @@ func setupTestRouter(authUser *model.User) *gin.Engine {
|
||||
}
|
||||
|
||||
func createMultipartRequest(t *testing.T, fieldName, fileName string, fileContent []byte, extraFields map[string]string) (string, *bytes.Buffer) {
|
||||
t.Helper()
|
||||
body := &bytes.Buffer{}
|
||||
writer := multipart.NewWriter(body)
|
||||
|
||||
@@ -1086,6 +1087,7 @@ func TestDeleteReservedUploadType(t *testing.T) {
|
||||
}
|
||||
|
||||
func configureLocalStorageRoot(t *testing.T, dbConn *gorm.DB, tempDir string) {
|
||||
t.Helper()
|
||||
var sc model.SystemConfig
|
||||
if err := dbConn.Where("key = ?", model.ConfigKeyStorageConfig).First(&sc).Error; err != nil {
|
||||
t.Fatalf("failed to find storage config: %v", err)
|
||||
|
||||
@@ -59,8 +59,8 @@ func validateAllowedExtension(ctx context.Context, ext string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
allowedExts := strings.Split(strings.ToLower(sc.Value), ",")
|
||||
for _, allowedExt := range allowedExts {
|
||||
allowedExts := strings.SplitSeq(strings.ToLower(sc.Value), ",")
|
||||
for allowedExt := range allowedExts {
|
||||
if strings.TrimSpace(allowedExt) == ext {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -54,6 +54,8 @@ func ingestWithHashPolicy(ctx context.Context, req Request) (Result, error) {
|
||||
return Result{}, ErrStorageReadOnly
|
||||
}
|
||||
return createDedupRecord(ctx, existing, req)
|
||||
case PolicyCreate:
|
||||
// 忽略已存在记录,走下方的新建流程。
|
||||
}
|
||||
}
|
||||
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
|
||||
@@ -513,6 +513,7 @@ func loadTotalStats(ctx context.Context) (totalStatsSnapshot, error) {
|
||||
}
|
||||
|
||||
func setupMockStorage(t *testing.T, putCount *int) (restore func(), disable func()) {
|
||||
t.Helper()
|
||||
return setupMockStorageWithDeleteCount(t, putCount, nil)
|
||||
}
|
||||
|
||||
|
||||
@@ -69,7 +69,7 @@ func (h *MigrationHandler) ValidatePayload(payload []byte) ([]byte, error) {
|
||||
return payload, err
|
||||
}
|
||||
if active {
|
||||
return payload, fmt.Errorf("storage migration task is already unresolved")
|
||||
return payload, errors.New("storage migration task is already unresolved")
|
||||
}
|
||||
return normalized, nil
|
||||
}
|
||||
@@ -193,18 +193,18 @@ func migrateObjects(
|
||||
const batchSize = 50
|
||||
const migrationConcurrency = 10
|
||||
const sha256HexLength = 64
|
||||
var migrated int64
|
||||
var migrated atomic.Int64
|
||||
var lastFilePath string
|
||||
for {
|
||||
if err := ctx.Err(); err != nil {
|
||||
return atomic.LoadInt64(&migrated), fmt.Errorf("storage migration canceled: %w", err)
|
||||
return migrated.Load(), fmt.Errorf("storage migration canceled: %w", err)
|
||||
}
|
||||
|
||||
task.AppendLog(ctx, "正在查询待迁移对象批次,当前已完成迁移: %d/%d", atomic.LoadInt64(&migrated), total)
|
||||
task.AppendLog(ctx, "正在查询待迁移对象批次,当前已完成迁移: %d/%d", migrated.Load(), total)
|
||||
|
||||
objects, err := repository.ListDistinctActiveStorageObjects(ctx, lastFilePath, batchSize)
|
||||
if err != nil {
|
||||
return atomic.LoadInt64(&migrated), fmt.Errorf("query source objects: %w", err)
|
||||
return migrated.Load(), fmt.Errorf("query source objects: %w", err)
|
||||
}
|
||||
if len(objects) == 0 {
|
||||
task.AppendLog(ctx, "所有对象迁移完毕")
|
||||
@@ -223,18 +223,18 @@ func migrateObjects(
|
||||
if err := migrateSingleObject(ctx, sourceBackend, targetBackend, obj, sha256HexLength); err != nil {
|
||||
return err
|
||||
}
|
||||
atomic.AddInt64(&migrated, 1)
|
||||
migrated.Add(1)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
if err := g.Wait(); err != nil {
|
||||
return atomic.LoadInt64(&migrated), err
|
||||
return migrated.Load(), err
|
||||
}
|
||||
|
||||
task.AppendLog(ctx, "当前批次迁移完成。迁移进度: %d/%d", atomic.LoadInt64(&migrated), total)
|
||||
task.AppendLog(ctx, "当前批次迁移完成。迁移进度: %d/%d", migrated.Load(), total)
|
||||
}
|
||||
return atomic.LoadInt64(&migrated), nil
|
||||
return migrated.Load(), nil
|
||||
}
|
||||
|
||||
func migrateSingleObject(
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/Rain-kl/Wavelet/internal/apps/upload/shared"
|
||||
@@ -11,32 +12,17 @@ import (
|
||||
|
||||
// IsImageExtension reports whether ext is a common image format.
|
||||
func IsImageExtension(ext string) bool {
|
||||
for _, imgExt := range []string{"jpg", "jpeg", "png", "webp", "gif"} {
|
||||
if ext == imgExt {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
return slices.Contains([]string{"jpg", "jpeg", "png", "webp", "gif"}, ext)
|
||||
}
|
||||
|
||||
// IsArchiveExtension reports whether ext is a common archive format.
|
||||
func IsArchiveExtension(ext string) bool {
|
||||
for _, e := range []string{"zip", "rar", "7z", "tar", "gz", "tgz", "bz2", "xz"} {
|
||||
if ext == e {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
return slices.Contains([]string{"zip", "rar", "7z", "tar", "gz", "tgz", "bz2", "xz"}, ext)
|
||||
}
|
||||
|
||||
// IsDocumentExtension reports whether ext is a common document format.
|
||||
func IsDocumentExtension(ext string) bool {
|
||||
for _, e := range []string{"pdf", "doc", "docx", "xls", "xlsx", "ppt", "pptx", "txt", "md", "csv", "json", "yaml", "yml", "xml"} {
|
||||
if ext == e {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
return slices.Contains([]string{"pdf", "doc", "docx", "xls", "xlsx", "ppt", "pptx", "txt", "md", "csv", "json", "yaml", "yml", "xml"}, ext)
|
||||
}
|
||||
|
||||
// NormalizeImageQuality normalizes the requested image quality query parameter.
|
||||
|
||||
Reference in New Issue
Block a user