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
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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=
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package model
|
||||
package models
|
||||
|
||||
import (
|
||||
"time"
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package model
|
||||
package models
|
||||
|
||||
import (
|
||||
"time"
|
||||
@@ -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
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -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{},
|
||||
// другие модели...
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user