diff --git a/backend/internal/controller/user_controller.go b/backend/internal/controller/user_controller.go index ee207e4c..9dede1ed 100644 --- a/backend/internal/controller/user_controller.go +++ b/backend/internal/controller/user_controller.go @@ -47,6 +47,9 @@ func NewUserController(group *gin.RouterGroup, authMiddleware *middleware.AuthMi group.POST("/one-time-access-token/:token", rateLimitMiddleware.Add(rate.Every(10*time.Second), 5), uc.exchangeOneTimeAccessTokenHandler) group.POST("/one-time-access-token/setup", uc.getSetupAccessTokenHandler) group.POST("/one-time-access-email", rateLimitMiddleware.Add(rate.Every(10*time.Minute), 3), uc.requestOneTimeAccessEmailHandler) + + group.DELETE("/users/:id/profile-picture", authMiddleware.Add(), uc.resetUserProfilePictureHandler) + group.DELETE("/users/me/profile-picture", authMiddleware.WithAdminNotRequired().Add(), uc.resetCurrentUserProfilePictureHandler) } type UserController struct { @@ -480,3 +483,40 @@ func (uc *UserController) updateUser(c *gin.Context, updateOwnUser bool) { c.JSON(http.StatusOK, userDto) } + +// resetUserProfilePictureHandler godoc +// @Summary Reset user profile picture +// @Description Reset a specific user's profile picture to the default +// @Tags Users +// @Produce json +// @Param id path string true "User ID" +// @Success 204 "No Content" +// @Router /users/{id}/profile-picture [delete] +func (uc *UserController) resetUserProfilePictureHandler(c *gin.Context) { + userID := c.Param("id") + + if err := uc.userService.ResetProfilePicture(userID); err != nil { + c.Error(err) + return + } + + c.Status(http.StatusNoContent) +} + +// resetCurrentUserProfilePictureHandler godoc +// @Summary Reset current user's profile picture +// @Description Reset the currently authenticated user's profile picture to the default +// @Tags Users +// @Produce json +// @Success 204 "No Content" +// @Router /users/me/profile-picture [delete] +func (uc *UserController) resetCurrentUserProfilePictureHandler(c *gin.Context) { + userID := c.GetString("userID") + + if err := uc.userService.ResetProfilePicture(userID); err != nil { + c.Error(err) + return + } + + c.Status(http.StatusNoContent) +} diff --git a/backend/internal/service/user_service.go b/backend/internal/service/user_service.go index 663b27b1..42507bba 100644 --- a/backend/internal/service/user_service.go +++ b/backend/internal/service/user_service.go @@ -365,3 +365,27 @@ func (s *UserService) checkDuplicatedFields(user model.User) error { return nil } + +// ResetProfilePicture deletes a user's custom profile picture +func (s *UserService) ResetProfilePicture(userID string) error { + // Validate the user ID to prevent directory traversal + if err := uuid.Validate(userID); err != nil { + return &common.InvalidUUIDError{} + } + + // Build path to profile picture + profilePicturePath := fmt.Sprintf("%s/profile-pictures/%s.png", common.EnvConfig.UploadPath, userID) + + // Check if file exists and delete it + if _, err := os.Stat(profilePicturePath); err == nil { + if err := os.Remove(profilePicturePath); err != nil { + return fmt.Errorf("failed to delete profile picture: %w", err) + } + } else if !os.IsNotExist(err) { + // If any error other than "file not exists" + return fmt.Errorf("failed to check if profile picture exists: %w", err) + } + // It's okay if the file doesn't exist - just means there's no custom picture to delete + + return nil +} diff --git a/frontend/src/lib/components/form/profile-picture-settings.svelte b/frontend/src/lib/components/form/profile-picture-settings.svelte index 6631011c..c49e6f74 100644 --- a/frontend/src/lib/components/form/profile-picture-settings.svelte +++ b/frontend/src/lib/components/form/profile-picture-settings.svelte @@ -1,20 +1,23 @@
The image should be in PNG or JPEG format.
{/if} +