first commit
This commit is contained in:
67
internal/controllers/auth.go
Normal file
67
internal/controllers/auth.go
Normal file
@ -0,0 +1,67 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"studioj/boilerplate_go/internal/models"
|
||||
"studioj/boilerplate_go/internal/services"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type AuthController interface {
|
||||
Register(*gin.Context)
|
||||
Login(*gin.Context)
|
||||
}
|
||||
|
||||
type authController struct {
|
||||
service services.AuthService
|
||||
}
|
||||
|
||||
func NewAuthController(service services.AuthService) AuthController {
|
||||
return &authController{
|
||||
service: service,
|
||||
}
|
||||
}
|
||||
|
||||
func (controller *authController) Register(c *gin.Context) {
|
||||
var params models.RegisterRequest
|
||||
if c.BindJSON(¶ms) != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "bad request"})
|
||||
return
|
||||
}
|
||||
|
||||
user, err := controller.service.Register(¶ms)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
token, err := controller.service.CreateToken(user.ID)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"user": user, "token": token.Token, "refresh_token": token.RefreshToken})
|
||||
}
|
||||
|
||||
func (controller *authController) Login(c *gin.Context) {
|
||||
var params models.LoginRequest
|
||||
if c.BindJSON(¶ms) != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "bad request"})
|
||||
return
|
||||
}
|
||||
|
||||
user, err := controller.service.Login(¶ms)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
token, err := controller.service.CreateToken(user.ID)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"user": user, "token": token.Token, "refresh_token": token.RefreshToken})
|
||||
}
|
59
internal/controllers/token.go
Normal file
59
internal/controllers/token.go
Normal file
@ -0,0 +1,59 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"studioj/boilerplate_go/internal/services"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type TokenController interface {
|
||||
Find(*gin.Context)
|
||||
}
|
||||
|
||||
type tokenController struct {
|
||||
service services.TokenService
|
||||
}
|
||||
|
||||
func NewTokenController(service services.TokenService) TokenController {
|
||||
return &tokenController{
|
||||
service: service,
|
||||
}
|
||||
}
|
||||
|
||||
func (controller *tokenController) Find(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
user_id := c.GetString("user_id")
|
||||
|
||||
if user_id != id {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Wrong user"})
|
||||
return
|
||||
}
|
||||
|
||||
result, err := controller.service.Find(id)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, result)
|
||||
}
|
||||
|
||||
func (controller *tokenController) List(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
user_id := c.GetString("user_id")
|
||||
|
||||
if user_id != id {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Wrong user"})
|
||||
return
|
||||
}
|
||||
|
||||
result, err := controller.service.Find(id)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, result)
|
||||
}
|
46
internal/controllers/user.go
Normal file
46
internal/controllers/user.go
Normal file
@ -0,0 +1,46 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"studioj/boilerplate_go/internal/services"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type UserController interface {
|
||||
Find(*gin.Context)
|
||||
}
|
||||
|
||||
type userController struct {
|
||||
service services.UserService
|
||||
tokenService services.TokenService
|
||||
}
|
||||
|
||||
func NewUserController(service services.UserService, tokenService services.TokenService) UserController {
|
||||
return &userController{
|
||||
service: service,
|
||||
tokenService: tokenService,
|
||||
}
|
||||
}
|
||||
|
||||
func (controller *userController) Find(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
user_id := c.GetString("sub")
|
||||
fmt.Println("id", id)
|
||||
fmt.Println("user_id", user_id)
|
||||
|
||||
if user_id != id {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Wrong user"})
|
||||
return
|
||||
}
|
||||
|
||||
result, err := controller.service.FindByID(id)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, result)
|
||||
}
|
40
internal/database/database.go
Normal file
40
internal/database/database.go
Normal file
@ -0,0 +1,40 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
config "studioj/boilerplate_go/configs"
|
||||
"studioj/boilerplate_go/internal/models"
|
||||
|
||||
"gorm.io/driver/mysql"
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/schema"
|
||||
)
|
||||
|
||||
type Database struct {
|
||||
*gorm.DB
|
||||
}
|
||||
|
||||
var DB *gorm.DB
|
||||
|
||||
func Init() {
|
||||
DB = ConnectDB((config.DATABASE_URL))
|
||||
}
|
||||
|
||||
func ConnectDB(url string) *gorm.DB {
|
||||
gorm_config := gorm.Config{NamingStrategy: schema.NamingStrategy{SingularTable: true}}
|
||||
db, err := gorm.Open(mysql.Open(url), &gorm_config)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return db
|
||||
}
|
||||
|
||||
func GetDB() *gorm.DB {
|
||||
return DB
|
||||
}
|
||||
|
||||
func AutoMigrate() {
|
||||
DB.AutoMigrate(
|
||||
&models.User{},
|
||||
&models.Token{},
|
||||
)
|
||||
}
|
29
internal/helpers/helpers.go
Normal file
29
internal/helpers/helpers.go
Normal file
@ -0,0 +1,29 @@
|
||||
package helpers
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
func InLambda() bool {
|
||||
if lambdaTaskRoot := os.Getenv("LAMBDA_TASK_ROOT"); lambdaTaskRoot != "" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func RandomPin() string {
|
||||
min := 100000
|
||||
max := 999999
|
||||
r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
pin := r.Intn(max-min) + min
|
||||
return strconv.Itoa(pin)
|
||||
}
|
||||
|
||||
func Datetime(timeString string) (time.Time, error) {
|
||||
layout := "2006-01-02T15:04:05.000Z"
|
||||
result, err := time.Parse(layout, timeString)
|
||||
return result, err
|
||||
}
|
68
internal/middleware/auth.go
Normal file
68
internal/middleware/auth.go
Normal file
@ -0,0 +1,68 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
config "studioj/boilerplate_go/configs"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
)
|
||||
|
||||
func Auth() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
sub, err := UserID(c.Request)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "unauthorized"})
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println("token", extract(c.Request))
|
||||
fmt.Println("sub", *sub)
|
||||
|
||||
c.Set("token", extract(c.Request))
|
||||
c.Set("sub", *sub)
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
func extract(r *http.Request) string {
|
||||
authorization := r.Header.Get("Authorization")
|
||||
strArr := strings.Split(authorization, " ")
|
||||
if len(strArr) == 2 {
|
||||
return strArr[1]
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func verify(r *http.Request) (*jwt.Token, error) {
|
||||
tokenString := extract(r)
|
||||
jwtToken, err := jwt.Parse(tokenString, func(jwtToken *jwt.Token) (interface{}, error) {
|
||||
if _, ok := jwtToken.Method.(*jwt.SigningMethodHMAC); !ok {
|
||||
return nil, fmt.Errorf("unexpected signing method: %v", jwtToken.Header["alg"])
|
||||
}
|
||||
return []byte(config.SECRET_KEY), nil
|
||||
})
|
||||
|
||||
return jwtToken, err
|
||||
}
|
||||
|
||||
func UserID(r *http.Request) (*string, error) {
|
||||
jwtToken, err := verify(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
claims, ok := jwtToken.Claims.(jwt.MapClaims)
|
||||
if !ok || !jwtToken.Valid {
|
||||
return nil, errors.New("refresh token is invalid")
|
||||
}
|
||||
|
||||
sub := claims["sub"].(string)
|
||||
|
||||
return &sub, nil
|
||||
}
|
44
internal/middleware/transaction.go
Normal file
44
internal/middleware/transaction.go
Normal file
@ -0,0 +1,44 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func StatusInList(status int, statusList []int) bool {
|
||||
for _, i := range statusList {
|
||||
for i == status {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func Transaction(db *gorm.DB) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
txHandle := db.Begin()
|
||||
log.Print("begining database transaction")
|
||||
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
txHandle.Rollback()
|
||||
}
|
||||
}()
|
||||
|
||||
c.Set("db_trx", txHandle)
|
||||
c.Next()
|
||||
|
||||
if StatusInList(c.Writer.Status(), []int{http.StatusOK, http.StatusCreated}) {
|
||||
log.Print("committing transactions")
|
||||
if err := txHandle.Commit().Error; err != nil {
|
||||
log.Print("transaction commit error: ", err)
|
||||
} else {
|
||||
log.Print("rollback transaction due to status code: ", c.Writer.Status())
|
||||
txHandle.Rollback()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
11
internal/models/auth.go
Normal file
11
internal/models/auth.go
Normal file
@ -0,0 +1,11 @@
|
||||
package models
|
||||
|
||||
type LoginRequest struct {
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
type RegisterRequest struct {
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
}
|
25
internal/models/token.go
Normal file
25
internal/models/token.go
Normal file
@ -0,0 +1,25 @@
|
||||
package models
|
||||
|
||||
import "time"
|
||||
|
||||
type Token struct {
|
||||
ID string `json:"id" db:"id" gorm:"column:id;size:255;primary_key;"`
|
||||
UserID string `json:"user_id" db:"user_id" gorm:"column:user_id;size:255;index;"`
|
||||
Token string `json:"token" gorm:"column:token;size:255;index;"`
|
||||
RefreshToken string `json:"refresh_token" gorm:"column:token;size:255;index;"`
|
||||
Status string `json:"status" gorm:"column:status;size:10;index;"`
|
||||
ExpireAt time.Time `json:"expire_at" gorm:"column:expire_at;index;"`
|
||||
UpdatedAt time.Time `json:"updated_at" gorm:"column:updated_at;index;"`
|
||||
CreatedAt time.Time `json:"created_at" gorm:"column:created_at;index;"`
|
||||
}
|
||||
|
||||
type TokenResponse struct {
|
||||
Token string `json:"token"`
|
||||
TokenBody TokenBody `json:"tokenBody"`
|
||||
}
|
||||
|
||||
type TokenBody struct {
|
||||
ExpireAt time.Time `json:"tokenExpiredDate"`
|
||||
TokenIdx int `json:"tokenIdx"`
|
||||
TokenType int `json:"tokenType"`
|
||||
}
|
9
internal/models/user.go
Normal file
9
internal/models/user.go
Normal file
@ -0,0 +1,9 @@
|
||||
package models
|
||||
|
||||
type User struct {
|
||||
ID string `json:"id" db:"id" gorm:"column:id;size:255;primary_key;"`
|
||||
Username string `json:"username" db:"username" gorm:"column:username;size:50;uniqueIndex;"`
|
||||
Score int32 `json:"score" db:"score" gorm:"column:score;"`
|
||||
Money int32 `json:"money" db:"money" gorm:"column:money;"`
|
||||
Password string `json:"-" db:"password" gorm:"column:password;size:255;not null;"`
|
||||
}
|
68
internal/repositories/token.go
Normal file
68
internal/repositories/token.go
Normal file
@ -0,0 +1,68 @@
|
||||
package repositories
|
||||
|
||||
import (
|
||||
config "studioj/boilerplate_go/configs"
|
||||
"studioj/boilerplate_go/internal/models"
|
||||
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type tokenRepository struct {
|
||||
DB *gorm.DB
|
||||
}
|
||||
|
||||
func NewTokenRepository(db *gorm.DB) TokenRepository {
|
||||
return &tokenRepository{
|
||||
DB: db,
|
||||
}
|
||||
}
|
||||
|
||||
type TokenRepository interface {
|
||||
Generate(string, int64) (string, error)
|
||||
Find(string) (*models.Token, error)
|
||||
Create(*models.Token) (*models.Token, error)
|
||||
Update(*models.Token) (*models.Token, error)
|
||||
Delete(string) error
|
||||
}
|
||||
|
||||
func (s *tokenRepository) Generate(sub string, expire_at int64) (string, error) {
|
||||
claims := jwt.MapClaims{}
|
||||
claims["authorized"] = true
|
||||
claims["sub"] = sub
|
||||
claims["exp"] = expire_at
|
||||
at := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||
token, err := at.SignedString([]byte(config.SECRET_KEY))
|
||||
|
||||
return token, err
|
||||
}
|
||||
|
||||
func (r *tokenRepository) Find(id string) (*models.Token, error) {
|
||||
var token *models.Token
|
||||
err := r.DB.Where("id = ?", id).First(&token).Error
|
||||
return token, err
|
||||
}
|
||||
|
||||
func (r *tokenRepository) Create(token *models.Token) (*models.Token, error) {
|
||||
err := r.DB.Create(&token).Error
|
||||
return token, err
|
||||
}
|
||||
|
||||
func (r *tokenRepository) Update(token *models.Token) (*models.Token, error) {
|
||||
var row *models.Token
|
||||
if err := r.DB.Where("id=?", token.ID).First(&row).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err := r.DB.Model(&row).Select("*").Updates(&token).Error
|
||||
return row, err
|
||||
}
|
||||
|
||||
func (r *tokenRepository) Delete(id string) error {
|
||||
var token *models.Token
|
||||
if err := r.DB.Where("id=?", id).First(&token).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
err := r.DB.Delete(&token).Error
|
||||
return err
|
||||
}
|
68
internal/repositories/user.go
Normal file
68
internal/repositories/user.go
Normal file
@ -0,0 +1,68 @@
|
||||
package repositories
|
||||
|
||||
import (
|
||||
"studioj/boilerplate_go/internal/models"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type userRepository struct {
|
||||
DB *gorm.DB
|
||||
}
|
||||
|
||||
func NewUserRepository(db *gorm.DB) UserRepository {
|
||||
return &userRepository{
|
||||
DB: db,
|
||||
}
|
||||
}
|
||||
|
||||
type UserRepository interface {
|
||||
List() (*[]models.User, error)
|
||||
FindByID(string) (*models.User, error)
|
||||
FindByUsername(string) (*models.User, error)
|
||||
Create(*models.User) (*models.User, error)
|
||||
Update(*models.User) (*models.User, error)
|
||||
Delete(string) error
|
||||
}
|
||||
|
||||
func (r *userRepository) List() (*[]models.User, error) {
|
||||
var users *[]models.User
|
||||
err := r.DB.Find(&users).Error
|
||||
return users, err
|
||||
}
|
||||
|
||||
func (r *userRepository) FindByID(id string) (*models.User, error) {
|
||||
var user *models.User
|
||||
err := r.DB.Where("id = ?", id).First(&user).Error
|
||||
return user, err
|
||||
}
|
||||
|
||||
func (r *userRepository) FindByUsername(username string) (*models.User, error) {
|
||||
var user *models.User
|
||||
err := r.DB.Where("username = ?", username).First(&user).Error
|
||||
return user, err
|
||||
}
|
||||
|
||||
func (r *userRepository) Create(user *models.User) (*models.User, error) {
|
||||
err := r.DB.Create(&user).Error
|
||||
return user, err
|
||||
}
|
||||
|
||||
func (r *userRepository) Update(user *models.User) (*models.User, error) {
|
||||
var row *models.User
|
||||
if err := r.DB.Where("id=?", user.ID).First(&row).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err := r.DB.Model(&row).Select("*").Updates(&user).Error
|
||||
return row, err
|
||||
}
|
||||
|
||||
func (r *userRepository) Delete(id string) error {
|
||||
var user *models.User
|
||||
if err := r.DB.Where("id=?", id).First(&user).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
err := r.DB.Delete(&user).Error
|
||||
return err
|
||||
}
|
53
internal/routers/auth.go
Normal file
53
internal/routers/auth.go
Normal file
@ -0,0 +1,53 @@
|
||||
package routers
|
||||
|
||||
import (
|
||||
"studioj/boilerplate_go/internal/controllers"
|
||||
"studioj/boilerplate_go/internal/repositories"
|
||||
"studioj/boilerplate_go/internal/services"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type AuthRouter interface {
|
||||
SetRouter(db *gorm.DB, router *gin.Engine)
|
||||
}
|
||||
|
||||
type authRouter struct {
|
||||
db *gorm.DB
|
||||
userRepository repositories.UserRepository
|
||||
tokenRepository repositories.TokenRepository
|
||||
service services.AuthService
|
||||
tokenService services.TokenService
|
||||
controller controllers.AuthController
|
||||
router *gin.Engine
|
||||
}
|
||||
|
||||
func InitAuthRouter(db *gorm.DB, router *gin.Engine) {
|
||||
r := NewAuthRouter(db, router)
|
||||
r.SetAuthRouter(db, router)
|
||||
}
|
||||
|
||||
func NewAuthRouter(db *gorm.DB, router *gin.Engine) *authRouter {
|
||||
userRepository := repositories.NewUserRepository(db)
|
||||
tokenRepository := repositories.NewTokenRepository(db)
|
||||
service := services.NewAuthService(userRepository, tokenRepository)
|
||||
tokenService := services.NewTokenService(tokenRepository)
|
||||
controller := controllers.NewAuthController(service)
|
||||
|
||||
return &authRouter{
|
||||
db: db,
|
||||
userRepository: userRepository,
|
||||
tokenRepository: tokenRepository,
|
||||
service: service,
|
||||
tokenService: tokenService,
|
||||
controller: controller,
|
||||
router: router,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *authRouter) SetAuthRouter(db *gorm.DB, router *gin.Engine) {
|
||||
group := router.Group("/auth")
|
||||
group.POST("login", r.controller.Login)
|
||||
group.POST("register", r.controller.Register)
|
||||
}
|
19
internal/routers/router.go
Normal file
19
internal/routers/router.go
Normal file
@ -0,0 +1,19 @@
|
||||
package routers
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"studioj/boilerplate_go/internal/database"
|
||||
)
|
||||
|
||||
var Router *gin.Engine
|
||||
|
||||
func Init() {
|
||||
gin.SetMode(gin.ReleaseMode)
|
||||
Router = gin.Default()
|
||||
maindb := database.GetDB()
|
||||
|
||||
InitAuthRouter(maindb, Router)
|
||||
InitTokenRouter(maindb, Router)
|
||||
InitUserRouter(maindb, Router)
|
||||
}
|
46
internal/routers/token.go
Normal file
46
internal/routers/token.go
Normal file
@ -0,0 +1,46 @@
|
||||
package routers
|
||||
|
||||
import (
|
||||
"studioj/boilerplate_go/internal/controllers"
|
||||
"studioj/boilerplate_go/internal/repositories"
|
||||
"studioj/boilerplate_go/internal/services"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type TokenRouter interface {
|
||||
SetRouter(db *gorm.DB, router *gin.Engine)
|
||||
}
|
||||
|
||||
type tokenRouter struct {
|
||||
db *gorm.DB
|
||||
repository repositories.TokenRepository
|
||||
service services.TokenService
|
||||
controller controllers.TokenController
|
||||
router *gin.Engine
|
||||
}
|
||||
|
||||
func InitTokenRouter(db *gorm.DB, router *gin.Engine) {
|
||||
r := NewTokenRouter(db, router)
|
||||
r.SetTokenRouter(db, router)
|
||||
}
|
||||
|
||||
func NewTokenRouter(db *gorm.DB, router *gin.Engine) *tokenRouter {
|
||||
repository := repositories.NewTokenRepository(db)
|
||||
service := services.NewTokenService(repository)
|
||||
controller := controllers.NewTokenController(service)
|
||||
|
||||
return &tokenRouter{
|
||||
db: db,
|
||||
repository: repository,
|
||||
service: service,
|
||||
controller: controller,
|
||||
router: router,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *tokenRouter) SetTokenRouter(db *gorm.DB, router *gin.Engine) {
|
||||
// group := router.Group("/token")
|
||||
// group.GET("refresh", middleware.Auth(), r.controller.Refresh)
|
||||
}
|
50
internal/routers/user.go
Normal file
50
internal/routers/user.go
Normal file
@ -0,0 +1,50 @@
|
||||
package routers
|
||||
|
||||
import (
|
||||
"studioj/boilerplate_go/internal/controllers"
|
||||
"studioj/boilerplate_go/internal/middleware"
|
||||
"studioj/boilerplate_go/internal/repositories"
|
||||
"studioj/boilerplate_go/internal/services"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type UserRouter interface {
|
||||
SetRouter(db *gorm.DB, router *gin.Engine)
|
||||
}
|
||||
|
||||
type userRouter struct {
|
||||
db *gorm.DB
|
||||
repository repositories.UserRepository
|
||||
service services.UserService
|
||||
controller controllers.UserController
|
||||
router *gin.Engine
|
||||
}
|
||||
|
||||
func InitUserRouter(db *gorm.DB, router *gin.Engine) {
|
||||
r := NewUserRouter(db, router)
|
||||
r.SetUserRouter(db, router)
|
||||
}
|
||||
|
||||
func NewUserRouter(db *gorm.DB, router *gin.Engine) *userRouter {
|
||||
repository := repositories.NewUserRepository(db)
|
||||
tokenRepository := repositories.NewTokenRepository(db)
|
||||
service := services.NewUserService(repository, tokenRepository)
|
||||
|
||||
tokenService := services.NewTokenService(tokenRepository)
|
||||
controller := controllers.NewUserController(service, tokenService)
|
||||
|
||||
return &userRouter{
|
||||
db: db,
|
||||
repository: repository,
|
||||
service: service,
|
||||
controller: controller,
|
||||
router: router,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *userRouter) SetUserRouter(db *gorm.DB, router *gin.Engine) {
|
||||
group := router.Group("/user")
|
||||
group.GET("/:id", middleware.Auth(), r.controller.Find)
|
||||
}
|
90
internal/services/auth.go
Normal file
90
internal/services/auth.go
Normal file
@ -0,0 +1,90 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"studioj/boilerplate_go/internal/models"
|
||||
"studioj/boilerplate_go/internal/repositories"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
type authService struct {
|
||||
userRepository repositories.UserRepository
|
||||
tokenRepository repositories.TokenRepository
|
||||
}
|
||||
|
||||
type AuthService interface {
|
||||
Register(*models.RegisterRequest) (*models.User, error)
|
||||
Login(*models.LoginRequest) (*models.User, error)
|
||||
CreateToken(string) (*models.Token, error)
|
||||
}
|
||||
|
||||
func NewAuthService(userRepository repositories.UserRepository, tokenRepository repositories.TokenRepository) AuthService {
|
||||
return &authService{
|
||||
userRepository: userRepository,
|
||||
tokenRepository: tokenRepository,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *authService) Register(request *models.RegisterRequest) (*models.User, error) {
|
||||
hash, err := bcrypt.GenerateFromPassword([]byte(request.Password), 10)
|
||||
if err != nil {
|
||||
return nil, errors.New("fail to hash password")
|
||||
}
|
||||
|
||||
// Create the user
|
||||
newUser := models.User{
|
||||
ID: uuid.NewString(),
|
||||
Username: request.Username,
|
||||
Password: string(hash),
|
||||
}
|
||||
|
||||
user, err := s.userRepository.Create(&newUser)
|
||||
if err != nil {
|
||||
return nil, errors.New("fail to create user")
|
||||
}
|
||||
|
||||
return user, err
|
||||
}
|
||||
|
||||
func (s *authService) Login(request *models.LoginRequest) (*models.User, error) {
|
||||
user, err := s.userRepository.FindByUsername(request.Username)
|
||||
if err != nil || user == nil {
|
||||
return nil, errors.New("invalid user or password")
|
||||
}
|
||||
|
||||
err = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(request.Password))
|
||||
if err != nil {
|
||||
return nil, errors.New("invalid user or password")
|
||||
}
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func (s *authService) CreateToken(user_id string) (*models.Token, error) {
|
||||
tokenExpiredAt := time.Now().Add(time.Hour * 24 * 30)
|
||||
accessToken, err := s.tokenRepository.Generate(user_id, tokenExpiredAt.Unix())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
refreshExpiredAt := time.Now().Add(time.Hour * 24 * 90)
|
||||
refreshToken, err := s.tokenRepository.Generate(user_id, refreshExpiredAt.Unix())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
newToken := &models.Token{
|
||||
ID: uuid.NewString(),
|
||||
UserID: user_id,
|
||||
Token: accessToken,
|
||||
RefreshToken: refreshToken,
|
||||
Status: "valid",
|
||||
ExpireAt: tokenExpiredAt,
|
||||
}
|
||||
|
||||
token, err := s.tokenRepository.Create(newToken)
|
||||
|
||||
return token, err
|
||||
}
|
115
internal/services/token.go
Normal file
115
internal/services/token.go
Normal file
@ -0,0 +1,115 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
config "studioj/boilerplate_go/configs"
|
||||
"studioj/boilerplate_go/internal/models"
|
||||
"studioj/boilerplate_go/internal/repositories"
|
||||
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
)
|
||||
|
||||
type tokenService struct {
|
||||
repository repositories.TokenRepository
|
||||
}
|
||||
|
||||
type TokenService interface {
|
||||
Find(string) (*models.Token, error)
|
||||
Create(*models.Token) (*models.Token, error)
|
||||
Update(*models.Token) (*models.Token, error)
|
||||
Delete(string) error
|
||||
|
||||
Generate(string, int64) (string, error)
|
||||
Verify(tokenString string) (*jwt.Token, error)
|
||||
|
||||
GetJwtToken(string) (*jwt.Token, error)
|
||||
ExtractTokenString(string) string
|
||||
VerifyTokenString(string) (*jwt.Token, error)
|
||||
ValidToken(*jwt.Token) (bool, error)
|
||||
}
|
||||
|
||||
func NewTokenService(repository repositories.TokenRepository) TokenService {
|
||||
return &tokenService{
|
||||
repository: repository,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *tokenService) Find(id string) (*models.Token, error) {
|
||||
return s.repository.Find(id)
|
||||
}
|
||||
|
||||
func (s *tokenService) Create(token *models.Token) (*models.Token, error) {
|
||||
return s.repository.Create(token)
|
||||
}
|
||||
|
||||
func (s *tokenService) Update(token *models.Token) (*models.Token, error) {
|
||||
return s.repository.Update(token)
|
||||
}
|
||||
|
||||
func (s *tokenService) Delete(id string) error {
|
||||
return s.repository.Delete(id)
|
||||
}
|
||||
|
||||
func (s *tokenService) Verify(tokenString string) (*jwt.Token, error) {
|
||||
jwtToken, err := jwt.Parse(tokenString, func(jwtToken *jwt.Token) (interface{}, error) {
|
||||
if _, ok := jwtToken.Method.(*jwt.SigningMethodHMAC); !ok {
|
||||
return nil, fmt.Errorf("unexpected signing method: %v", jwtToken.Header["alg"])
|
||||
}
|
||||
return []byte(config.SECRET_KEY), nil
|
||||
})
|
||||
|
||||
return jwtToken, err
|
||||
}
|
||||
|
||||
func (s *tokenService) Generate(user_id string, expire_at int64) (string, error) {
|
||||
claims := jwt.MapClaims{}
|
||||
claims["authorized"] = true
|
||||
claims["sub"] = user_id
|
||||
claims["exp"] = expire_at
|
||||
at := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||
token, err := at.SignedString([]byte(config.SECRET_KEY))
|
||||
|
||||
return token, err
|
||||
}
|
||||
|
||||
func (s *tokenService) GetJwtToken(tokenString string) (*jwt.Token, error) {
|
||||
jwtToken, err := jwt.Parse(tokenString, func(jwtToken *jwt.Token) (interface{}, error) {
|
||||
if _, ok := jwtToken.Method.(*jwt.SigningMethodHMAC); !ok {
|
||||
return nil, fmt.Errorf("unexpected signing method: %v", jwtToken.Header["alg"])
|
||||
}
|
||||
return []byte(config.SECRET_KEY), nil
|
||||
})
|
||||
return jwtToken, err
|
||||
}
|
||||
|
||||
func (s *tokenService) ExtractTokenString(authorization string) string {
|
||||
strArr := strings.Split(authorization, " ")
|
||||
if len(strArr) == 2 {
|
||||
return strArr[1]
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (s *tokenService) VerifyTokenString(tokenString string) (*jwt.Token, error) {
|
||||
jwtToken, err := jwt.Parse(tokenString, func(jwtToken *jwt.Token) (interface{}, error) {
|
||||
if _, ok := jwtToken.Method.(*jwt.SigningMethodHMAC); !ok {
|
||||
return nil, fmt.Errorf("unexpected signing method: %v", jwtToken.Header["alg"])
|
||||
}
|
||||
return []byte(config.SECRET_KEY), nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return jwtToken, nil
|
||||
}
|
||||
|
||||
func (s *tokenService) ValidToken(jwtToken *jwt.Token) (bool, error) {
|
||||
if jwtToken == nil {
|
||||
return false, fmt.Errorf("no token")
|
||||
}
|
||||
return jwtToken.Valid, nil
|
||||
}
|
38
internal/services/user.go
Normal file
38
internal/services/user.go
Normal file
@ -0,0 +1,38 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"studioj/boilerplate_go/internal/models"
|
||||
"studioj/boilerplate_go/internal/repositories"
|
||||
)
|
||||
|
||||
type userService struct {
|
||||
repository repositories.UserRepository
|
||||
tokenRepository repositories.TokenRepository
|
||||
}
|
||||
|
||||
type UserService interface {
|
||||
FindByID(string) (*models.User, error)
|
||||
FindByUsername(string) (*models.User, error)
|
||||
Create(*models.User) (*models.User, error)
|
||||
}
|
||||
|
||||
func NewUserService(repository repositories.UserRepository, tokenRepository repositories.TokenRepository) UserService {
|
||||
return &userService{
|
||||
repository: repository,
|
||||
tokenRepository: tokenRepository,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *userService) FindByID(id string) (*models.User, error) {
|
||||
return s.repository.FindByID(id)
|
||||
}
|
||||
|
||||
func (s *userService) FindByUsername(username string) (*models.User, error) {
|
||||
return s.repository.FindByUsername(username)
|
||||
}
|
||||
|
||||
func (s *userService) Create(user *models.User) (*models.User, error) {
|
||||
result, err := s.repository.Create(user)
|
||||
|
||||
return result, err
|
||||
}
|
Reference in New Issue
Block a user