mirror of
https://github.com/infiniflow/ragflow.git
synced 2026-06-29 23:41:12 +08:00
go: add PATCH /api/v1/users/me user settings update (#15297)
### What problem does this PR solve?
- Add Go implementation parity for `PATCH /api/v1/users/me`.
- This updates the Go user settings endpoint to match the Python
behavior for updating the current user's profile settings.
### Changes
- Route `PATCH /api/v1/users/me` through the authenticated current user
from middleware.
- Add `password` and `new_password` support to `UpdateSettingsRequest`.
- Prevent `email` from being updated through this endpoint, matching the
Python blacklist behavior.
- Support updating:
- `nickname`
- `avatar`
- `language`
- `color_schema`
- `timezone`
- `password`
- Align password handling with Python:
- invalid plaintext password payload returns `CodeExceptionError`
- wrong old password returns `Password error!`
- successful update returns `{ code: 0, data: true, message: "success"
}`
### Test
Tested manually with Python and Go backends using the same request
bodies:
- `PATCH /api/v1/users/me` with nickname/timezone update
- plaintext password payload returns Python-compatible `Incorrect
padding`
- wrong old password returns `Password error!`
This commit is contained in:
@@ -411,27 +411,11 @@ func (h *UserHandler) Info(c *gin.Context) {
|
||||
// @Security ApiKeyAuth
|
||||
// @Param request body service.UpdateSettingsRequest true "user settings"
|
||||
// @Success 200 {object} map[string]interface{}
|
||||
// @Router /v1/user/setting [post]
|
||||
// @Router /api/v1/users/me [patch]
|
||||
func (h *UserHandler) Setting(c *gin.Context) {
|
||||
// Extract token from request
|
||||
token := c.GetHeader("Authorization")
|
||||
if token == "" {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": common.CodeUnauthorized,
|
||||
"message": "Missing Authorization header",
|
||||
"data": false,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Get user by token
|
||||
user, code, err := h.userService.GetUserByToken(token)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": code,
|
||||
"message": err.Error(),
|
||||
"data": false,
|
||||
})
|
||||
user, errorCode, errorMessage := GetUser(c)
|
||||
if errorCode != common.CodeSuccess {
|
||||
jsonError(c, errorCode, errorMessage)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -447,8 +431,16 @@ func (h *UserHandler) Setting(c *gin.Context) {
|
||||
}
|
||||
|
||||
// Update user settings
|
||||
code, err = h.userService.UpdateUserSettings(user, &req)
|
||||
code, err := h.userService.UpdateUserSettings(user, &req)
|
||||
if err != nil {
|
||||
if code == common.CodeExceptionError {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": code,
|
||||
"message": err.Error(),
|
||||
"data": nil,
|
||||
})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": code,
|
||||
"message": err.Error(),
|
||||
@@ -459,7 +451,7 @@ func (h *UserHandler) Setting(c *gin.Context) {
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": common.CodeSuccess,
|
||||
"message": "settings updated successfully",
|
||||
"message": "success",
|
||||
"data": true,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -80,11 +80,12 @@ type EmailLoginRequest struct {
|
||||
// UpdateSettingsRequest update user settings request
|
||||
type UpdateSettingsRequest struct {
|
||||
Nickname *string `json:"nickname,omitempty"`
|
||||
Email *string `json:"email,omitempty" binding:"omitempty,email"`
|
||||
Avatar *string `json:"avatar,omitempty"`
|
||||
Language *string `json:"language,omitempty"`
|
||||
ColorSchema *string `json:"color_schema,omitempty"`
|
||||
Timezone *string `json:"timezone,omitempty"`
|
||||
Password *string `json:"password,omitempty"`
|
||||
NewPassword *string `json:"new_password,omitempty"`
|
||||
}
|
||||
|
||||
// ChangePasswordRequest change password request
|
||||
@@ -765,12 +766,44 @@ func (s *UserService) GetUserProfile(user *entity.User) map[string]interface{} {
|
||||
// UpdateUserSettings updates user settings
|
||||
func (s *UserService) UpdateUserSettings(user *entity.User, req *UpdateSettingsRequest) (common.ErrorCode, error) {
|
||||
// Update fields if provided
|
||||
if req.Password != nil {
|
||||
ciphertext, err := base64.StdEncoding.DecodeString(*req.Password)
|
||||
if err != nil {
|
||||
return common.CodeExceptionError, fmt.Errorf("Error('Incorrect padding')")
|
||||
}
|
||||
privateKey, err := s.loadPrivateKey()
|
||||
if err != nil {
|
||||
return common.CodeExceptionError, err
|
||||
}
|
||||
oldPasswordBytes, err := rsa.DecryptPKCS1v15(nil, privateKey, ciphertext)
|
||||
oldPassword := "Fail to decrypt password!"
|
||||
if err == nil {
|
||||
oldPassword = string(oldPasswordBytes)
|
||||
}
|
||||
if user.Password == nil || !s.VerifyPassword(*user.Password, oldPassword) {
|
||||
return common.CodeAuthenticationError, fmt.Errorf("Password error!")
|
||||
}
|
||||
|
||||
if req.NewPassword != nil {
|
||||
ciphertext, err := base64.StdEncoding.DecodeString(*req.NewPassword)
|
||||
if err != nil {
|
||||
return common.CodeExceptionError, fmt.Errorf("Error('Incorrect padding')")
|
||||
}
|
||||
newPasswordBytes, err := rsa.DecryptPKCS1v15(nil, privateKey, ciphertext)
|
||||
if err != nil {
|
||||
return common.CodeExceptionError, err
|
||||
}
|
||||
|
||||
hashedPassword, err := s.HashPassword(string(newPasswordBytes))
|
||||
if err != nil {
|
||||
return common.CodeExceptionError, err
|
||||
}
|
||||
user.Password = &hashedPassword
|
||||
}
|
||||
}
|
||||
if req.Nickname != nil {
|
||||
user.Nickname = *req.Nickname
|
||||
}
|
||||
if req.Email != nil {
|
||||
user.Email = *req.Email
|
||||
}
|
||||
if req.Avatar != nil {
|
||||
// In Go version, avatar might be stored differently
|
||||
// For now, just update if field exists
|
||||
|
||||
Reference in New Issue
Block a user