package auth import ( "RenewCMS/api/pkg/apperr" "RenewCMS/api/pkg/claims" validator "RenewCMS/api/pkg/validator" "RenewCMS/internal/domain/user" "RenewCMS/internal/infrastructure/useCases" "encoding/json" "errors" "net/http" ) type EmailChangeHandler struct { RequestUseCase *useCases.RequestEmailChangeUseCase ResendUseCase *useCases.ResendEmailChangeUseCase CancelUseCase *useCases.CancelEmailChangeUseCase ConfirmUseCase *useCases.ConfirmEmailChangeUseCase } type RequestEmailChangeCredentials struct { NewEmail string `json:"new_email" validate:"required,max=255,inviteemail"` CurrentPassword string `json:"current_password" validate:"required"` } type ConfirmEmailChangeCredentials struct { Code string `json:"code" validate:"required"` } func NewEmailChangeHandler(request *useCases.RequestEmailChangeUseCase, resend *useCases.ResendEmailChangeUseCase, cancel *useCases.CancelEmailChangeUseCase, confirm *useCases.ConfirmEmailChangeUseCase) *EmailChangeHandler { return &EmailChangeHandler{ RequestUseCase: request, ResendUseCase: resend, CancelUseCase: cancel, ConfirmUseCase: confirm, } } func (h *EmailChangeHandler) Request(w http.ResponseWriter, r *http.Request) { var credentials RequestEmailChangeCredentials if err := json.NewDecoder(r.Body).Decode(&credentials); err != nil { http.Error(w, apperr.BodyErrorMessage, http.StatusBadRequest) return } if err := validator.Validate.Struct(credentials); err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } userId, ok := claims.UserId(r) if !ok { http.Error(w, apperr.UnauthorizedErrorMessage, http.StatusUnauthorized) return } pending, err := h.RequestUseCase.RequestEmailChange(r.Context(), useCases.RequestEmailChangeCommand{ UserID: userId, NewEmail: credentials.NewEmail, CurrentPassword: credentials.CurrentPassword, }) if err != nil { writeEmailChangeError(w, err) return } writePendingEmail(w, pending) } func (h *EmailChangeHandler) Resend(w http.ResponseWriter, r *http.Request) { userId, ok := claims.UserId(r) if !ok { http.Error(w, apperr.UnauthorizedErrorMessage, http.StatusUnauthorized) return } pending, err := h.ResendUseCase.ResendEmailChange(r.Context(), userId) if err != nil { writeEmailChangeError(w, err) return } writePendingEmail(w, pending) } func (h *EmailChangeHandler) Cancel(w http.ResponseWriter, r *http.Request) { userId, ok := claims.UserId(r) if !ok { http.Error(w, apperr.UnauthorizedErrorMessage, http.StatusUnauthorized) return } if err := h.CancelUseCase.CancelEmailChange(r.Context(), userId); err != nil { writeEmailChangeError(w, err) return } w.WriteHeader(http.StatusNoContent) } func (h *EmailChangeHandler) Confirm(w http.ResponseWriter, r *http.Request) { var credentials ConfirmEmailChangeCredentials if err := json.NewDecoder(r.Body).Decode(&credentials); err != nil { http.Error(w, apperr.BodyErrorMessage, http.StatusBadRequest) return } if err := validator.Validate.Struct(credentials); err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } userId, ok := claims.UserId(r) if !ok { http.Error(w, apperr.UnauthorizedErrorMessage, http.StatusUnauthorized) return } email, err := h.ConfirmUseCase.ConfirmEmailChange(r.Context(), userId, credentials.Code) if err != nil { writeEmailChangeError(w, err) return } body, err := json.Marshal(map[string]string{"email": email}) if err != nil { http.Error(w, "Internal server error", http.StatusInternalServerError) return } _, _ = w.Write(body) } func writePendingEmail(w http.ResponseWriter, pending string) { body, err := json.Marshal(map[string]string{"pending_email": pending}) if err != nil { http.Error(w, "Internal server error", http.StatusInternalServerError) return } _, _ = w.Write(body) } func writeEmailChangeError(w http.ResponseWriter, err error) { switch { case errors.Is(err, user.ErrUserNotFound): http.Error(w, apperr.UnauthorizedErrorMessage, http.StatusUnauthorized) case errors.Is(err, user.ErrWrongPassword): http.Error(w, apperr.WrongPasswordErrorCode, http.StatusForbidden) case errors.Is(err, user.ErrSameEmail): http.Error(w, apperr.SameEmailErrorCode, http.StatusBadRequest) case errors.Is(err, user.ErrNoPendingEmailChange): http.Error(w, apperr.NoPendingEmailChangeErrorCode, http.StatusNotFound) case errors.Is(err, user.ErrInvalidEmailChangeCode): http.Error(w, apperr.InvalidEmailChangeCodeErrorCode, http.StatusForbidden) case errors.Is(err, user.ErrEmailUnavailable): http.Error(w, apperr.EmailUnavailableErrorCode, http.StatusConflict) default: http.Error(w, "Internal server error", http.StatusInternalServerError) } }