Files
tp/main_dc/yalarba/api_yal/internal/domain/account/handler.go
T
valitovgaziz 75b2f3f6b2 On branch main
modified:   main_dc/yalarba/api_yal/internal/domain/account/dto.go
	new file:   main_dc/yalarba/api_yal/internal/domain/account/errors.go
	modified:   main_dc/yalarba/api_yal/internal/domain/account/handler.go
	modified:   main_dc/yalarba/api_yal/internal/domain/account/router.go
	modified:   main_dc/yalarba/api_yal/internal/domain/account/service.go
	new file:   main_dc/yalarba/api_yal/internal/domain/account/types.go
	new file:   main_dc/yalarba/api_yal/internal/middleware/admin.go
	modified:   main_dc/yalarba/api_yal/internal/middleware/auth.go
	new file:   main_dc/yalarba/api_yal/internal/middleware/context.go
	new file:   main_dc/yalarba/api_yal/internal/middleware/logging.go
	modified:   main_dc/yalarba/api_yal/internal/router/router.go
last but not yet commit
2026-03-31 09:43:18 +05:00

447 lines
12 KiB
Go

package account
import (
"encoding/json"
"errors"
"fmt"
"net/http"
"strconv"
"api_yal/internal/logger"
"api_yal/internal/middleware"
"github.com/go-playground/validator/v10"
"go.uber.org/zap"
)
// Handler обработчик для операций с аккаунтами
type Handler struct {
service Service
validator *validator.Validate
}
// NewHandler создает новый экземпляр Handler
func NewHandler(service Service) *Handler {
return &Handler{
service: service,
validator: validator.New(),
}
}
// GetAccountByID получение аккаунта по ID
func (h *Handler) GetAccountByID(w http.ResponseWriter, r *http.Request) {
l := logger.Get()
// Получаем ID из контекста (для своего профиля)
userID, ok := r.Context().Value(middleware.UserIDKey).(uint)
if !ok {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
// Проверяем, что пользователь запрашивает свой профиль
// В реальном приложении админы могут просматривать чужие профили
account, err := h.service.GetAccountByID(userID)
if err != nil {
l.Error("Ошибка получения аккаунта", zap.Error(err))
if errors.Is(err, ErrAccountNotFound) {
http.Error(w, "Account not found", http.StatusNotFound)
return
}
http.Error(w, "Failed to get account", http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(account)
}
// GetAccountProfile получение профиля пользователя
func (h *Handler) GetAccountProfile(w http.ResponseWriter, r *http.Request) {
l := logger.Get()
userID, ok := r.Context().Value(middleware.UserIDKey).(uint)
if !ok {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
profile, err := h.service.GetAccountProfile(userID)
if err != nil {
l.Error("Ошибка получения профиля", zap.Error(err))
if errors.Is(err, ErrAccountNotFound) {
http.Error(w, "Account not found", http.StatusNotFound)
return
}
http.Error(w, "Failed to get profile", http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(profile)
}
// UpdateAccount обновление аккаунта
func (h *Handler) UpdateAccount(w http.ResponseWriter, r *http.Request) {
l := logger.Get()
userID, ok := r.Context().Value(middleware.UserIDKey).(uint)
if !ok {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
var req UpdateAccountRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, "Invalid request body", http.StatusBadRequest)
return
}
// Валидация не требуется для всех полей, так как они опциональны
account, err := h.service.UpdateAccount(userID, req)
if err != nil {
l.Error("Ошибка обновления аккаунта", zap.Error(err))
if errors.Is(err, ErrAccountNotFound) {
http.Error(w, "Account not found", http.StatusNotFound)
return
}
http.Error(w, "Failed to update account", http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(account)
}
// ChangePassword смена пароля
func (h *Handler) ChangePassword(w http.ResponseWriter, r *http.Request) {
l := logger.Get()
userID, ok := r.Context().Value(middleware.UserIDKey).(uint)
if !ok {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
var req ChangePasswordRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, "Invalid request body", http.StatusBadRequest)
return
}
if err := h.validator.Struct(req); err != nil {
h.handleValidationError(w, err)
return
}
if err := h.service.ChangePassword(userID, req); err != nil {
l.Error("Ошибка смены пароля", zap.Error(err))
if errors.Is(err, ErrInvalidCurrentPassword) {
http.Error(w, "Current password is incorrect", http.StatusBadRequest)
return
}
if errors.Is(err, ErrAccountNotFound) {
http.Error(w, "Account not found", http.StatusNotFound)
return
}
http.Error(w, "Failed to change password", http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(map[string]string{
"message": "Password changed successfully",
})
}
// ForgotPassword запрос на сброс пароля
func (h *Handler) ForgotPassword(w http.ResponseWriter, r *http.Request) {
l := logger.Get()
var req ForgotPasswordRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, "Invalid request body", http.StatusBadRequest)
return
}
if err := h.validator.Struct(req); err != nil {
h.handleValidationError(w, err)
return
}
token, err := h.service.ForgotPassword(req.Email)
if err != nil {
l.Error("Ошибка запроса сброса пароля", zap.Error(err))
http.Error(w, "Password reset request failed", http.StatusInternalServerError)
return
}
// В реальном приложении здесь отправляется email
// Для тестирования возвращаем токен
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(PasswordResetResponse{
Message: "If the email exists, a reset link has been sent",
Token: token, // Только для тестирования
})
}
// ResetPassword подтверждение сброса пароля
func (h *Handler) ResetPassword(w http.ResponseWriter, r *http.Request) {
l := logger.Get()
var req ResetPasswordRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, "Invalid request body", http.StatusBadRequest)
return
}
if err := h.validator.Struct(req); err != nil {
h.handleValidationError(w, err)
return
}
if err := h.service.ResetPassword(req.Token, req.NewPassword); err != nil {
l.Error("Ошибка сброса пароля", zap.Error(err))
if errors.Is(err, ErrResetTokenNotFound) {
http.Error(w, "Reset token not found", http.StatusNotFound)
return
}
if errors.Is(err, ErrResetTokenExpired) {
http.Error(w, "Reset token has expired", http.StatusBadRequest)
return
}
if errors.Is(err, ErrResetTokenAlreadyUsed) {
http.Error(w, "Reset token has already been used", http.StatusBadRequest)
return
}
http.Error(w, "Password reset failed", http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(map[string]string{
"message": "Password has been successfully reset",
})
}
// DeleteAccount удаление аккаунта
func (h *Handler) DeleteAccount(w http.ResponseWriter, r *http.Request) {
l := logger.Get()
userID, ok := r.Context().Value(middleware.UserIDKey).(uint)
if !ok {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
if err := h.service.DeleteAccount(userID); err != nil {
l.Error("Ошибка удаления аккаунта", zap.Error(err))
if errors.Is(err, ErrAccountNotFound) {
http.Error(w, "Account not found", http.StatusNotFound)
return
}
http.Error(w, "Failed to delete account", http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(map[string]string{
"message": "Account deleted successfully",
})
}
// ==================== Административные методы ====================
// ListAccounts список аккаунтов (админ)
func (h *Handler) ListAccounts(w http.ResponseWriter, r *http.Request) {
l := logger.Get()
var req ListAccountsRequest
// Парсинг query параметров
if pageStr := r.URL.Query().Get("page"); pageStr != "" {
page, _ := strconv.Atoi(pageStr)
req.Page = page
}
if pageSizeStr := r.URL.Query().Get("page_size"); pageSizeStr != "" {
pageSize, _ := strconv.Atoi(pageSizeStr)
req.PageSize = pageSize
}
req.Search = r.URL.Query().Get("search")
req.Role = r.URL.Query().Get("role")
if isActiveStr := r.URL.Query().Get("is_active"); isActiveStr != "" {
isActive, _ := strconv.ParseBool(isActiveStr)
req.IsActive = &isActive
}
if err := h.validator.Struct(req); err != nil {
h.handleValidationError(w, err)
return
}
response, err := h.service.ListAccounts(req)
if err != nil {
l.Error("Ошибка получения списка аккаунтов", zap.Error(err))
http.Error(w, "Failed to list accounts", http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(response)
}
// GetAccountByIDAdmin получение аккаунта по ID (админ)
func (h *Handler) GetAccountByIDAdmin(w http.ResponseWriter, r *http.Request) {
l := logger.Get()
// Получаем ID из URL
idStr := r.URL.Query().Get("id")
if idStr == "" {
http.Error(w, "Missing id parameter", http.StatusBadRequest)
return
}
id, err := strconv.ParseUint(idStr, 10, 32)
if err != nil {
http.Error(w, "Invalid id parameter", http.StatusBadRequest)
return
}
account, err := h.service.GetAccountByID(uint(id))
if err != nil {
l.Error("Ошибка получения аккаунта", zap.Error(err))
if errors.Is(err, ErrAccountNotFound) {
http.Error(w, "Account not found", http.StatusNotFound)
return
}
http.Error(w, "Failed to get account", http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(account)
}
// VerifyAccount верификация аккаунта (админ)
func (h *Handler) VerifyAccount(w http.ResponseWriter, r *http.Request) {
l := logger.Get()
// Получаем ID из URL
idStr := r.URL.Query().Get("id")
if idStr == "" {
http.Error(w, "Missing id parameter", http.StatusBadRequest)
return
}
id, err := strconv.ParseUint(idStr, 10, 32)
if err != nil {
http.Error(w, "Invalid id parameter", http.StatusBadRequest)
return
}
var req VerifyAccountRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, "Invalid request body", http.StatusBadRequest)
return
}
if err := h.service.VerifyAccount(uint(id), req); err != nil {
l.Error("Ошибка верификации аккаунта", zap.Error(err))
if errors.Is(err, ErrAccountNotFound) {
http.Error(w, "Account not found", http.StatusNotFound)
return
}
http.Error(w, "Failed to verify account", http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(map[string]string{
"message": "Account verified successfully",
})
}
// UpdateAccountStatus обновление статуса аккаунта (админ)
func (h *Handler) UpdateAccountStatus(w http.ResponseWriter, r *http.Request) {
l := logger.Get()
// Получаем ID из URL
idStr := r.URL.Query().Get("id")
if idStr == "" {
http.Error(w, "Missing id parameter", http.StatusBadRequest)
return
}
id, err := strconv.ParseUint(idStr, 10, 32)
if err != nil {
http.Error(w, "Invalid id parameter", http.StatusBadRequest)
return
}
var req UpdateAccountStatusRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, "Invalid request body", http.StatusBadRequest)
return
}
if err := h.validator.Struct(req); err != nil {
h.handleValidationError(w, err)
return
}
if err := h.service.UpdateAccountStatus(uint(id), req); err != nil {
l.Error("Ошибка обновления статуса аккаунта", zap.Error(err))
if errors.Is(err, ErrAccountNotFound) {
http.Error(w, "Account not found", http.StatusNotFound)
return
}
http.Error(w, "Failed to update account status", http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(map[string]string{
"message": "Account status updated successfully",
})
}
// handleValidationError обрабатывает ошибки валидации
func (h *Handler) handleValidationError(w http.ResponseWriter, err error) {
var invalidValidationError *validator.InvalidValidationError
if errors.As(err, &invalidValidationError) {
http.Error(w, "Invalid request", http.StatusBadRequest)
return
}
var errs []string
for _, err := range err.(validator.ValidationErrors) {
errs = append(errs, fmt.Sprintf("field %s is invalid: %s", err.Field(), err.Tag()))
}
w.WriteHeader(http.StatusBadRequest)
json.NewEncoder(w).Encode(map[string]interface{}{
"error": "Validation failed",
"fields": errs,
})
}