Добавил миддлвейр для авторизации. И дописал readme.md
This commit is contained in:
parent
f18f1a1662
commit
2310cfd05b
|
@ -2,5 +2,5 @@ vars {
|
||||||
host: http://localhost:8080
|
host: http://localhost:8080
|
||||||
manufacturerID: 1
|
manufacturerID: 1
|
||||||
productID: 6
|
productID: 6
|
||||||
token: npG8pIdPaFSBHdM119QpZtm773KKCoVIGw==
|
token: UFY7FwOZpuac11IXiogxtpfVYt0Ib2Il3w==
|
||||||
}
|
}
|
|
@ -24,7 +24,7 @@ func main() {
|
||||||
panic("failed to connect database")
|
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 {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
package controllers
|
package controllers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/rand"
|
|
||||||
"encoding/base64"
|
|
||||||
"golang-test/database"
|
"golang-test/database"
|
||||||
"golang-test/libs"
|
"golang-test/libs"
|
||||||
"golang-test/types"
|
"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
|
token.UserID = user.ID
|
||||||
user.Tokens = append(user.Tokens, token)
|
user.Tokens = append(user.Tokens, token)
|
||||||
|
|
||||||
|
@ -98,7 +96,7 @@ func LoginUser(c *gin.Context, login types.LoginRequest) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
token.Token = generateToken()
|
token.Token = libs.GenerateToken()
|
||||||
token.UserID = user.ID
|
token.UserID = user.ID
|
||||||
|
|
||||||
user.Tokens = append(user.Tokens, token)
|
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})
|
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) {
|
func GetUser(c *gin.Context) {
|
||||||
var products []types.ProductResponse
|
var products []types.ProductResponse
|
||||||
user, err := libs.GetUserFromHeaders(c)
|
userFromMiddleware, ok := c.Get("user")
|
||||||
if err != nil {
|
if !ok {
|
||||||
c.JSON(http.StatusUnauthorized, types.ErrorResponse{Message: err.Error()})
|
c.JSON(http.StatusUnauthorized, types.ErrorResponse{Message: "Authorization Error. Please login"})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
user := userFromMiddleware.(database.User)
|
||||||
|
|
||||||
for _, product := range user.Products {
|
for _, product := range user.Products {
|
||||||
products = append(products, types.ProductResponse{
|
products = append(products, types.ProductResponse{
|
||||||
ID: product.ID,
|
ID: product.ID,
|
||||||
Name: product.Name,
|
Name: product.Name,
|
||||||
Price: product.Price,
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
user, err := libs.GetUserFromHeaders(c)
|
userFromMiddleware, ok := c.Get("user")
|
||||||
if err != nil {
|
if !ok {
|
||||||
c.JSON(http.StatusUnauthorized, types.ErrorResponse{Message: err.Error()})
|
c.JSON(http.StatusUnauthorized, types.ErrorResponse{Message: "Authorization Error. Please login"})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
user := userFromMiddleware.(database.User)
|
||||||
|
|
||||||
user.Name = userName.Name
|
user.Name = userName.Name
|
||||||
|
|
||||||
|
@ -201,11 +198,12 @@ func BuyProduct(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
user, err := libs.GetUserFromHeaders(c)
|
userFromMiddleware, ok := c.Get("user")
|
||||||
if err != nil {
|
if !ok {
|
||||||
c.JSON(http.StatusUnauthorized, types.ErrorResponse{Message: err.Error()})
|
c.JSON(http.StatusUnauthorized, types.ErrorResponse{Message: "Authorization Error. Please login"})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
user := userFromMiddleware.(database.User)
|
||||||
|
|
||||||
if user.Money-product.Price < 0 {
|
if user.Money-product.Price < 0 {
|
||||||
c.JSON(http.StatusOK, types.MessageResponse{Message: "Not enough money"})
|
c.JSON(http.StatusOK, types.MessageResponse{Message: "Not enough money"})
|
||||||
|
|
|
@ -1,11 +0,0 @@
|
||||||
meta {
|
|
||||||
name: Test
|
|
||||||
type: http
|
|
||||||
seq: 2
|
|
||||||
}
|
|
||||||
|
|
||||||
get {
|
|
||||||
url: {{host}}/test
|
|
||||||
body: none
|
|
||||||
auth: none
|
|
||||||
}
|
|
|
@ -1,6 +1,7 @@
|
||||||
package libs
|
package libs
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/base64"
|
||||||
"errors"
|
"errors"
|
||||||
"golang-test/database"
|
"golang-test/database"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -65,3 +66,11 @@ func GetUserFromHeaders(c *gin.Context) (database.User, error) {
|
||||||
|
|
||||||
return user, nil
|
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
105
main.go
|
@ -1,12 +1,10 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"golang-test/controllers"
|
|
||||||
"golang-test/database"
|
|
||||||
"golang-test/types"
|
|
||||||
"log"
|
|
||||||
"net/http"
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"golang-test/database"
|
||||||
|
"golang-test/routes"
|
||||||
|
"log"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
@ -14,106 +12,15 @@ func main() {
|
||||||
// Создание сервера
|
// Создание сервера
|
||||||
r := gin.Default()
|
r := gin.Default()
|
||||||
|
|
||||||
r.POST("/product", func(c *gin.Context) {
|
//Создание маршрутов
|
||||||
var product database.Product
|
routes.ResolveRoutes(r)
|
||||||
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(®ister); 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)
|
|
||||||
})
|
|
||||||
|
|
||||||
err := r.Run()
|
err := r.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Отключение от БД при выключении приложения.
|
||||||
defer func() {
|
defer func() {
|
||||||
db := database.Connector()
|
db := database.Connector()
|
||||||
sqlDB, err := db.DB()
|
sqlDB, err := db.DB()
|
||||||
|
|
|
@ -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()
|
||||||
|
}
|
||||||
|
}
|
18
readme.md
18
readme.md
|
@ -3,6 +3,24 @@
|
||||||
## Обзор
|
## Обзор
|
||||||
Этот проект представляет собой RESTful API для управления товарами, производителями и пользователями. В качестве ORM используется GORM.
|
Этот проект представляет собой 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
|
## Endpoints
|
||||||
|
|
||||||
### Товары
|
### Товары
|
||||||
|
|
|
@ -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(®ister); 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)
|
||||||
|
})
|
||||||
|
}
|
Loading…
Reference in New Issue