refactor: 提取 avatar handler 魔法数字为具名常量

- maxAvatarSize = 5 * 1024 * 1024 (5MB)
- magicBytesBufSize = 512
- avatarTokenLen = 8
- dirPerm = 0o755
- filePerm = 0o644
This commit is contained in:
2026-05-08 12:42:35 +08:00
parent 1f7a223768
commit 9ad7b5c0df

View File

@@ -32,6 +32,14 @@ func NewAvatarHandler(userRepo avatarUserRepository) *AvatarHandler {
return &AvatarHandler{userRepo: userRepo} return &AvatarHandler{userRepo: userRepo}
} }
const (
maxAvatarSize = 5 * 1024 * 1024 // 5MB
magicBytesBufSize = 512
avatarTokenLen = 8
dirPerm = 0o755
filePerm = 0o644
)
// generateSecureToken generates a secure random token // generateSecureToken generates a secure random token
func generateSecureToken(length int) string { func generateSecureToken(length int) string {
bytes := make([]byte, length) bytes := make([]byte, length)
@@ -93,7 +101,7 @@ func (h *AvatarHandler) UploadAvatar(c *gin.Context) {
} }
// Validate file size (max 5MB) // Validate file size (max 5MB)
if file.Size > 5*1024*1024 { if file.Size > maxAvatarSize {
c.JSON(http.StatusBadRequest, gin.H{"code": 400, "message": "file size exceeds 5MB limit"}) c.JSON(http.StatusBadRequest, gin.H{"code": 400, "message": "file size exceeds 5MB limit"})
return return
} }
@@ -115,7 +123,7 @@ func (h *AvatarHandler) UploadAvatar(c *gin.Context) {
defer src.Close() defer src.Close()
// Validate Magic Bytes to detect actual file type (prevents file extension spoofing) // Validate Magic Bytes to detect actual file type (prevents file extension spoofing)
buf := make([]byte, 512) buf := make([]byte, magicBytesBufSize)
n, err := src.Read(buf) n, err := src.Read(buf)
if err != nil && err != io.EOF { if err != nil && err != io.EOF {
c.JSON(http.StatusBadRequest, gin.H{"code": 400, "message": "failed to read file"}) c.JSON(http.StatusBadRequest, gin.H{"code": 400, "message": "failed to read file"})
@@ -140,11 +148,11 @@ func (h *AvatarHandler) UploadAvatar(c *gin.Context) {
} }
// Generate unique filename // Generate unique filename
avatarFilename := fmt.Sprintf("avatar_%d_%s%s", userID, generateSecureToken(8), ext) avatarFilename := fmt.Sprintf("avatar_%d_%s%s", userID, generateSecureToken(avatarTokenLen), ext)
uploadDir := "./uploads/avatars" uploadDir := "./uploads/avatars"
// Create upload directory if not exists // Create upload directory if not exists
if err := os.MkdirAll(uploadDir, 0o755); err != nil { if err := os.MkdirAll(uploadDir, dirPerm); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"code": 500, "message": "failed to create upload directory"}) c.JSON(http.StatusInternalServerError, gin.H{"code": 500, "message": "failed to create upload directory"})
return return
} }
@@ -156,7 +164,7 @@ func (h *AvatarHandler) UploadAvatar(c *gin.Context) {
c.JSON(http.StatusInternalServerError, gin.H{"code": 500, "message": "failed to read uploaded file"}) c.JSON(http.StatusInternalServerError, gin.H{"code": 500, "message": "failed to read uploaded file"})
return return
} }
if err := os.WriteFile(dstPath, data, 0o644); err != nil { if err := os.WriteFile(dstPath, data, filePerm); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"code": 500, "message": "failed to save avatar file"}) c.JSON(http.StatusInternalServerError, gin.H{"code": 500, "message": "failed to save avatar file"})
return return
} }