- 新增 storage 配置(存储目录、单文件大小上限),ConfigVersion 3→4 自动补全 - internal/file:上传(sha256 秒传去重)、删除(上传者或管理员,引用中 409)、公开查看;本地存储 + 操作日志 + 引用计数,仅安全类型内联防存储型 XSS - internal/avatar:PUT/DELETE /api/me/avatar,自动管理头像文件引用与旧头像解绑 - 前端引入 vue-advanced-cropper,个人中心支持上传/更换/删除头像,裁剪输出 512×512 JPEG;http 请求支持 FormData - 导出 auth.CurrentUser、新增 model.User.IsAdmin 与 testutil 多部件上传辅助,补充接口测试并重新生成 Swagger 文档
103 lines
3.0 KiB
Go
103 lines
3.0 KiB
Go
package auth
|
|
|
|
import (
|
|
"net/http"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"gorm.io/gorm"
|
|
|
|
"rill/internal/httpx"
|
|
"rill/internal/user"
|
|
)
|
|
|
|
// UpdateProfileRequest 更新个人资料请求,仅更新请求中提供的字段;gender 空串表示清空。
|
|
type UpdateProfileRequest struct {
|
|
Nickname *string `json:"nickname" binding:"omitempty,max=50" example:"Alice"`
|
|
Gender *string `json:"gender" binding:"omitempty,len=0|oneof=male female other" example:"male"`
|
|
Birthday *string `json:"birthday" binding:"omitempty" example:"1995-06-15"`
|
|
}
|
|
|
|
// @Summary Get current user profile
|
|
// @Description Return the authenticated user's profile, including groups.
|
|
// @Tags user
|
|
// @Produce json
|
|
// @Success 200 {object} model.User
|
|
// @Security BearerAuth
|
|
// @Failure 401 {object} httpx.ErrorResponse "unauthorized or session expired"
|
|
// @Failure 403 {object} httpx.ErrorResponse "account disabled"
|
|
// @Router /me [get]
|
|
func Me() gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
current, ok := CurrentUser(c)
|
|
if !ok {
|
|
httpx.RespondUnauthorized(c)
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, current)
|
|
}
|
|
}
|
|
|
|
// @Summary Update current user profile
|
|
// @Description Update the authenticated user's nickname, gender, and birthday. birthday accepts YYYY-MM-DD, an empty string clears it, and omitting it keeps the current value.
|
|
// @Tags user
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Param profile body auth.UpdateProfileRequest true "Fields to update"
|
|
// @Success 200 {object} model.User
|
|
// @Failure 400 {object} httpx.ErrorResponse "invalid request"
|
|
// @Security BearerAuth
|
|
// @Failure 401 {object} httpx.ErrorResponse "unauthorized or session expired"
|
|
// @Failure 403 {object} httpx.ErrorResponse "account disabled"
|
|
// @Failure 500 {object} httpx.ErrorResponse
|
|
// @Router /me [put]
|
|
func UpdateMe(db *gorm.DB) gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
var req UpdateProfileRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
c.JSON(http.StatusBadRequest, httpx.ErrorResponse{Error: "invalid request: " + err.Error()})
|
|
return
|
|
}
|
|
|
|
current, ok := CurrentUser(c)
|
|
if !ok {
|
|
httpx.RespondUnauthorized(c)
|
|
return
|
|
}
|
|
|
|
updates := make(map[string]any, 3)
|
|
if req.Nickname != nil {
|
|
updates["nickname"] = *req.Nickname
|
|
current.Nickname = *req.Nickname
|
|
}
|
|
if req.Gender != nil {
|
|
updates["gender"] = *req.Gender
|
|
current.Gender = *req.Gender
|
|
}
|
|
if req.Birthday != nil {
|
|
birthday, err := user.NormalizeBirthday(req.Birthday)
|
|
if err != nil {
|
|
c.JSON(http.StatusBadRequest, httpx.ErrorResponse{Error: err.Error()})
|
|
return
|
|
}
|
|
if birthday.IsZero() {
|
|
updates["birthday"] = nil
|
|
} else {
|
|
updates["birthday"] = birthday
|
|
}
|
|
current.Birthday = birthday
|
|
}
|
|
if len(updates) == 0 {
|
|
c.JSON(http.StatusOK, current)
|
|
return
|
|
}
|
|
|
|
ctx := c.Request.Context()
|
|
if err := db.WithContext(ctx).Model(¤t).Updates(updates).Error; err != nil {
|
|
httpx.RespondDBError(c, err)
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, current)
|
|
}
|
|
}
|