Files
jiang13-bbs/backend/handler/upload.go
freefire 8c5de13899 fix: 空评论列表 500,并完善撤回审计与角色说明
空切片 JSON 统一为数组;撤回保留正文供站长阅读,成员侧居中提示与重新编辑;头像支持拖放;角色弹窗补全职责说明并汇入封禁。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-16 00:23:55 +08:00

101 lines
2.9 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package handler
import (
"errors"
"io"
"net/http"
"strconv"
"github.com/freefire/jiang13-bbs/middleware"
"github.com/freefire/jiang13-bbs/service"
"github.com/gin-gonic/gin"
)
// UploadAvatar 上传裁剪后的头像(multipart 字段 file,内容必须是 WebP)
func (h *Handlers) UploadAvatar(c *gin.Context) {
claims := middleware.CurrentUser(c)
// 限制请求体:以裁剪后文件为依据,允许少量 multipart 边界开销
c.Request.Body = http.MaxBytesReader(c.Writer, c.Request.Body, service.AvatarMaxBytes+4096)
fh, err := c.FormFile("file")
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "图片过大(裁剪后不能超过 2MB)或格式不正确"})
return
}
if fh.Size > service.AvatarMaxBytes {
c.JSON(http.StatusBadRequest, gin.H{"error": "裁剪后的图片不能超过 2MB"})
return
}
f, err := fh.Open()
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "读取图片失败"})
return
}
defer f.Close()
// fh.Size 已受 MaxBytesReader 约束;读取时二次防御
data, err := io.ReadAll(io.LimitReader(f, service.AvatarMaxBytes+1))
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "读取图片失败"})
return
}
att, err := h.Upload.SaveAvatar(claims.ID, data)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{"url": att.URL, "attachment": att})
}
// UseAvatarRequest 选用历史头像请求
type UseAvatarRequest struct {
URL string `json:"url"`
}
// UseAvatar 选用本人历史上传的头像
func (h *Handlers) UseAvatar(c *gin.Context) {
claims := middleware.CurrentUser(c)
var req UseAvatarRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "请求参数格式错误"})
return
}
if err := h.Upload.UseAvatar(claims.ID, req.URL); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{"url": req.URL})
}
// MyMedia 媒体库:本人上传的全部图片(头像、帖子插图等)
func (h *Handlers) MyMedia(c *gin.Context) {
claims := middleware.CurrentUser(c)
list, err := h.Upload.ListMedia(claims.ID)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{"attachments": nonNilSlice(list)})
}
// DeleteAttachment 彻底删除本人附件(DB + 物理文件)
func (h *Handlers) DeleteAttachment(c *gin.Context) {
claims := middleware.CurrentUser(c)
id, err := strconv.ParseUint(c.Param("id"), 10, 64)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "无效的附件 ID"})
return
}
if err := h.Upload.DeleteAttachment(claims.ID, uint(id)); err != nil {
if errors.Is(err, service.ErrAttachmentInUse) {
c.JSON(http.StatusConflict, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{"ok": true})
}