From f7b09e260cffd1bd43e297c5d41392528fcc92a8 Mon Sep 17 00:00:00 2001 From: valitovgaziz Date: Mon, 29 Sep 2025 22:01:04 +0500 Subject: [PATCH] modified: go.mod modified: go.sum modified: internal/handlers/auth.go new file: internal/handlers/oauth.go modified: internal/handlers/user_handler.go renamed: internal/model/o_auth_provider.go -> internal/models/o_auth_provider.go renamed: internal/model/user.go -> internal/models/user.go modified: internal/repository/user_repository.go modified: internal/service/user_service.go modified: pkg/database/postgres.go add oauth_handler --- serv_golang_rest_api/go.mod | 9 +- serv_golang_rest_api/go.sum | 8 -- .../internal/handlers/auth.go | 8 +- .../internal/handlers/oauth.go | 129 ++++++++++++++++++ .../internal/handlers/user_handler.go | 4 +- .../{model => models}/o_auth_provider.go | 2 +- .../internal/{model => models}/user.go | 2 +- .../internal/repository/user_repository.go | 20 +-- .../internal/service/user_service.go | 16 +-- serv_golang_rest_api/pkg/database/postgres.go | 4 +- 10 files changed, 160 insertions(+), 42 deletions(-) create mode 100644 serv_golang_rest_api/internal/handlers/oauth.go rename serv_golang_rest_api/internal/{model => models}/o_auth_provider.go (97%) rename serv_golang_rest_api/internal/{model => models}/user.go (98%) diff --git a/serv_golang_rest_api/go.mod b/serv_golang_rest_api/go.mod index f12dd74..1846b3a 100644 --- a/serv_golang_rest_api/go.mod +++ b/serv_golang_rest_api/go.mod @@ -3,20 +3,17 @@ module serv_golang_rest_api go 1.25.1 require ( + github.com/golang-jwt/jwt/v4 v4.5.2 + golang.org/x/oauth2 v0.31.0 gorm.io/driver/postgres v1.6.0 gorm.io/gorm v1.31.0 ) -require ( - cloud.google.com/go/compute/metadata v0.3.0 // indirect - github.com/golang-jwt/jwt/v4 v4.5.2 // indirect - golang.org/x/oauth2 v0.31.0 // indirect -) +require cloud.google.com/go/compute/metadata v0.3.0 // indirect require ( github.com/go-chi/chi/v5 v5.2.3 github.com/go-chi/cors v1.2.2 - github.com/google/uuid v1.6.0 github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect github.com/jackc/pgx/v5 v5.6.0 // indirect diff --git a/serv_golang_rest_api/go.sum b/serv_golang_rest_api/go.sum index bccdc64..138e72d 100644 --- a/serv_golang_rest_api/go.sum +++ b/serv_golang_rest_api/go.sum @@ -9,8 +9,6 @@ github.com/go-chi/cors v1.2.2 h1:Jmey33TE+b+rB7fT8MUy1u0I4L+NARQlK6LhzKPSyQE= github.com/go-chi/cors v1.2.2/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58= github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI= github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= -github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= -github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= @@ -30,18 +28,12 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= -golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI= golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8= golang.org/x/oauth2 v0.31.0 h1:8Fq0yVZLh4j4YA47vHKFTa9Ew5XIrCP8LC6UeNZnLxo= golang.org/x/oauth2 v0.31.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= -golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= -golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= -golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= -golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk= golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/serv_golang_rest_api/internal/handlers/auth.go b/serv_golang_rest_api/internal/handlers/auth.go index b253839..cb967e3 100644 --- a/serv_golang_rest_api/internal/handlers/auth.go +++ b/serv_golang_rest_api/internal/handlers/auth.go @@ -3,7 +3,7 @@ package handlers import ( "net/http" - "serv_golang_rest_api/internal/model" + "serv_golang_rest_api/internal/models" "serv_golang_rest_api/internal/utils" "gorm.io/gorm" @@ -32,7 +32,7 @@ func (h *AuthHandler) Register(w http.ResponseWriter, r *http.Request) { } // Проверяем, существует ли пользователь - var existingUser model.User + var existingUser models.User if err := h.DB.Where("email = ?", req.Email).First(&existingUser).Error; err == nil { utils.WriteError(w, http.StatusConflict, "User already exists") return @@ -46,7 +46,7 @@ func (h *AuthHandler) Register(w http.ResponseWriter, r *http.Request) { } // Создаем пользователя - user := model.User{ + user := models.User{ Email: req.Email, Password: hashedPassword, Name: req.Name, @@ -78,7 +78,7 @@ func (h *AuthHandler) Login(w http.ResponseWriter, r *http.Request) { } // Ищем пользователя - var user model.User + var user models.User if err := h.DB.Where("email = ?", req.Email).First(&user).Error; err != nil { utils.WriteError(w, http.StatusUnauthorized, "Invalid credentials") return diff --git a/serv_golang_rest_api/internal/handlers/oauth.go b/serv_golang_rest_api/internal/handlers/oauth.go new file mode 100644 index 0000000..9957207 --- /dev/null +++ b/serv_golang_rest_api/internal/handlers/oauth.go @@ -0,0 +1,129 @@ +// handlers/oauth.go +package handlers + +import ( + "encoding/json" + "net/http" + "serv_golang_rest_api/internal/config" + "serv_golang_rest_api/internal/models" + "serv_golang_rest_api/internal/utils" + + "gorm.io/gorm" +) + +type OAuthHandler struct { + DB *gorm.DB +} + +type GoogleUserInfo struct { + ID string `json:"id"` + Email string `json:"email"` + Name string `json:"name"` +} + +type VKUserInfo struct { + Response []struct { + ID int `json:"id"` + FirstName string `json:"first_name"` + LastName string `json:"last_name"` + Email string `json:"email"` + } `json:"response"` +} + +func (h *OAuthHandler) GoogleLogin(w http.ResponseWriter, r *http.Request) { + url := config.GoogleOAuthConfig.AuthCodeURL("state") + http.Redirect(w, r, url, http.StatusTemporaryRedirect) +} + +func (h *OAuthHandler) GoogleCallback(w http.ResponseWriter, r *http.Request) { + code := r.URL.Query().Get("code") + + token, err := config.GoogleOAuthConfig.Exchange(r.Context(), code) + if err != nil { + utils.WriteError(w, http.StatusBadRequest, "Failed to exchange token") + return + } + + client := config.GoogleOAuthConfig.Client(r.Context(), token) + resp, err := client.Get("https://www.googleapis.com/oauth2/v2/userinfo") + if err != nil { + utils.WriteError(w, http.StatusBadRequest, "Failed to get user info") + return + } + defer resp.Body.Close() + + var userInfo GoogleUserInfo + if err := json.NewDecoder(resp.Body).Decode(&userInfo); err != nil { + utils.WriteError(w, http.StatusBadRequest, "Failed to decode user info") + return + } + + // Создаем или находим пользователя + user, err := h.findOrCreateOAuthUser("google", userInfo.ID, userInfo.Email, userInfo.Name) + if err != nil { + utils.WriteError(w, http.StatusInternalServerError, "Error processing user") + return + } + + jwtToken, err := utils.GenerateJWT(user.ID, user.Email) + if err != nil { + utils.WriteError(w, http.StatusInternalServerError, "Error generating token") + return + } + + // Редирект или возврат токена + utils.WriteJSON(w, http.StatusOK, map[string]interface{}{ + "token": jwtToken, + "user": user, + }) +} + +// Аналогичные методы для Yandex и VK... + +func (h *OAuthHandler) findOrCreateOAuthUser(provider, providerID, email, name string) (*models.User, error) { + var oauthProvider models.OAuthProvider + + // Ищем существующую привязку OAuth + err := h.DB.Where("provider = ? AND provider_id = ?", provider, providerID). + Preload("User"). + First(&oauthProvider).Error + + if err == nil { + // Нашли привязку, теперь загружаем пользователя + var user models.User + if err := h.DB.First(&user, oauthProvider.UserID).Error; err != nil { + return nil, err + } + return &user, nil + } + + // Если привязки нет, ищем пользователя по email + var user models.User + err = h.DB.Where("email = ?", email).First(&user).Error + + if err != nil { + // Создаем нового пользователя + user = models.User{ + Email: email, + Name: name, + // Генерируем случайный пароль для OAuth пользователей + Password: utils.GenerateRandomPassword(), + } + if err := h.DB.Create(&user).Error; err != nil { + return nil, err + } + } + + // Создаем привязку OAuth + oauthProvider = models.OAuthProvider{ + UserID: user.ID, + Provider: provider, + ProviderID: providerID, + } + + if err := h.DB.Create(&oauthProvider).Error; err != nil { + return nil, err + } + + return &user, nil +} \ No newline at end of file diff --git a/serv_golang_rest_api/internal/handlers/user_handler.go b/serv_golang_rest_api/internal/handlers/user_handler.go index 89466b3..09dc033 100644 --- a/serv_golang_rest_api/internal/handlers/user_handler.go +++ b/serv_golang_rest_api/internal/handlers/user_handler.go @@ -6,7 +6,7 @@ import ( "strconv" "github.com/go-chi/chi/v5" - "serv_golang_rest_api/internal/model" + "serv_golang_rest_api/internal/models" "serv_golang_rest_api/internal/service" ) @@ -19,7 +19,7 @@ func NewUserHandler(userService *service.UserService) *UserHandler { } func (h *UserHandler) CreateUser(w http.ResponseWriter, r *http.Request) { - var req model.CreateUserRequest + var req models.CreateUserRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, "Invalid request body", http.StatusBadRequest) return diff --git a/serv_golang_rest_api/internal/model/o_auth_provider.go b/serv_golang_rest_api/internal/models/o_auth_provider.go similarity index 97% rename from serv_golang_rest_api/internal/model/o_auth_provider.go rename to serv_golang_rest_api/internal/models/o_auth_provider.go index 006ee59..d594204 100644 --- a/serv_golang_rest_api/internal/model/o_auth_provider.go +++ b/serv_golang_rest_api/internal/models/o_auth_provider.go @@ -1,4 +1,4 @@ -package model +package models import ( "time" diff --git a/serv_golang_rest_api/internal/model/user.go b/serv_golang_rest_api/internal/models/user.go similarity index 98% rename from serv_golang_rest_api/internal/model/user.go rename to serv_golang_rest_api/internal/models/user.go index 9aab969..624bf41 100644 --- a/serv_golang_rest_api/internal/model/user.go +++ b/serv_golang_rest_api/internal/models/user.go @@ -1,4 +1,4 @@ -package model +package models import ( "time" diff --git a/serv_golang_rest_api/internal/repository/user_repository.go b/serv_golang_rest_api/internal/repository/user_repository.go index 090b192..8db3404 100644 --- a/serv_golang_rest_api/internal/repository/user_repository.go +++ b/serv_golang_rest_api/internal/repository/user_repository.go @@ -1,7 +1,7 @@ package repository import ( - "serv_golang_rest_api/internal/model" + "serv_golang_rest_api/internal/models" "gorm.io/gorm" ) @@ -14,32 +14,32 @@ func NewUserRepository(db *gorm.DB) *UserRepository { return &UserRepository{db: db} } -func (r *UserRepository) Create(user *model.User) error { +func (r *UserRepository) Create(user *models.User) error { return r.db.Create(user).Error } -func (r *UserRepository) FindByID(id uint) (*model.User, error) { - var user model.User +func (r *UserRepository) FindByID(id uint) (*models.User, error) { + var user models.User err := r.db.First(&user, id).Error return &user, err } -func (r *UserRepository) FindByEmail(email string) (*model.User, error) { - var user model.User +func (r *UserRepository) FindByEmail(email string) (*models.User, error) { + var user models.User err := r.db.Where("email = ?", email).First(&user).Error return &user, err } -func (r *UserRepository) FindAll() ([]model.User, error) { - var users []model.User +func (r *UserRepository) FindAll() ([]models.User, error) { + var users []models.User err := r.db.Find(&users).Error return users, err } -func (r *UserRepository) Update(user *model.User) error { +func (r *UserRepository) Update(user *models.User) error { return r.db.Save(user).Error } func (r *UserRepository) Delete(id uint) error { - return r.db.Delete(&model.User{}, id).Error + return r.db.Delete(&models.User{}, id).Error } \ No newline at end of file diff --git a/serv_golang_rest_api/internal/service/user_service.go b/serv_golang_rest_api/internal/service/user_service.go index 163939b..ec20773 100644 --- a/serv_golang_rest_api/internal/service/user_service.go +++ b/serv_golang_rest_api/internal/service/user_service.go @@ -2,7 +2,7 @@ package service import ( "errors" - "serv_golang_rest_api/internal/model" + "serv_golang_rest_api/internal/models" "serv_golang_rest_api/internal/repository" "golang.org/x/crypto/bcrypt" @@ -16,7 +16,7 @@ func NewUserService(userRepo *repository.UserRepository) *UserService { return &UserService{userRepo: userRepo} } -func (s *UserService) CreateUser(req *model.CreateUserRequest) (*model.UserResponse, error) { +func (s *UserService) CreateUser(req *models.CreateUserRequest) (*models.UserResponse, error) { // Проверяем существует ли пользователь с таким email existingUser, err := s.userRepo.FindByEmail(req.Email) // Проверяем как на nil, так на пустой ID @@ -30,7 +30,7 @@ func (s *UserService) CreateUser(req *model.CreateUserRequest) (*model.UserRespo return nil, err } - user := &model.User{ + user := &models.User{ Name: req.Name, Email: req.Email, Password: string(hashedPassword), @@ -43,7 +43,7 @@ func (s *UserService) CreateUser(req *model.CreateUserRequest) (*model.UserRespo return s.toUserResponse(user), nil } -func (s *UserService) GetUserByID(id uint) (*model.UserResponse, error) { +func (s *UserService) GetUserByID(id uint) (*models.UserResponse, error) { user, err := s.userRepo.FindByID(id) if err != nil { return nil, errors.New("user not found") @@ -52,13 +52,13 @@ func (s *UserService) GetUserByID(id uint) (*model.UserResponse, error) { return s.toUserResponse(user), nil } -func (s *UserService) GetAllUsers() ([]model.UserResponse, error) { +func (s *UserService) GetAllUsers() ([]models.UserResponse, error) { users, err := s.userRepo.FindAll() if err != nil { return nil, err } - var responses []model.UserResponse + var responses []models.UserResponse for _, user := range users { responses = append(responses, *s.toUserResponse(&user)) } @@ -66,8 +66,8 @@ func (s *UserService) GetAllUsers() ([]model.UserResponse, error) { return responses, nil } -func (s *UserService) toUserResponse(user *model.User) *model.UserResponse { - return &model.UserResponse{ +func (s *UserService) toUserResponse(user *models.User) *models.UserResponse { + return &models.UserResponse{ ID: user.ID, CreatedAt: user.CreatedAt, UpdatedAt: user.UpdatedAt, diff --git a/serv_golang_rest_api/pkg/database/postgres.go b/serv_golang_rest_api/pkg/database/postgres.go index 3bc7947..800c2ee 100644 --- a/serv_golang_rest_api/pkg/database/postgres.go +++ b/serv_golang_rest_api/pkg/database/postgres.go @@ -4,7 +4,7 @@ import ( "fmt" "log" "serv_golang_rest_api/internal/config" - "serv_golang_rest_api/internal/model" + "serv_golang_rest_api/internal/models" "gorm.io/driver/postgres" "gorm.io/gorm" @@ -46,7 +46,7 @@ func autoMigrate(db *gorm.DB) error { // Или используйте автоматические миграции GORM return db.AutoMigrate( - &model.User{}, + &models.User{}, // другие модели... ) } \ No newline at end of file