package user import ( "RenewCMS/api/pkg/apperr" "RenewCMS/api/pkg/authtoken" "RenewCMS/api/pkg/claims" validator "RenewCMS/api/pkg/validator" domain "RenewCMS/internal/domain/user" "RenewCMS/internal/infrastructure/useCases" "encoding/json" "errors" "net/http" "time" ) type DeleteAccountHandler struct { DeleteUseCase *useCases.DeleteOwnAccountUseCase IsProduction bool } type DeleteAccountRequest struct { CurrentPassword string `json:"current_password" validate:"required"` Confirmation string `json:"confirmation" validate:"required,eq=DELETE"` } func NewDeleteAccountHandler(deleteAccount *useCases.DeleteOwnAccountUseCase, isProduction bool) *DeleteAccountHandler { return &DeleteAccountHandler{DeleteUseCase: deleteAccount, IsProduction: isProduction} } func (h *DeleteAccountHandler) Delete(w http.ResponseWriter, r *http.Request) { var request DeleteAccountRequest if err := json.NewDecoder(r.Body).Decode(&request); err != nil { http.Error(w, apperr.BodyErrorMessage, http.StatusBadRequest) return } if err := validator.Validate.Struct(request); 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 } command := useCases.DeleteOwnAccountCommand{UserID: userId, CurrentPassword: request.CurrentPassword} command.SessionID, command.SessionExpiresAt = sessionOf(r) if err := h.DeleteUseCase.Delete(r.Context(), command); err != nil { writeDeleteAccountError(w, err) return } http.SetCookie(w, authtoken.ExpiredCookie(h.IsProduction)) w.WriteHeader(http.StatusNoContent) } func sessionOf(r *http.Request) (string, time.Time) { tokenClaims, err := authtoken.FromContext(r.Context()) if err != nil { return "", time.Time{} } id, _ := tokenClaims["jti"].(string) expiry, _ := tokenClaims["exp"].(float64) return id, time.Unix(int64(expiry), 0) } func writeDeleteAccountError(w http.ResponseWriter, err error) { var lastAdmin domain.LastAdminError switch { case errors.Is(err, domain.ErrUserNotFound): http.Error(w, apperr.UnauthorizedErrorMessage, http.StatusUnauthorized) case errors.Is(err, domain.ErrWrongPassword): http.Error(w, apperr.WrongPasswordErrorCode, http.StatusForbidden) case errors.As(err, &lastAdmin): w.Header().Set(apperr.LastRoleHeader, lastAdmin.Role) http.Error(w, apperr.LastAdminErrorCode, http.StatusConflict) case errors.Is(err, domain.ErrAccountNotDeletable): http.Error(w, apperr.AccountNotDeletableErrorCode, http.StatusConflict) default: http.Error(w, "Internal server error", http.StatusInternalServerError) } }