Добавил миддлвейр для авторизации. И дописал readme.md

This commit is contained in:
Владимир Шалимов 2024-06-10 15:59:20 +05:00
parent f18f1a1662
commit 2310cfd05b
25 changed files with 205 additions and 136 deletions

View File

@ -2,5 +2,5 @@ vars {
host: http://localhost:8080
manufacturerID: 1
productID: 6
token: npG8pIdPaFSBHdM119QpZtm773KKCoVIGw==
token: UFY7FwOZpuac11IXiogxtpfVYt0Ib2Il3w==
}

View File

@ -24,7 +24,7 @@ func main() {
panic("failed to connect database")
}
err = db.AutoMigrate(&database.Manufacturer{}, &database.Product{}, &database.User{})
err = db.AutoMigrate(&database.Manufacturer{}, &database.Product{}, &database.User{}, &database.Token{})
if err != nil {
return
}

View File

@ -1,8 +1,6 @@
package controllers
import (
"crypto/rand"
"encoding/base64"
"golang-test/database"
"golang-test/libs"
"golang-test/types"
@ -55,7 +53,7 @@ func RegisterUser(c *gin.Context, register types.RegisterRequest) {
}
// Генерация токена для пользователя
token.Token = generateToken()
token.Token = libs.GenerateToken()
token.UserID = user.ID
user.Tokens = append(user.Tokens, token)
@ -98,7 +96,7 @@ func LoginUser(c *gin.Context, login types.LoginRequest) {
return
}
token.Token = generateToken()
token.Token = libs.GenerateToken()
token.UserID = user.ID
user.Tokens = append(user.Tokens, token)
@ -108,27 +106,25 @@ func LoginUser(c *gin.Context, login types.LoginRequest) {
c.JSON(http.StatusOK, types.TokenResponse{Token: token.Token})
}
func generateToken() string {
b := make([]byte, 25)
if _, err := rand.Read(b); err != nil {
return ""
}
return base64.StdEncoding.EncodeToString(b)
}
func GetUser(c *gin.Context) {
var products []types.ProductResponse
user, err := libs.GetUserFromHeaders(c)
if err != nil {
c.JSON(http.StatusUnauthorized, types.ErrorResponse{Message: err.Error()})
userFromMiddleware, ok := c.Get("user")
if !ok {
c.JSON(http.StatusUnauthorized, types.ErrorResponse{Message: "Authorization Error. Please login"})
return
}
user := userFromMiddleware.(database.User)
for _, product := range user.Products {
products = append(products, types.ProductResponse{
ID: product.ID,
Name: product.Name,
Price: product.Price,
ID: product.ID,
Name: product.Name,
Price: product.Price,
ManufacturerID: product.ManufacturerID,
Manufacturer: types.ManufacturerResponse{
Name: product.Manufacturer.Name,
ID: product.Manufacturer.ID,
},
})
}
@ -152,11 +148,12 @@ func EditUserName(c *gin.Context, userName types.EditUserNameRequest) {
return
}
user, err := libs.GetUserFromHeaders(c)
if err != nil {
c.JSON(http.StatusUnauthorized, types.ErrorResponse{Message: err.Error()})
userFromMiddleware, ok := c.Get("user")
if !ok {
c.JSON(http.StatusUnauthorized, types.ErrorResponse{Message: "Authorization Error. Please login"})
return
}
user := userFromMiddleware.(database.User)
user.Name = userName.Name
@ -201,11 +198,12 @@ func BuyProduct(c *gin.Context) {
return
}
user, err := libs.GetUserFromHeaders(c)
if err != nil {
c.JSON(http.StatusUnauthorized, types.ErrorResponse{Message: err.Error()})
userFromMiddleware, ok := c.Get("user")
if !ok {
c.JSON(http.StatusUnauthorized, types.ErrorResponse{Message: "Authorization Error. Please login"})
return
}
user := userFromMiddleware.(database.User)
if user.Money-product.Price < 0 {
c.JSON(http.StatusOK, types.MessageResponse{Message: "Not enough money"})

View File

@ -1,11 +0,0 @@
meta {
name: Test
type: http
seq: 2
}
get {
url: {{host}}/test
body: none
auth: none
}

View File

@ -1,6 +1,7 @@
package libs
import (
"encoding/base64"
"errors"
"golang-test/database"
"strings"
@ -65,3 +66,11 @@ func GetUserFromHeaders(c *gin.Context) (database.User, error) {
return user, nil
}
func GenerateToken() string {
b := make([]byte, 25)
if _, err := rand.Read(b); err != nil {
return ""
}
return base64.StdEncoding.EncodeToString(b)
}

105
main.go
View File

@ -1,12 +1,10 @@
package main
import (
"golang-test/controllers"
"golang-test/database"
"golang-test/types"
"log"
"net/http"
"github.com/gin-gonic/gin"
"golang-test/database"
"golang-test/routes"
"log"
)
func main() {
@ -14,106 +12,15 @@ func main() {
// Создание сервера
r := gin.Default()
r.POST("/product", func(c *gin.Context) {
var product database.Product
if err := c.ShouldBind(&product); err != nil {
c.JSON(http.StatusBadRequest, types.ErrorResponse{Message: err.Error()})
return
}
controllers.CreateProduct(c, product)
})
r.DELETE("/product/:id", func(c *gin.Context) {
controllers.DeleteProduct(c)
})
r.GET("/product", func(c *gin.Context) {
controllers.GetProducts(c)
})
r.GET("/product/:id", func(c *gin.Context) {
controllers.GetProductInfo(c)
})
r.GET("/product/:id/buy", func(c *gin.Context) {
controllers.BuyProduct(c)
})
r.POST("/manufacturer", func(c *gin.Context) {
var manufacturer database.Manufacturer
if err := c.ShouldBind(&manufacturer); err != nil {
c.JSON(http.StatusBadRequest, types.ErrorResponse{Message: err.Error()})
return
}
controllers.CreateManufacturer(c, manufacturer)
})
r.DELETE("/manufacturer/:id", func(c *gin.Context) {
controllers.DeleteManufacturer(c)
})
r.GET("/manufacturer", func(c *gin.Context) {
controllers.GetManufacturers(c)
})
r.PATCH("/manufacturer/:id", func(c *gin.Context) {
var manufacturer types.ManufacturerPatchRequest
if err := c.ShouldBind(&manufacturer); err != nil {
c.JSON(http.StatusBadRequest, types.ErrorResponse{Message: err.Error()})
return
}
controllers.EditManufacture(c, manufacturer)
})
r.POST("/registration", func(c *gin.Context) {
var form types.RegisterRequest
if err := c.ShouldBind(&form); err != nil {
c.JSON(http.StatusBadRequest, types.ErrorResponse{Message: err.Error()})
return
}
controllers.RegisterUser(c, form)
})
r.POST("/login", func(c *gin.Context) {
var register types.LoginRequest
if err := c.ShouldBind(&register); err != nil {
c.JSON(http.StatusBadRequest, types.ErrorResponse{Message: err.Error()})
return
}
controllers.LoginUser(c, register)
})
r.GET("/user", func(c *gin.Context) {
controllers.GetUser(c)
})
r.PATCH("/user", func(c *gin.Context) {
var user types.EditUserNameRequest
if err := c.ShouldBind(&user); err != nil {
c.JSON(http.StatusBadRequest, types.ErrorResponse{Message: err.Error()})
return
}
controllers.EditUserName(c, user)
})
r.POST("/user/addmoney", func(c *gin.Context) {
var moneyRequest types.AddMoneyRequest
if err := c.ShouldBind(&moneyRequest); err != nil {
c.JSON(http.StatusBadRequest, types.ErrorResponse{Message: err.Error()})
return
}
controllers.AddMoneyToUser(c, moneyRequest)
})
r.GET("/user/products", func(c *gin.Context) {
controllers.GetBuyedProducts(c)
})
//Создание маршрутов
routes.ResolveRoutes(r)
err := r.Run()
if err != nil {
return
}
//Отключение от БД при выключении приложения.
defer func() {
db := database.Connector()
sqlDB, err := db.DB()

View File

@ -0,0 +1,24 @@
package middlewares
import (
"github.com/gin-gonic/gin"
"golang-test/libs"
"golang-test/types"
"net/http"
)
// Middleware для авторизации
func AuthMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
user, err := libs.GetUserFromHeaders(c)
if err != nil {
c.JSON(http.StatusUnauthorized, types.ErrorResponse{Message: err.Error()})
c.Abort() // Прерываем обработку запроса
return
}
// Если пользователь успешно извлечен, продолжаем обработку запроса
c.Set("user", user)
c.Next()
}
}

View File

@ -3,6 +3,24 @@
## Обзор
Этот проект представляет собой RESTful API для управления товарами, производителями и пользователями. В качестве ORM используется GORM.
## Структура проекта
- **cli/migration.go** - Применение автомиграции в БД.
- **contollers/** - Вся бизнес логика, обращения к БД.
- **database/connect.go** - Подключение к БД.
- **database/models.go** - Модели Gorm.
- **brunoTest/** - Коллекция Bruno для тестирования API.
- **libs/** - Небольшие функции используемые в разных контроллерах.
- **middlewares/authMiddleware.go** - Прослойка для авторизации.
- **routes.go** - Описание всех маршрутов.
- **types/** - Типы для запросов и ответов на сервер.
- **validators/** - Кастомные валидаторы.
- **docker-compose.yml** - Файл docker-compose для запуска в контейнере вместе в БД.
- **Dockerfile** - Файл для сборки докер контейнера.
- **entrypoint.sh** - Входная точка для докер контейнера. Нужна чтобы запустить миграцию в бд перед запуском сервера.
- **main.go** - Основной файл. Используется для запуска сервера.
## Endpoints
### Товары

124
routes/routes.go Normal file
View File

@ -0,0 +1,124 @@
package routes
import (
"github.com/gin-gonic/gin"
"golang-test/controllers"
"golang-test/database"
"golang-test/middlewares"
"golang-test/types"
"net/http"
)
// ResolveRoutes настраивает конечные точки API
func ResolveRoutes(r *gin.Engine) {
// Создать новый продукт
r.POST("/product", func(c *gin.Context) {
var product database.Product
if err := c.ShouldBind(&product); err != nil {
c.JSON(http.StatusBadRequest, types.ErrorResponse{Message: err.Error()})
return
}
controllers.CreateProduct(c, product)
})
// Удалить продукт по ID
r.DELETE("/product/:id", func(c *gin.Context) {
controllers.DeleteProduct(c)
})
// Получить все продукты
r.GET("/product", func(c *gin.Context) {
controllers.GetProducts(c)
})
// Получить информацию о продукте по ID
r.GET("/product/:id", func(c *gin.Context) {
controllers.GetProductInfo(c)
})
// Купить продукт по ID
r.GET("/product/:id/buy", middlewares.AuthMiddleware(), func(c *gin.Context) {
controllers.BuyProduct(c)
})
// Создать нового производителя
r.POST("/manufacturer", func(c *gin.Context) {
var manufacturer database.Manufacturer
if err := c.ShouldBind(&manufacturer); err != nil {
c.JSON(http.StatusBadRequest, types.ErrorResponse{Message: err.Error()})
return
}
controllers.CreateManufacturer(c, manufacturer)
})
// Удалить производителя по ID
r.DELETE("/manufacturer/:id", func(c *gin.Context) {
controllers.DeleteManufacturer(c)
})
// Получить всех производителей
r.GET("/manufacturer", func(c *gin.Context) {
controllers.GetManufacturers(c)
})
// Редактировать производителя по ID
r.PATCH("/manufacturer/:id", func(c *gin.Context) {
var manufacturer types.ManufacturerPatchRequest
if err := c.ShouldBind(&manufacturer); err != nil {
c.JSON(http.StatusBadRequest, types.ErrorResponse{Message: err.Error()})
return
}
controllers.EditManufacture(c, manufacturer)
})
// Зарегистрировать нового пользователя
r.POST("/registration", func(c *gin.Context) {
var form types.RegisterRequest
if err := c.ShouldBind(&form); err != nil {
c.JSON(http.StatusBadRequest, types.ErrorResponse{Message: err.Error()})
return
}
controllers.RegisterUser(c, form)
})
// Войти в систему
r.POST("/login", func(c *gin.Context) {
var register types.LoginRequest
if err := c.ShouldBind(&register); err != nil {
c.JSON(http.StatusBadRequest, types.ErrorResponse{Message: err.Error()})
return
}
controllers.LoginUser(c, register)
})
// Получить информацию о пользователе
r.GET("/user", middlewares.AuthMiddleware(), func(c *gin.Context) {
controllers.GetUser(c)
})
// Редактировать имя пользователя
r.PATCH("/user", middlewares.AuthMiddleware(), func(c *gin.Context) {
var user types.EditUserNameRequest
if err := c.ShouldBind(&user); err != nil {
c.JSON(http.StatusBadRequest, types.ErrorResponse{Message: err.Error()})
return
}
controllers.EditUserName(c, user)
})
// Добавить деньги на счет пользователя
r.POST("/user/addmoney", func(c *gin.Context) {
var moneyRequest types.AddMoneyRequest
if err := c.ShouldBind(&moneyRequest); err != nil {
c.JSON(http.StatusBadRequest, types.ErrorResponse{Message: err.Error()})
return
}
controllers.AddMoneyToUser(c, moneyRequest)
})
// Получить купленные продукты пользователя
r.GET("/user/products", middlewares.AuthMiddleware(), func(c *gin.Context) {
controllers.GetBuyedProducts(c)
})
}