feat: change user password

This commit is contained in:
2026-08-29 17:23:25 +02:00
parent 67f5d43a58
commit dc71ff4332
5 changed files with 97 additions and 9 deletions
+49 -2
View File
@@ -11,10 +11,11 @@ import (
type UserHandler struct {
UserService *domain.UserService
AuthService *domain.AuthService
}
func NewUserHandler(userService *domain.UserService) *UserHandler {
return &UserHandler{UserService: userService}
func NewUserHandler(userService *domain.UserService, authService *domain.AuthService) *UserHandler {
return &UserHandler{UserService: userService, AuthService: authService}
}
// CreateUser handles the creation of a user
@@ -49,3 +50,49 @@ func (h *UserHandler) CreateUser(w http.ResponseWriter, r *http.Request) {
slog.InfoContext(ctx, "created user successfully", slog.Any("user_id", user.ID))
response.JSON(ctx, w, http.StatusCreated, user)
}
// ChangePassword handles changing a users password
//
// @Summary Change password route
// @Description Change password for the authenticated user
// @Tags User
// @Accept json
// @Produce json
// @Param payload body request.ChangePasswordPayload true "Change password payload"
// @Success 204
// @Error 400 {object} response.ErrorResponse "failed to decode request body"
// @Error 500 {object} response.ErrorResponse "could not get auth context"
// @Error 500 {object} response.ErrorResponse "failed to change password"
// @Router /users/password [post]
func (h *UserHandler) ChangePassword(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
payload, err := request.DecodeJSON[request.ChangePasswordPayload](r)
if err != nil {
slog.ErrorContext(ctx, "failed to decode change password payload", slog.Any("error", err))
response.Error(ctx, w, http.StatusBadRequest, "failed to decode request body")
return
}
authContext, err := domain.GetAuthContext(ctx)
if err != nil {
slog.ErrorContext(ctx, "failed to get auth context", slog.Any("error", err))
response.Error(ctx, w, http.StatusInternalServerError, "could not get auth context")
return
}
err = h.UserService.ChangePassword(ctx, authContext.UserID, payload.Password)
if err != nil {
slog.ErrorContext(ctx, "failed to change password", slog.Any("error", err))
response.Error(ctx, w, http.StatusInternalServerError, "failed to change password")
return
}
err = h.AuthService.LogoutAllDevices(ctx, authContext.UserID)
if err != nil {
slog.WarnContext(ctx, "failed to logout user from all devices after password change", slog.Any("error", err))
}
slog.InfoContext(ctx, "password changed successfully", slog.Any("user_id", authContext.UserID))
response.Status(ctx, w, http.StatusNoContent)
}
+4
View File
@@ -5,3 +5,7 @@ type CreateUserPayload struct {
Name string `json:"name"`
Password string `json:"password"`
}
type ChangePasswordPayload struct {
Password string `json:"password"`
}
+5 -4
View File
@@ -16,14 +16,14 @@ type Config struct {
}
func NewMux(cfg Config) http.Handler {
userRepo := repository.NewUserRepo(cfg.Database)
userService := domain.NewUserService(userRepo)
userHandler := handler.NewUserHandler(userService)
authRepo := repository.NewAuthRepo(cfg.Database)
authService := domain.NewAuthService(authRepo, []byte(cfg.JWTSecret))
authHandler := handler.NewAuthHandler(authService)
userRepo := repository.NewUserRepo(cfg.Database)
userService := domain.NewUserService(userRepo)
userHandler := handler.NewUserHandler(userService, authService)
listRepo := repository.NewListRepo(cfg.Database)
listService := domain.NewListService(listRepo)
listHandler := handler.NewListHandler(listService, userService)
@@ -40,6 +40,7 @@ func NewMux(cfg Config) http.Handler {
apiMux.HandleFunc("POST /logout/all", authHandler.LogoutAllDevices)
apiMux.Handle("POST /users", protected(userHandler.CreateUser))
apiMux.Handle("POST /users/password", protected(userHandler.ChangePassword))
apiMux.Handle("POST /lists", protected(listHandler.CreateList))
apiMux.Handle("DELETE /lists/{id}", protected(listHandler.DeleteList))