diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a56a9bc --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +config/config.yml + +/.idea/ +/qodana.yaml diff --git a/config/config.go b/config/config.go index 3a924a5..a1e91e6 100644 --- a/config/config.go +++ b/config/config.go @@ -22,6 +22,9 @@ type Config struct { Bcrypt struct { Cost int } + Jwtconfig struct { + Secret string + } } var AppConfig *Config diff --git a/controllers/auth_controller.go b/controllers/auth_controller.go index a353382..d45ab65 100644 --- a/controllers/auth_controller.go +++ b/controllers/auth_controller.go @@ -5,7 +5,6 @@ import ( "GinTutorial/models" "GinTutorial/utils" "net/http" - "strconv" "github.com/gin-gonic/gin" ) @@ -44,7 +43,7 @@ func Register(ctx *gin.Context) { return } - token, err := utils.GenerateJWT(strconv.Itoa(int(user.ID))) + token, err := utils.GenerateJWT(user.Username) if err != nil { ctx.JSON(http.StatusInternalServerError, gin.H{ @@ -87,7 +86,7 @@ func Login(ctx *gin.Context) { return } - token, err := utils.GenerateJWT(strconv.Itoa(int(user.ID))) + token, err := utils.GenerateJWT(user.Username) if err != nil { ctx.JSON(http.StatusInternalServerError, gin.H{ diff --git a/controllers/ex_controller.go b/controllers/ex_controller.go new file mode 100644 index 0000000..74985e6 --- /dev/null +++ b/controllers/ex_controller.go @@ -0,0 +1,43 @@ +package controllers + +import ( + "GinTutorial/global" + "GinTutorial/models" + "net/http" + "time" + + "github.com/gin-gonic/gin" +) + +func CreateExchangeRate(ctx *gin.Context) { + var exchangeRate models.ExchangeRate + + if err := ctx.ShouldBindJSON(&exchangeRate); err != nil { + ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + exchangeRate.Date = time.Now() + + if err := global.Db.AutoMigrate(&exchangeRate); err != nil { + ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + if err := global.Db.Create(&exchangeRate).Error; err != nil { + ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + ctx.JSON(http.StatusCreated, gin.H{"data": exchangeRate}) +} + +func GetExchangeRates(ctx *gin.Context) { + var exchangeRates []models.ExchangeRate + + if err := global.Db.Find(&exchangeRates).Error; err != nil { + ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + ctx.JSON(http.StatusOK, gin.H{"data": exchangeRates}) +} diff --git a/middlewares/auth_middleware.go b/middlewares/auth_middleware.go new file mode 100644 index 0000000..2ef1410 --- /dev/null +++ b/middlewares/auth_middleware.go @@ -0,0 +1,28 @@ +package middlewares + +import ( + "GinTutorial/utils" + "net/http" + + "github.com/gin-gonic/gin" +) + +func AuthMiddleWare() gin.HandlerFunc { + return func(ctx *gin.Context) { + token := ctx.GetHeader("Authorization") + if token == "" { + ctx.JSON(http.StatusUnauthorized, gin.H{"message": "Token is empty"}) + ctx.Abort() + return + } + username, err := utils.ParseToken(token) + + if err != nil { + ctx.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid Token"}) + ctx.Abort() + return + } + ctx.Set("username", username) + ctx.Next() + } +} diff --git a/models/ex_rate.go b/models/ex_rate.go new file mode 100644 index 0000000..0c65f9f --- /dev/null +++ b/models/ex_rate.go @@ -0,0 +1,11 @@ +package models + +import "time" + +type ExchangeRate struct { + ID uint `gorm:"primary_key" json:"_id"` + FromCurrency string `json:"from_currency" binding:"required"` + ToCurrency string `json:"to_currency" binding:"required"` + Rate float64 `json:"rate" binding:"required"` + Date time.Time `json:"date"` +} diff --git a/router/router.go b/router/router.go index 15e1417..9eea2fc 100644 --- a/router/router.go +++ b/router/router.go @@ -2,6 +2,7 @@ package router import ( "GinTutorial/controllers" + "GinTutorial/middlewares" "github.com/gin-gonic/gin" ) @@ -17,6 +18,13 @@ func SetupRouter() *gin.Engine { auth.POST("/login", controllers.Login) auth.POST("/register", controllers.Register) } + + ex := v1.Group("/ex") + { + ex.GET("/exchangeRates", controllers.GetExchangeRates) + ex.Use(middlewares.AuthMiddleWare()) + ex.POST("/exchangeRates", controllers.CreateExchangeRate) + } } } return r diff --git a/utils/utils.go b/utils/utils.go index cc118fe..4314f06 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -2,6 +2,8 @@ package utils import ( "GinTutorial/config" + "errors" + "fmt" "time" "github.com/golang-jwt/jwt/v5" @@ -19,7 +21,7 @@ func GenerateJWT(username string) (string, error) { "exp": time.Now().Add(time.Hour * 72).Unix(), }) - signedToken, err := token.SignedString([]byte("secret")) + signedToken, err := token.SignedString([]byte(config.AppConfig.Jwtconfig.Secret)) return "Bearer " + signedToken, err } @@ -27,3 +29,29 @@ func CheckPassword(password, hash string) bool { err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password)) return err == nil } + +func ParseToken(tokenString string) (string, error) { + if len(tokenString) > 7 && tokenString[0:7] == "Bearer " { + tokenString = tokenString[7:] + } + token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) { + if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { + return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"]) + } + return []byte(config.AppConfig.Jwtconfig.Secret), nil + }) + + if err != nil { + return "", err + } + + if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid { + username, ok := claims["username"].(string) + if !ok { + return "", errors.New("username invalid") + } + return username, nil + } + + return "", errors.New("token invalid") +}