Some checks failed
Deploy Production / Deploy to Staging (push) Has been skipped
Go Linter / Run golangci-lint (api_gateway) (push) Failing after 2m31s
Go Linter / Build golang services (api_gateway) (push) Has been skipped
Go Linter / Tag Commit (push) Has been skipped
Go Linter / Push Docker Images (api_gateway) (push) Has been skipped
644 lines
21 KiB
Go
644 lines
21 KiB
Go
package http_router
|
||
|
||
import (
|
||
"context"
|
||
"encoding/json"
|
||
"fmt"
|
||
"html/template"
|
||
"io"
|
||
"log/slog"
|
||
"net/http"
|
||
"net/url"
|
||
|
||
"git-molva.ru/Molva/molva-backend/services/api_gateway/internal/auth"
|
||
"git-molva.ru/Molva/molva-backend/services/api_gateway/internal/auth/keycloak"
|
||
"git-molva.ru/Molva/molva-backend/services/api_gateway/internal/broker"
|
||
"git-molva.ru/Molva/molva-backend/services/api_gateway/internal/constants"
|
||
dbtypes "git-molva.ru/Molva/molva-backend/services/api_gateway/internal/database/types"
|
||
"git-molva.ru/Molva/molva-backend/services/api_gateway/internal/feed"
|
||
notification "git-molva.ru/Molva/molva-backend/services/api_gateway/internal/notifications/git-molva.ru/Molva/molva-notification-service"
|
||
rmodel "git-molva.ru/Molva/molva-backend/services/api_gateway/internal/request_model"
|
||
authinfra "git-molva.ru/Molva/molva-backend/services/api_gateway/internal/services/auth_infrastructure"
|
||
"google.golang.org/protobuf/proto"
|
||
)
|
||
|
||
// ------------------------------
|
||
// LOGIN USER
|
||
// ------------------------------
|
||
|
||
// @Summary Вход пользователя
|
||
// @Description Аутентификация пользователя по email и паролю
|
||
// @Tags auth
|
||
// @Accept json
|
||
// @Produce json
|
||
// @Param request body rmodel.LoginUserRequest true "Данные для входа"
|
||
// @Success 200 {object} rmodel.LoginUserResponse "Успешная аутентификация"
|
||
// @Failure 400 {object} map[string]string "Неверные данные запроса"
|
||
// @Failure 401 {object} map[string]string "Неверные учетные данные"
|
||
// @Failure 500 {object} map[string]string "Внутренняя ошибка сервера"
|
||
// @Router /api/v1/login [post]
|
||
func (h *handler) loginHandler(w http.ResponseWriter, r *http.Request) {
|
||
const handlerName = "loginHandler"
|
||
|
||
var request rmodel.LoginUserRequest
|
||
|
||
if err := json.NewDecoder(r.Body).Decode(&request); err != nil {
|
||
http.Error(w, constants.ErrBadRequest.Error(), http.StatusBadRequest)
|
||
h.logger.Error("Invalid request body",
|
||
slog.String("error", err.Error()),
|
||
slog.String("handler", handlerName))
|
||
|
||
return
|
||
}
|
||
|
||
defer func(body io.ReadCloser) {
|
||
if err := body.Close(); err != nil {
|
||
http.Error(w, constants.ErrInternalServerError.Error(), http.StatusInternalServerError)
|
||
h.logger.Error("error closing body",
|
||
slog.String("error", err.Error()),
|
||
slog.String("handler", handlerName))
|
||
}
|
||
}(r.Body)
|
||
|
||
tokens, err := h.authManager.LoginUser(r.Context(), auth.LoginUserRequest{
|
||
Email: request.Email,
|
||
Password: request.Password,
|
||
})
|
||
if err != nil {
|
||
h.handleKeycloakError(w, err)
|
||
h.logger.Error("error while login user",
|
||
slog.String("error", err.Error()),
|
||
slog.String("handler", handlerName))
|
||
|
||
return
|
||
}
|
||
|
||
userInfo, err := h.authManager.GetUserInfo(r.Context(), tokens.UserId)
|
||
if err != nil {
|
||
h.handleKeycloakError(w, err)
|
||
h.logger.Error("error while getting user info",
|
||
slog.String("error", err.Error()),
|
||
slog.String("handler", handlerName))
|
||
|
||
return
|
||
}
|
||
|
||
emailVerificationStatus, err := h.authManager.GetEmailVerificationStatus(r.Context(), tokens.UserId)
|
||
if err != nil {
|
||
h.handleKeycloakError(w, err)
|
||
h.logger.Error("error while getting email verification status",
|
||
slog.String("error", err.Error()),
|
||
slog.String("handler", handlerName))
|
||
|
||
return
|
||
}
|
||
|
||
w.Header().Set("Content-Type", "application/json")
|
||
|
||
if err = json.NewEncoder(w).Encode(rmodel.LoginUserResponse{
|
||
Uid: tokens.UserId,
|
||
AccessToken: tokens.AccessToken,
|
||
RefreshToken: tokens.RefreshToken,
|
||
UserType: userInfo.UserType,
|
||
Permissions: rmodel.Permissions{
|
||
Balance: userInfo.Permissions[keycloak.PermissionBalance],
|
||
Company: userInfo.Permissions[keycloak.PermissionCompany],
|
||
Employees: userInfo.Permissions[keycloak.PermissionEmployees],
|
||
Profile: userInfo.Permissions[keycloak.PermissionProfile],
|
||
Submissions: userInfo.Permissions[keycloak.PermissionSubmissions],
|
||
Vacancies: userInfo.Permissions[keycloak.PermissionVacancies],
|
||
},
|
||
EmailVerified: emailVerificationStatus.EmailVerified,
|
||
}); err != nil {
|
||
http.Error(w, constants.ErrInternalServerError.Error(), http.StatusInternalServerError)
|
||
h.logger.Error("error while encoding response: ",
|
||
slog.String("error", err.Error()),
|
||
slog.String("handler", handlerName))
|
||
}
|
||
}
|
||
|
||
// ------------------------------
|
||
// REGISTER USER
|
||
// ------------------------------
|
||
|
||
// @Summary Регистрация пользователя
|
||
// @Description Создание нового пользователя в системе
|
||
// @Tags auth
|
||
// @Accept json
|
||
// @Produce json
|
||
// @Param request body rmodel.UserCredentials true "Данные для регистрации"
|
||
// @Success 201 {object} rmodel.RegisterResponse "Пользователь успешно создан"
|
||
// @Failure 400 {object} map[string]string "Неверные данные запроса"
|
||
// @Failure 409 {object} map[string]string "Пользователь уже существует"
|
||
// @Failure 500 {object} map[string]string "Внутренняя ошибка сервера"
|
||
// @Router /api/v1/register [post]
|
||
func (h *handler) registerHandler(w http.ResponseWriter, r *http.Request) {
|
||
const handlerName = "registerHandler"
|
||
|
||
var creds rmodel.UserCredentials
|
||
|
||
if err := json.NewDecoder(r.Body).Decode(&creds); err != nil {
|
||
http.Error(w, constants.ErrBadRequest.Error(), http.StatusBadRequest)
|
||
h.logger.Error("error decoding request body",
|
||
slog.String("error", err.Error()),
|
||
slog.String("handler", handlerName))
|
||
|
||
return
|
||
}
|
||
|
||
defer r.Body.Close()
|
||
|
||
if h.env == "production" {
|
||
isEmailValid, err := h.validateEmail(creds.Email)
|
||
if err != nil {
|
||
h.logger.Warn("EmailVerificationService API service error, proceeding with email validation via message",
|
||
slog.String("error", err.Error()),
|
||
slog.String("email", creds.Email),
|
||
slog.String("handler", handlerName))
|
||
} else if !isEmailValid {
|
||
h.logger.Warn("email validation failed - invalid email address",
|
||
slog.String("email", creds.Email),
|
||
slog.String("handler", handlerName))
|
||
http.Error(w, "Invalid email address", http.StatusBadRequest)
|
||
|
||
return
|
||
}
|
||
}
|
||
|
||
uid, err := h.authManager.RegisterUser(r.Context(), auth.RegisterUserRequest{
|
||
User: auth.User{
|
||
Email: creds.Email,
|
||
Password: creds.Password,
|
||
FirstName: creds.FirstName,
|
||
SecondName: creds.LastName,
|
||
Patronymic: creds.MiddleName,
|
||
Number: creds.PhoneNumber,
|
||
Permissions: creds.Permissions,
|
||
},
|
||
UserType: creds.UserType,
|
||
})
|
||
if err != nil {
|
||
h.logger.Error("error while saving user to keycloak",
|
||
slog.String("error", err.Error()),
|
||
slog.String("handler", handlerName))
|
||
h.handleKeycloakError(w, err)
|
||
|
||
return
|
||
}
|
||
|
||
if err := h.saveUser(r.Context(), uid.UserId, creds); err != nil {
|
||
h.handleDBError(w, err)
|
||
h.logger.Error("error saving user to DB",
|
||
slog.String("error", err.Error()),
|
||
slog.String("handler", handlerName))
|
||
|
||
return
|
||
}
|
||
|
||
tokenResp, err := h.authManager.GetUserEmailVerificationToken(r.Context(), uid.UserId)
|
||
if err != nil {
|
||
h.logger.Error("error getting user token",
|
||
slog.String("error", err.Error()),
|
||
slog.String("handler", handlerName))
|
||
http.Error(w, constants.ErrInternalServerError.Error(), http.StatusInternalServerError)
|
||
|
||
return
|
||
}
|
||
|
||
userName := formatName(creds.LastName, creds.FirstName, creds.MiddleName)
|
||
|
||
if err := h.sendConfirmationEmail(r, creds.Email, userName, uid.UserId, tokenResp.AccessToken); err != nil {
|
||
h.logger.Error("error sending confirmation email",
|
||
slog.String("error", err.Error()),
|
||
slog.String("handler", handlerName))
|
||
http.Error(w, constants.ErrInternalServerError.Error(), http.StatusInternalServerError)
|
||
|
||
return
|
||
}
|
||
|
||
h.createWelcomeEvent(r.Context(), uid.UserId, creds, handlerName)
|
||
|
||
w.Header().Set("Content-Type", "application/json")
|
||
w.WriteHeader(http.StatusCreated)
|
||
|
||
if err := json.NewEncoder(w).Encode(&rmodel.RegisterResponse{UUID: uid.UserId}); err != nil {
|
||
http.Error(w, constants.ErrInternalServerError.Error(), http.StatusInternalServerError)
|
||
h.logger.Error("error while encoding response", slog.String("error", err.Error()),
|
||
slog.String("handler", handlerName))
|
||
}
|
||
}
|
||
|
||
func (h *handler) createWelcomeEvent(ctx context.Context, userID string, creds rmodel.UserCredentials, handlerName string) {
|
||
ownerType := feed.RoleAgent
|
||
if creds.UserType == constants.DistributorClientType {
|
||
ownerType = feed.RoleDistributor
|
||
}
|
||
|
||
event := feed.Event{
|
||
OwnerId: userID,
|
||
OwnerType: ownerType,
|
||
EventType: feed.EventWelcome,
|
||
Message: "Добро пожаловать в Molva!",
|
||
Visibility: feed.VisibilityPrivate,
|
||
Payload: feed.EventPayload{},
|
||
}
|
||
|
||
if err := h.feed.CreateEvent(ctx, &event); err != nil {
|
||
h.logger.Error("failed to create feed event",
|
||
slog.String("error", err.Error()),
|
||
slog.String("handler", handlerName),
|
||
slog.String("uid", userID))
|
||
}
|
||
}
|
||
|
||
func (h *handler) sendConfirmationEmail(r *http.Request, email, userName, userID, accessToken string) error {
|
||
scheme := "https"
|
||
if r.TLS == nil {
|
||
scheme = "http"
|
||
}
|
||
|
||
confirmURL := fmt.Sprintf("%s://%s/api/v1/confirm_email?uid=%s&token=%s",
|
||
scheme,
|
||
r.Host,
|
||
url.QueryEscape(userID),
|
||
url.QueryEscape(accessToken),
|
||
)
|
||
|
||
emailBody := fmt.Sprintf(constants.EmailVerificationTemplate,
|
||
userName,
|
||
confirmURL,
|
||
confirmURL,
|
||
)
|
||
|
||
msg, err := proto.Marshal(¬ification.SendEmailRequest{
|
||
To: []string{email},
|
||
Subject: constants.EmailVerificationMessageSubject,
|
||
ContentType: constants.HTMLNotificationContentType,
|
||
Body: []byte(emailBody),
|
||
})
|
||
if err != nil {
|
||
return fmt.Errorf("marshaling email message: %w", err)
|
||
}
|
||
|
||
if err := broker.SendNotification(
|
||
broker.NotificationQueue,
|
||
constants.EmailNotificationMessageType,
|
||
msg,
|
||
h.logger,
|
||
); err != nil {
|
||
return fmt.Errorf("sending notification: %w", err)
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
// NOTE: lastName and firstName are required to be non-empty
|
||
func formatName(lastName, firstName string, middleName *string) string {
|
||
name := fmt.Sprintf("%s %s", lastName, firstName)
|
||
|
||
if middleName != nil {
|
||
name = fmt.Sprintf("%s %s", name, *middleName)
|
||
}
|
||
|
||
return name
|
||
}
|
||
|
||
func (h *handler) saveUser(ctx context.Context, uid string, creds rmodel.UserCredentials) error {
|
||
userName := formatName(creds.LastName, creds.FirstName, creds.MiddleName)
|
||
|
||
if _, err := h.dbClient.CreateUser(ctx, &dbtypes.UserSaveRequest{
|
||
Id: uid,
|
||
FullName: userName,
|
||
Phone: creds.PhoneNumber,
|
||
Email: creds.Email,
|
||
Type: dbtypes.UserType(creds.UserType),
|
||
}); err != nil {
|
||
return err
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
// ------------------------------
|
||
// LOGOUT USER
|
||
// ------------------------------
|
||
|
||
// @Summary Выход пользователя
|
||
// @Description Завершение сессии пользователя
|
||
// @Tags auth
|
||
// @Accept json
|
||
// @Produce json
|
||
// @Param request body rmodel.LogoutUserRequest true "Токен для выхода"
|
||
// @Success 200 "Успешный выход"
|
||
// @Failure 400 {object} map[string]string "Неверные данные запроса"
|
||
// @Failure 401 {object} map[string]string "Неверный токен"
|
||
// @Failure 500 {object} map[string]string "Внутренняя ошибка сервера"
|
||
// @Router /api/v1/logout [post]
|
||
func (h *handler) logoutHandler(w http.ResponseWriter, r *http.Request) {
|
||
const handlerName = "logoutHandler"
|
||
|
||
var request rmodel.LogoutUserRequest
|
||
|
||
if err := json.NewDecoder(r.Body).Decode(&request); err != nil {
|
||
http.Error(w, constants.ErrInternalServerError.Error(), http.StatusBadRequest)
|
||
h.logger.Error("error while decoding request body: ",
|
||
slog.String("error", err.Error()),
|
||
slog.String("handler", handlerName))
|
||
|
||
return
|
||
}
|
||
|
||
if err := h.authManager.LogoutUser(r.Context(), auth.LogoutUserRequest{
|
||
RefreshToken: request.RefreshToken,
|
||
}); err != nil {
|
||
h.handleKeycloakError(w, err)
|
||
return
|
||
}
|
||
}
|
||
|
||
// ------------------------------
|
||
// REFRESH USER TOKEN
|
||
// ------------------------------
|
||
|
||
// @Summary Обновление токена доступа
|
||
// @Description Получение нового access token по refresh token
|
||
// @Tags auth
|
||
// @Accept json
|
||
// @Produce json
|
||
// @Param request body rmodel.RefreshTokenRequest true "Refresh token"
|
||
// @Success 200 {object} rmodel.RefreshTokenResponse "Новые токены"
|
||
// @Failure 400 {object} map[string]string "Неверные данные запроса"
|
||
// @Failure 401 {object} map[string]string "Неверный refresh token"
|
||
// @Failure 500 {object} map[string]string "Внутренняя ошибка сервера"
|
||
// @Router /api/v1/refresh-token [post]
|
||
func (h *handler) refreshTokenHandler(w http.ResponseWriter, r *http.Request) {
|
||
const handlerName = "refreshTokenHandler"
|
||
|
||
var request rmodel.RefreshTokenRequest
|
||
|
||
if err := json.NewDecoder(r.Body).Decode(&request); err != nil {
|
||
http.Error(w, constants.ErrInternalServerError.Error(), http.StatusBadRequest)
|
||
h.logger.Error("error while decoding request body: ",
|
||
slog.String("error", err.Error()),
|
||
slog.String("handler", handlerName))
|
||
|
||
return
|
||
}
|
||
|
||
resp, err := h.authManager.GetNewAccessToken(r.Context(), auth.GetNewAccessTokenRequest{
|
||
RefreshToken: request.RefreshToken,
|
||
})
|
||
if err != nil {
|
||
h.handleKeycloakError(w, err)
|
||
return
|
||
}
|
||
|
||
if err := json.NewEncoder(w).Encode(rmodel.RefreshTokenResponse{
|
||
AccessToken: resp.AccessToken,
|
||
RefreshToken: resp.RefreshToken,
|
||
}); err != nil {
|
||
http.Error(w, constants.ErrInternalServerError.Error(), http.StatusInternalServerError)
|
||
h.logger.Error("error while encoding response: ",
|
||
slog.String("error", err.Error()),
|
||
slog.String("handler", handlerName))
|
||
|
||
return
|
||
}
|
||
}
|
||
|
||
// @Summary Страница подтверждения email
|
||
// @Description HTML страница для подтверждения email адреса пользователя
|
||
// @Tags auth
|
||
// @Accept html
|
||
// @Produce html
|
||
// @Param uid query string true "ID пользователя"
|
||
// @Param token query string true "Токен подтверждения"
|
||
// @Success 200 {string} string "HTML страница подтверждения"
|
||
// @Failure 400 {object} map[string]string "Неверные параметры запроса"
|
||
// @Failure 500 {object} map[string]string "Внутренняя ошибка сервера"
|
||
// @Router /api/v1/confirm_email [get]
|
||
func (h *handler) confirmEmailPageHandler(w http.ResponseWriter, r *http.Request) {
|
||
const handlerName = "confirmEmailPageHandler"
|
||
|
||
if r.Method != http.MethodGet {
|
||
http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed)
|
||
return
|
||
}
|
||
|
||
uid := r.URL.Query().Get("uid")
|
||
token := r.URL.Query().Get("token")
|
||
|
||
if uid == "" || token == "" {
|
||
http.Error(w, "Bad Request: отсутствует uid или token", http.StatusBadRequest)
|
||
return
|
||
}
|
||
|
||
scheme := "http"
|
||
|
||
if protocol := r.Header.Get("X-Forwarded-Proto"); protocol != "" {
|
||
scheme = protocol
|
||
} else if h.env == "production" || h.env == "development" {
|
||
scheme = "https"
|
||
}
|
||
|
||
// Формируем URL для PATCH-запроса
|
||
verificationLink := fmt.Sprintf("%s://%s/api/v1/verify_email?uid=%s&token=%s",
|
||
scheme,
|
||
r.Host,
|
||
url.QueryEscape(uid),
|
||
url.QueryEscape(token),
|
||
)
|
||
|
||
tmpl, err := template.New("confirm").Parse(constants.EmailConfirmationPage)
|
||
if err != nil {
|
||
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
|
||
return
|
||
}
|
||
|
||
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||
|
||
if err := tmpl.Execute(w, struct {
|
||
VerificationURL string
|
||
}{
|
||
VerificationURL: verificationLink,
|
||
}); err != nil {
|
||
http.Error(w, constants.ErrInternalServerError.Error(), http.StatusInternalServerError)
|
||
h.logger.Error("error while executing confirm email page template",
|
||
slog.String("error", err.Error()),
|
||
slog.String("handler", handlerName))
|
||
}
|
||
}
|
||
|
||
// ------------------------------
|
||
// RESET PASSWORD
|
||
// ------------------------------
|
||
|
||
// @Summary Запрос восстановления пароля
|
||
// @Description Отправка OTP кода на email для восстановления пароля
|
||
// @Tags auth
|
||
// @Accept json
|
||
// @Produce json
|
||
// @Param request body rmodel.ForgotPasswordRequest true "Данные для восстановления пароля"
|
||
// @Success 200 {object} map[string]string "OTP код отправлен"
|
||
// @Failure 400 {object} map[string]string "Неверные данные запроса"
|
||
// @Failure 500 {object} map[string]string "Внутренняя ошибка сервера"
|
||
// @Router /api/v1/forgot_password [post]
|
||
func (h *handler) forgotPasswordHandler(w http.ResponseWriter, r *http.Request) {
|
||
const handlerName = "forgotPasswordHandler"
|
||
|
||
var request rmodel.ForgotPasswordRequest
|
||
|
||
if err := json.NewDecoder(r.Body).Decode(&request); err != nil {
|
||
http.Error(w, constants.ErrBadRequest.Error(), http.StatusBadRequest)
|
||
h.logger.Error("error while decoding request body: ",
|
||
slog.String("error", err.Error()),
|
||
slog.String("handler", handlerName))
|
||
|
||
return
|
||
}
|
||
|
||
createOTPResp, err := h.authInfraService.CreatePasswordResetOTP(r.Context(), &authinfra.PasswordResetOTPCreateRequest{
|
||
Email: request.Email,
|
||
})
|
||
if err != nil {
|
||
h.handleAuthInfraError(w, err, handlerName)
|
||
return
|
||
}
|
||
|
||
message, err := proto.Marshal(¬ification.SendEmailRequest{
|
||
To: []string{request.Email},
|
||
Subject: constants.ForgotPasswordNotificationMessageSubject,
|
||
ContentType: constants.TextNotificationContentType,
|
||
Body: fmt.Appendf(nil,
|
||
constants.ForgotPasswordNotificationText,
|
||
createOTPResp.OTP,
|
||
),
|
||
})
|
||
if err != nil {
|
||
http.Error(w, constants.ErrInternalServerError.Error(), http.StatusInternalServerError)
|
||
h.logger.Error("error while marshaling forgot password notification message",
|
||
slog.String("error", err.Error()),
|
||
slog.String("handler", handlerName))
|
||
|
||
return
|
||
}
|
||
|
||
if err := broker.SendNotification(
|
||
broker.NotificationQueue,
|
||
constants.EmailNotificationMessageType,
|
||
message,
|
||
h.logger,
|
||
); err != nil {
|
||
http.Error(w, constants.ErrInternalServerError.Error(), http.StatusInternalServerError)
|
||
h.logger.Error("error while sending forgot password notification",
|
||
slog.String("error", err.Error()),
|
||
slog.String("handler", handlerName))
|
||
|
||
return
|
||
}
|
||
|
||
w.WriteHeader(http.StatusCreated)
|
||
}
|
||
|
||
// @Summary Валидация OTP кода
|
||
// @Description Проверка OTP кода для восстановления пароля
|
||
// @Tags auth
|
||
// @Accept json
|
||
// @Produce json
|
||
// @Param otp query string true "OTP код"
|
||
// @Param email query string true "Email пользователя"
|
||
// @Success 200 {object} map[string]string "OTP код валиден"
|
||
// @Failure 400 {object} map[string]string "Неверные параметры запроса"
|
||
// @Failure 500 {object} map[string]string "Внутренняя ошибка сервера"
|
||
// @Router /api/v1/validate_otp [get]
|
||
func (h *handler) validateOTPHandler(w http.ResponseWriter, r *http.Request) {
|
||
const handlerName = "validateOTPHandler"
|
||
|
||
query := r.URL.Query()
|
||
|
||
email := query.Get("email")
|
||
if email == "" {
|
||
http.Error(w, constants.ErrBadRequest.Error(), http.StatusBadRequest)
|
||
h.logger.Debug("email is required, but missing",
|
||
slog.String("handler", handlerName))
|
||
|
||
return
|
||
}
|
||
|
||
otp := query.Get("otp")
|
||
if otp == "" {
|
||
http.Error(w, constants.ErrBadRequest.Error(), http.StatusBadRequest)
|
||
h.logger.Debug("OTP is required, but missing",
|
||
slog.String("handler", handlerName))
|
||
|
||
return
|
||
}
|
||
|
||
checkOTPResp, err := h.authInfraService.ValidatePasswordResetOTP(r.Context(), &authinfra.ValidatePasswordResetOTPRequest{
|
||
Email: email,
|
||
OTP: otp,
|
||
})
|
||
if err != nil {
|
||
h.handleAuthInfraError(w, err, handlerName)
|
||
return
|
||
}
|
||
|
||
w.Header().Set("Content-Type", "application/json")
|
||
|
||
if err := json.NewEncoder(w).Encode(rmodel.ValidateOTPResponse{
|
||
Token: checkOTPResp.Token,
|
||
}); err != nil {
|
||
http.Error(w, constants.ErrInternalServerError.Error(), http.StatusInternalServerError)
|
||
h.logger.Error("error encoding response: ",
|
||
slog.String("error", err.Error()),
|
||
slog.String("handler", handlerName))
|
||
}
|
||
}
|
||
|
||
// @Summary Сброс пароля
|
||
// @Description Установка нового пароля после валидации OTP кода
|
||
// @Tags auth
|
||
// @Accept json
|
||
// @Produce json
|
||
// @Param request body rmodel.ResetPasswordRequest true "Данные для сброса пароля"
|
||
// @Success 200 {object} map[string]string "Пароль успешно изменен"
|
||
// @Failure 400 {object} map[string]string "Неверные данные запроса"
|
||
// @Failure 500 {object} map[string]string "Внутренняя ошибка сервера"
|
||
// @Router /api/v1/reset_password [put]
|
||
func (h *handler) resetPasswordHandler(w http.ResponseWriter, r *http.Request) {
|
||
const handlerName = "resetPasswordHandler"
|
||
|
||
var request rmodel.ResetPasswordRequest
|
||
|
||
if err := json.NewDecoder(r.Body).Decode(&request); err != nil {
|
||
http.Error(w, constants.ErrBadRequest.Error(), http.StatusBadRequest)
|
||
h.logger.Error("error decoding request body: ",
|
||
slog.String("error", err.Error()),
|
||
slog.String("handler", handlerName))
|
||
|
||
return
|
||
}
|
||
|
||
if _, err := h.authInfraService.ValidatePasswordResetToken(r.Context(), &authinfra.ValidatePasswordResetTokenRequest{
|
||
Email: request.Email,
|
||
Token: request.Token,
|
||
}); err != nil {
|
||
h.handleAuthInfraError(w, err, handlerName)
|
||
return
|
||
}
|
||
|
||
if err := h.authManager.ResetPassword(r.Context(), auth.ResetPasswordRequest{
|
||
Email: request.Email,
|
||
NewPassword: request.Password,
|
||
}); err != nil {
|
||
h.handleKeycloakError(w, err)
|
||
h.logger.Error("error resetting password",
|
||
slog.String("error", err.Error()),
|
||
slog.String("handler", handlerName))
|
||
|
||
return
|
||
}
|
||
|
||
w.WriteHeader(http.StatusNoContent)
|
||
}
|