3a898e8aa0
后端修复: - 验证码存储与校验机制 - 订单创建事务+库存扣减 - GetStats字段名错误 - VerifyEmail改为POST - 供应商更新字段白名单 - 文件删除安全检查 - 抽奖安全随机数 - 用户管理CRUD - 订单取消/确认收货 - 工单回复 - Toggle返回新数据 - 移除死代码 前端修复: - 404兜底路由 - 401软跳转 - API层统一 - 面包屑补充banners - 国际化完善 - 购物车并行删除 - 退出清理购物车 - 供应商Dashboard数据 - 工单详情页 - 订单取消/确认收货
135 lines
3.4 KiB
Go
135 lines
3.4 KiB
Go
package handlers
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/google/uuid"
|
|
)
|
|
|
|
type UploadHandler struct{}
|
|
|
|
func NewUploadHandler() *UploadHandler {
|
|
return &UploadHandler{}
|
|
}
|
|
|
|
func (h *UploadHandler) UploadImage(c *gin.Context) {
|
|
file, err := c.FormFile("file")
|
|
if err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "No file uploaded"})
|
|
return
|
|
}
|
|
|
|
ext := strings.ToLower(filepath.Ext(file.Filename))
|
|
allowedExts := map[string]bool{".jpg": true, ".jpeg": true, ".png": true, ".gif": true, ".webp": true}
|
|
if !allowedExts[ext] {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid file type. Only jpg, jpeg, png, gif, webp are allowed"})
|
|
return
|
|
}
|
|
|
|
uploadDir := "uploads/images"
|
|
if err := os.MkdirAll(uploadDir, 0755); err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create upload directory"})
|
|
return
|
|
}
|
|
|
|
filename := fmt.Sprintf("%s%s", uuid.New().String(), ext)
|
|
filepath := filepath.Join(uploadDir, filename)
|
|
|
|
if err := c.SaveUploadedFile(file, filepath); err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to save file"})
|
|
return
|
|
}
|
|
|
|
url := fmt.Sprintf("/uploads/images/%s", filename)
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"url": url,
|
|
"filename": filename,
|
|
})
|
|
}
|
|
|
|
func (h *UploadHandler) UploadMultiple(c *gin.Context) {
|
|
form, err := c.MultipartForm()
|
|
if err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "No files uploaded"})
|
|
return
|
|
}
|
|
|
|
files := form.File["files"]
|
|
if len(files) == 0 {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "No files uploaded"})
|
|
return
|
|
}
|
|
|
|
uploadDir := "uploads/images"
|
|
if err := os.MkdirAll(uploadDir, 0755); err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create upload directory"})
|
|
return
|
|
}
|
|
|
|
allowedExts := map[string]bool{".jpg": true, ".jpeg": true, ".png": true, ".gif": true, ".webp": true}
|
|
var urls []string
|
|
|
|
for _, file := range files {
|
|
ext := strings.ToLower(filepath.Ext(file.Filename))
|
|
if !allowedExts[ext] {
|
|
continue
|
|
}
|
|
|
|
filename := fmt.Sprintf("%s%s", uuid.New().String(), ext)
|
|
filepath := filepath.Join(uploadDir, filename)
|
|
|
|
if err := c.SaveUploadedFile(file, filepath); err != nil {
|
|
continue
|
|
}
|
|
|
|
urls = append(urls, fmt.Sprintf("/uploads/images/%s", filename))
|
|
}
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"urls": urls,
|
|
})
|
|
}
|
|
|
|
func (h *UploadHandler) DeleteImage(c *gin.Context) {
|
|
filename := c.Param("filename")
|
|
if filename == "" {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "Filename is required"})
|
|
return
|
|
}
|
|
|
|
if strings.Contains(filename, "..") || strings.Contains(filename, "/") || strings.Contains(filename, "\\") {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid filename"})
|
|
return
|
|
}
|
|
|
|
filePath := filepath.Join("uploads/images", filename)
|
|
filePath, err := filepath.Abs(filePath)
|
|
if err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid file path"})
|
|
return
|
|
}
|
|
|
|
absUploadDir, _ := filepath.Abs("uploads/images")
|
|
if !strings.HasPrefix(filePath, absUploadDir) {
|
|
c.JSON(http.StatusForbidden, gin.H{"error": "Access denied"})
|
|
return
|
|
}
|
|
|
|
if _, err := os.Stat(filePath); os.IsNotExist(err) {
|
|
c.JSON(http.StatusNotFound, gin.H{"error": "File not found"})
|
|
return
|
|
}
|
|
|
|
if err := os.Remove(filePath); err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to delete file"})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, gin.H{"message": "File deleted successfully"})
|
|
}
|