├── .env.example ├── logo.png ├── .air.toml ├── src ├── models │ ├── base.go │ ├── cache.model.go │ ├── auth.model.go │ ├── article.model.go │ ├── user.model.go │ └── product.model.go ├── controllers │ ├── base.go │ ├── swagger.controllers.go │ ├── auth.controllers.go │ ├── users.controllers.go │ ├── products.controllers.go │ └── articles.controllers.go ├── config │ └── config.go ├── common │ ├── model.go │ └── controller.go ├── middleware │ └── jwt.middleware.go ├── main.go ├── core │ └── db │ │ ├── gorm.go │ │ ├── redis.go │ │ ├── mongo.go │ │ └── postgres.go └── utils │ ├── http.go │ └── token.go ├── Dockerfile ├── .vscode └── launch.json ├── .gitignore ├── docker-compose.yml ├── go.mod ├── README.md ├── docs ├── swagger.yaml ├── swagger.json └── docs.go └── go.sum /.env.example: -------------------------------------------------------------------------------- 1 | MONGO_CONNECTION_STRING=na 2 | PORT=na -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mehdikarimian/go-boilerplate/HEAD/logo.png -------------------------------------------------------------------------------- /.air.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | cmd = "go build -o ./tmp/main ./src" 3 | bin = "tmp/main" 4 | log = "air.log" 5 | 6 | [watch] 7 | includes = ["./src"] -------------------------------------------------------------------------------- /src/models/base.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import "go-boilerplate/src/common" 4 | 5 | type BaseModel struct { 6 | *common.ModelConstructor 7 | } 8 | -------------------------------------------------------------------------------- /src/controllers/base.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import "go-boilerplate/src/common" 4 | 5 | type BaseController struct { 6 | *common.ControllerConstructor 7 | } 8 | -------------------------------------------------------------------------------- /src/config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "os" 5 | 6 | "github.com/joho/godotenv" 7 | ) 8 | 9 | func LoadConfig(key string) string { 10 | err := godotenv.Load(".env") 11 | if err != nil { 12 | // log.Fatal("Error loading .env file", err) 13 | } 14 | 15 | return os.Getenv(key) 16 | } 17 | -------------------------------------------------------------------------------- /src/common/model.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "github.com/go-gorp/gorp" 5 | _redis "github.com/go-redis/redis/v7" 6 | "go.mongodb.org/mongo-driver/mongo" 7 | "gorm.io/gorm" 8 | ) 9 | 10 | type ModelConstructor struct { 11 | Collection *mongo.Collection 12 | Repository *gorp.DbMap 13 | Redis *_redis.Client 14 | Gorm *gorm.DB 15 | } 16 | -------------------------------------------------------------------------------- /src/common/controller.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "github.com/go-gorp/gorp" 6 | "go.mongodb.org/mongo-driver/mongo" 7 | ) 8 | 9 | type ControllerConstructor struct { 10 | Collection *mongo.Collection 11 | Repository *gorp.DbMap 12 | } 13 | 14 | type ControllerInterface interface { 15 | RegisterRoutes(*gin.Engine) 16 | } 17 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:alpine as builder 2 | 3 | WORKDIR /go/app 4 | 5 | COPY src ./src 6 | COPY docs ./docs 7 | COPY go.mod ./go.mod 8 | COPY go.sum ./go.sum 9 | 10 | RUN apk update && apk add --no-cache git 11 | RUN go mod download 12 | 13 | RUN go build -o ./src/build ./src 14 | 15 | FROM alpine:latest 16 | RUN apk --no-cache add ca-certificates 17 | 18 | WORKDIR /root/ 19 | 20 | COPY --from=builder /go/app/src/build . 21 | 22 | EXPOSE $PORT 23 | 24 | ENTRYPOINT ["./build"] -------------------------------------------------------------------------------- /src/middleware/jwt.middleware.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | 7 | "go-boilerplate/src/utils" 8 | 9 | "github.com/gin-gonic/gin" 10 | ) 11 | 12 | func JwtAuthMiddleware() gin.HandlerFunc { 13 | return func(ctx *gin.Context) { 14 | 15 | err := utils.TokenValid(ctx) 16 | 17 | if err != nil { 18 | ctx.JSON(http.StatusUnauthorized, gin.H{"Unauthorized": "Authentication required"}) 19 | fmt.Println(err) 20 | ctx.Abort() 21 | return 22 | } 23 | ctx.Next() 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Utilisez IntelliSense pour en savoir plus sur les attributs possibles. 3 | // Pointez pour afficher la description des attributs existants. 4 | // Pour plus d'informations, visitez : https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "Launch", 9 | "type": "go", 10 | "request": "launch", 11 | "mode": "debug", 12 | "remotePath": "", 13 | "program": "${workspaceFolder}/src/main.go", 14 | "env": {}, 15 | "args": [], 16 | "showLog": true 17 | } 18 | ] 19 | } -------------------------------------------------------------------------------- /src/models/cache.model.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import ( 4 | "go-boilerplate/src/common" 5 | "go-boilerplate/src/core/db" 6 | "time" 7 | ) 8 | 9 | func CacheModel() *BaseModel { 10 | mod := &BaseModel{ 11 | ModelConstructor: &common.ModelConstructor{ 12 | Redis: db.GetRedis(), 13 | }, 14 | } 15 | 16 | return mod 17 | } 18 | 19 | func (mod *BaseModel) GetCache(key string) string { 20 | val, err := mod.Redis.Get(key).Result() 21 | 22 | if err != nil { 23 | return "" 24 | } 25 | 26 | return val 27 | } 28 | 29 | func (mod *BaseModel) SetCache(key string, value interface{}, ttl time.Duration) { 30 | err := mod.Redis.Set(key, value, ttl).Err() 31 | if err != nil { 32 | panic(err) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "go-boilerplate/src/config" 6 | "go-boilerplate/src/controllers" 7 | "go-boilerplate/src/core/db" 8 | "go-boilerplate/src/models" 9 | 10 | "github.com/gin-contrib/gzip" 11 | "github.com/gin-gonic/gin" 12 | ) 13 | 14 | func main() { 15 | r := gin.Default() 16 | 17 | db.InitRedis(1) 18 | db.InitMongoDB() 19 | db.InitPostgresDB() 20 | db.InitGorm() 21 | 22 | models.MigrateUsers() 23 | 24 | // register controllers 25 | controllers.UsersController(r) 26 | controllers.AuthController(r) 27 | controllers.ArticlesController(r) 28 | controllers.ProductsController(r) 29 | controllers.SwaggersController(r) 30 | 31 | r.Use(gzip.Gzip(gzip.DefaultCompression)) 32 | 33 | // running 34 | r.Run(fmt.Sprintf(":%s", config.LoadConfig("PORT"))) 35 | } 36 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture-specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | # IDE files 23 | .idea/ 24 | *.swp 25 | *.swo 26 | 27 | # Ignore all files in .vscode directory 28 | .vscode/* 29 | 30 | # Do not ignore launch.json in .vscode directory 31 | !.vscode/launch.json 32 | 33 | # macOS specific 34 | .DS_Store 35 | 36 | # Windows specific 37 | Thumbs.db 38 | 39 | # Go specific 40 | bin/ 41 | pkg/ 42 | vendor/ 43 | 44 | # Log files 45 | *.log 46 | 47 | # Dependency directories (remove the comment below if you want to check in your dependencies) 48 | # vendor/ 49 | 50 | # JetBrains IDE 51 | .idea/ 52 | 53 | # Binary files produced by `go build` 54 | *.exe 55 | *.out 56 | 57 | tmp/ 58 | 59 | .env -------------------------------------------------------------------------------- /src/core/db/gorm.go: -------------------------------------------------------------------------------- 1 | package db 2 | 3 | import ( 4 | "fmt" 5 | "go-boilerplate/src/config" 6 | "log" 7 | 8 | "gorm.io/driver/postgres" 9 | "gorm.io/gorm" 10 | ) 11 | 12 | var gormdb *gorm.DB 13 | 14 | func connectGorm() (*gorm.DB, error) { 15 | dsn := fmt.Sprintf( 16 | "host=%s port=%s user=%s dbname=%s password=%s sslmode=disable", config.LoadConfig("POSTGRES_HOST"), config.LoadConfig("POSTGRES_PORT"), config.LoadConfig("POSTGRES_USER"), config.LoadConfig("POSTGRES_DB_NAME"), config.LoadConfig("POSTGRES_PASSWORD")) 17 | 18 | fmt.Println("connected to " + dsn) 19 | return gorm.Open(postgres.Open(dsn), &gorm.Config{}) 20 | } 21 | 22 | func InitGorm() *gorm.DB { 23 | if gormdb != nil { 24 | return gormdb 25 | } 26 | 27 | var err error 28 | gormdb, err = connectGorm() 29 | 30 | if err != nil { 31 | log.Fatalf("Failed to connect to database: %v", err) 32 | } 33 | 34 | return gormdb 35 | } 36 | 37 | func GetGorm() *gorm.DB { 38 | 39 | return gormdb 40 | } 41 | -------------------------------------------------------------------------------- /src/core/db/redis.go: -------------------------------------------------------------------------------- 1 | package db 2 | 3 | import ( 4 | "fmt" 5 | "go-boilerplate/src/config" 6 | 7 | _redis "github.com/go-redis/redis/v7" 8 | ) 9 | 10 | var RedisClient *_redis.Client 11 | 12 | //InitRedis ... 13 | func InitRedis(selectDB ...int) { 14 | 15 | var redisHost = config.LoadConfig("REDIS_HOST") 16 | var redisPassword = config.LoadConfig("REDIS_PASSWORD") 17 | 18 | RedisClient = _redis.NewClient(&_redis.Options{ 19 | Addr: redisHost, 20 | Password: redisPassword, 21 | DB: selectDB[0], 22 | // DialTimeout: 10 * time.Second, 23 | // ReadTimeout: 30 * time.Second, 24 | // WriteTimeout: 30 * time.Second, 25 | // PoolSize: 10, 26 | // PoolTimeout: 30 * time.Second, 27 | // IdleTimeout: 500 * time.Millisecond, 28 | // IdleCheckFrequency: 500 * time.Millisecond, 29 | // TLSConfig: &tls.Config{ 30 | // InsecureSkipVerify: true, 31 | // }, 32 | }) 33 | 34 | fmt.Println("Connected to Redis!") 35 | } 36 | 37 | //GetRedis ... 38 | func GetRedis() *_redis.Client { 39 | return RedisClient 40 | } 41 | -------------------------------------------------------------------------------- /src/core/db/mongo.go: -------------------------------------------------------------------------------- 1 | package db 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "go-boilerplate/src/config" 7 | "log" 8 | 9 | "go.mongodb.org/mongo-driver/mongo" 10 | "go.mongodb.org/mongo-driver/mongo/options" 11 | ) 12 | 13 | var client *mongo.Database 14 | 15 | func connectMongoDb(connectionString string) (*mongo.Database, error) { 16 | clientOptions := options.Client().ApplyURI(connectionString) 17 | client, err := mongo.Connect(context.TODO(), clientOptions) 18 | if err != nil { 19 | return nil, err 20 | } 21 | 22 | err = client.Ping(context.TODO(), nil) 23 | if err != nil { 24 | log.Fatal(err) 25 | } 26 | 27 | db := client.Database(config.LoadConfig(("MONGO_DB"))) 28 | 29 | fmt.Println("Connected to MongoDB!") 30 | 31 | return db, nil 32 | } 33 | 34 | func InitMongoDB() *mongo.Database { 35 | var error error 36 | client, error = connectMongoDb(config.LoadConfig(("MONGO_CONNECTION_STRING"))) 37 | 38 | if error != nil { 39 | log.Fatal(error) 40 | } 41 | 42 | return client 43 | } 44 | 45 | func GetMongoDb() *mongo.Database { 46 | return client 47 | } 48 | -------------------------------------------------------------------------------- /src/utils/http.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "net/http" 5 | "strconv" 6 | 7 | "github.com/gin-gonic/gin" 8 | ) 9 | 10 | func GetBody[ValidatorStruct any](ctx *gin.Context) ValidatorStruct { 11 | var body ValidatorStruct 12 | 13 | if err := ctx.ShouldBindJSON(&body); err != nil { 14 | ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) 15 | } 16 | 17 | return body 18 | } 19 | 20 | func GetParam[ValidatorStruct any](ctx *gin.Context) ValidatorStruct { 21 | var param ValidatorStruct 22 | 23 | if err := ctx.ShouldBindUri(¶m); err != nil { 24 | ctx.JSON(http.StatusBadRequest, gin.H{"msg": err}) 25 | } 26 | 27 | return param 28 | } 29 | 30 | func GetQueryInt(ctx *gin.Context, Key string, defaultValue int) int { 31 | query := ctx.Request.URL.Query() 32 | 33 | value, err := strconv.Atoi(query.Get(Key)) 34 | 35 | if err != nil { 36 | value = defaultValue 37 | } 38 | 39 | return value 40 | } 41 | 42 | func GetQueryString(ctx *gin.Context, Key string, defaultValue string) string { 43 | value := ctx.Request.URL.Query().Get(Key) 44 | 45 | return value 46 | } 47 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | web: 3 | build: . 4 | ports: 5 | - "8000:8000" 6 | environment: 7 | PORT: 8000 8 | GIN_MODE: release 9 | MONGO_CONNECTION_STRING: mongodb://mongo:mongo@mongo:27017/ 10 | MONGO_DB: go 11 | POSTGRES_HOST: postgres 12 | POSTGRES_PORT: 5432 13 | POSTGRES_USER: postgres 14 | POSTGRES_PASSWORD: postgres 15 | POSTGRES_DB_NAME: postgres 16 | REDIS_HOST: redis:6379 17 | REDIS_PASSWORD: 18 | networks: 19 | - go-network 20 | redis: 21 | hostname: redis 22 | image: "redis:latest" 23 | networks: 24 | - go-network 25 | ports: 26 | - 6379:6379 27 | mongo: 28 | hostname: mongo 29 | image: "mongo:latest" 30 | environment: 31 | MONGO_INITDB_ROOT_USERNAME: mongo 32 | MONGO_INITDB_ROOT_PASSWORD: mongo 33 | ports: 34 | - 27017:27017 35 | networks: 36 | - go-network 37 | postgres: 38 | hostname: postgres 39 | image: "postgres:latest" 40 | environment: 41 | POSTGRES_PASSWORD: postgres 42 | ports: 43 | - 5432:5432 44 | networks: 45 | - go-network 46 | networks: 47 | go-network: 48 | driver: bridge -------------------------------------------------------------------------------- /src/core/db/postgres.go: -------------------------------------------------------------------------------- 1 | package db 2 | 3 | import ( 4 | "database/sql" 5 | "fmt" 6 | "go-boilerplate/src/config" 7 | "log" 8 | 9 | "github.com/go-gorp/gorp" 10 | _ "github.com/lib/pq" //import postgres 11 | ) 12 | 13 | //DB ... 14 | type DB struct { 15 | *sql.DB 16 | } 17 | 18 | var db *gorp.DbMap 19 | 20 | func ConnectPostgresDB(dataSourceName string) (*gorp.DbMap, error) { 21 | db, err := sql.Open("postgres", dataSourceName) 22 | if err != nil { 23 | return nil, err 24 | } 25 | 26 | if err = db.Ping(); err != nil { 27 | return nil, err 28 | } 29 | 30 | fmt.Println("Connected to Postgres!") 31 | 32 | dbmap := &gorp.DbMap{Db: db, Dialect: gorp.PostgresDialect{}} 33 | 34 | return dbmap, nil 35 | } 36 | 37 | func InitPostgresDB() { 38 | dbinfo := fmt.Sprintf("host=%s port=%s user=%s password=%s dbname=%s sslmode=disable", config.LoadConfig("POSTGRES_HOST"), config.LoadConfig("POSTGRES_PORT"), config.LoadConfig("POSTGRES_USER"), config.LoadConfig("POSTGRES_PASSWORD"), config.LoadConfig("POSTGRES_DB_NAME")) 39 | 40 | fmt.Println(dbinfo) 41 | var err error 42 | db, err = ConnectPostgresDB(dbinfo) 43 | if err != nil { 44 | log.Fatal(err) 45 | } 46 | 47 | } 48 | 49 | func GetPostgresDB() *gorp.DbMap { 50 | return db 51 | } 52 | -------------------------------------------------------------------------------- /src/controllers/swagger.controllers.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import ( 4 | "go-boilerplate/docs" 5 | "go-boilerplate/src/common" 6 | "net/http" 7 | 8 | "github.com/gin-gonic/gin" 9 | swaggerfiles "github.com/swaggo/files" 10 | ginSwagger "github.com/swaggo/gin-swagger" 11 | ) 12 | 13 | func SwaggersController(r *gin.Engine) *BaseController { 14 | ctr := &BaseController{ 15 | ControllerConstructor: &common.ControllerConstructor{}, 16 | } 17 | 18 | ctr.SwaggersRoutes(r) 19 | 20 | return ctr 21 | } 22 | 23 | // @BasePath / 24 | 25 | // PingExample godoc 26 | // @Summary health check 27 | // @Schemes 28 | // @Description do health check 29 | // @Tags example 30 | // @Accept json 31 | // @Produce json 32 | // @securityDefinitions.apikey jwtTokenAuth 33 | // @in header 34 | // @name 35 | // @Success 200 {string} Healthy 36 | // @Router /healthcheck [get] 37 | func HealthCheck(g *gin.Context) { 38 | g.JSON(http.StatusOK, "Healthy") 39 | } 40 | 41 | func (ctr *BaseController) SwaggersRoutes(r *gin.Engine) { 42 | docs.SwaggerInfo.BasePath = "/" 43 | 44 | r.GET("/healthcheck", HealthCheck) 45 | 46 | ginSwagger.WrapHandler(swaggerfiles.Handler, 47 | ginSwagger.URL("/swagger/doc.json"), 48 | ginSwagger.DefaultModelsExpandDepth(-1)) 49 | 50 | r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerfiles.Handler)) 51 | } 52 | -------------------------------------------------------------------------------- /src/utils/token.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "fmt" 5 | "go-boilerplate/src/config" 6 | "strconv" 7 | "strings" 8 | 9 | "github.com/gin-gonic/gin" 10 | "github.com/golang-jwt/jwt/v4" 11 | ) 12 | 13 | func TokenValid(c *gin.Context) error { 14 | tokenString := ExtractToken(c) 15 | _, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) { 16 | if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { 17 | return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"]) 18 | } 19 | return []byte(config.LoadConfig("API_SECRET")), nil 20 | }) 21 | if err != nil { 22 | return err 23 | } 24 | return nil 25 | } 26 | 27 | func ExtractToken(c *gin.Context) string { 28 | token := c.Query("token") 29 | if token != "" { 30 | return token 31 | } 32 | bearerToken := c.Request.Header.Get("Authorization") 33 | if len(strings.Split(bearerToken, " ")) == 2 { 34 | return strings.Split(bearerToken, " ")[1] 35 | } 36 | return "" 37 | } 38 | 39 | func ExtractTokenID(c *gin.Context) (uint, error) { 40 | 41 | tokenString := ExtractToken(c) 42 | token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) { 43 | if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { 44 | return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"]) 45 | } 46 | return []byte(config.LoadConfig("API_SECRET")), nil 47 | }) 48 | if err != nil { 49 | return 0, err 50 | } 51 | claims, ok := token.Claims.(jwt.MapClaims) 52 | if ok && token.Valid { 53 | uid, err := strconv.ParseUint(fmt.Sprintf("%.0f", claims["id"]), 10, 32) 54 | if err != nil { 55 | return 0, err 56 | } 57 | return uint(uid), nil 58 | } 59 | return 0, nil 60 | } 61 | -------------------------------------------------------------------------------- /src/controllers/auth.controllers.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import ( 4 | "go-boilerplate/src/common" 5 | "go-boilerplate/src/middleware" 6 | "go-boilerplate/src/models" 7 | "go-boilerplate/src/utils" 8 | "net/http" 9 | 10 | "github.com/gin-gonic/gin" 11 | ) 12 | 13 | func AuthController(r *gin.Engine) *BaseController { 14 | ctr := &BaseController{ 15 | ControllerConstructor: &common.ControllerConstructor{ 16 | // Collection: core.InitMongoDB().Collection("testUsersCollection"), 17 | }, 18 | } 19 | 20 | ctr.AuthRoutes(r) 21 | 22 | return ctr 23 | } 24 | 25 | // @BasePath /users 26 | func (ctr *BaseController) AuthRoutes(r *gin.Engine) { 27 | api := r.Group("/auth") 28 | { 29 | api.POST("/login", func(ctx *gin.Context) { 30 | login(ctx, ctr) 31 | }) 32 | } 33 | api.Use(middleware.JwtAuthMiddleware()) 34 | { 35 | api.GET("/profile", func(ctx *gin.Context) { 36 | profile(ctx, ctr) 37 | }) 38 | } 39 | } 40 | 41 | // @Summary Login 42 | // @Tags auth 43 | // @Produce json 44 | // @Success 200 {array} models.User 45 | // @Router /auth/login [post] 46 | // @Param request body models.LoginInput true "body" 47 | func login(ctx *gin.Context, ctr *BaseController) { 48 | input := utils.GetBody[models.LoginInput](ctx) 49 | 50 | token, err := models.AuthModel().LoginCheck(input.Username, input.Password) 51 | 52 | if err != nil || token == "" { 53 | ctx.JSON(http.StatusBadRequest, gin.H{"error": "The username or password is not correct"}) 54 | return 55 | } 56 | 57 | ctx.JSON(http.StatusOK, gin.H{"token": token}) 58 | } 59 | 60 | // @Summary Profile 61 | // @Tags auth 62 | // @Produce json 63 | // @Success 200 {array} models.User 64 | // @Router /auth/profile [get] 65 | // @Security jwtTokenAuth 66 | func profile(ctx *gin.Context, ctr *BaseController) { 67 | user, err := models.AuthModel().CurrentUser(ctx) 68 | 69 | if err != nil { 70 | ctx.JSON(http.StatusUnauthorized, err) 71 | } 72 | 73 | ctx.JSON(http.StatusOK, user) 74 | } 75 | -------------------------------------------------------------------------------- /src/models/auth.model.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import ( 4 | "fmt" 5 | "go-boilerplate/src/common" 6 | "go-boilerplate/src/config" 7 | "go-boilerplate/src/core/db" 8 | "go-boilerplate/src/utils" 9 | "strconv" 10 | "time" 11 | 12 | "github.com/gin-gonic/gin" 13 | "github.com/golang-jwt/jwt" 14 | "golang.org/x/crypto/bcrypt" 15 | ) 16 | 17 | func AuthModel() *BaseModel { 18 | mod := &BaseModel{ 19 | ModelConstructor: &common.ModelConstructor{ 20 | Gorm: db.GetGorm(), 21 | }, 22 | } 23 | 24 | return mod 25 | } 26 | 27 | type LoginInput struct { 28 | Username string `json:"username" binding:"required"` 29 | Password string `json:"password" binding:"required"` 30 | } 31 | 32 | func (mod *BaseModel) CurrentUser(ctx *gin.Context) (User, error) { 33 | var user User 34 | 35 | user_id, err := utils.ExtractTokenID(ctx) 36 | fmt.Println(user) 37 | 38 | if err != nil { 39 | return user, err 40 | } 41 | 42 | user = UsersModel().GetOneUser(user_id) 43 | 44 | if err != nil { 45 | return user, err 46 | } 47 | 48 | return user, nil 49 | } 50 | 51 | func GenerateToken(user User) (string, error) { 52 | 53 | tokenLifespan, err := strconv.Atoi(config.LoadConfig("TOKEN_HOUR_LIFESPAN")) 54 | 55 | if err != nil { 56 | return "", err 57 | } 58 | 59 | claims := jwt.MapClaims{} 60 | claims["authorized"] = true 61 | claims["id"] = user.ID 62 | claims["exp"] = time.Now().Add(time.Hour * time.Duration(tokenLifespan)).Unix() 63 | token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) 64 | 65 | return token.SignedString([]byte(config.LoadConfig("API_SECRET"))) 66 | 67 | } 68 | 69 | func (mod *BaseModel) LoginCheck(username, password string) (string, error) { 70 | var err error 71 | 72 | user := User{} 73 | 74 | err = mod.Gorm.Limit(1).Where("username=?", username).Find(&user).Error 75 | 76 | if err != nil || user.ID == 0 { 77 | return "", err 78 | } 79 | 80 | err = VerifyPassword(password, user.Password) 81 | 82 | if err != nil && err == bcrypt.ErrMismatchedHashAndPassword { 83 | return "", err 84 | } 85 | 86 | token, err := GenerateToken(user) 87 | 88 | if err != nil { 89 | return "", err 90 | } 91 | 92 | return token, nil 93 | } 94 | -------------------------------------------------------------------------------- /src/models/article.model.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import ( 4 | "errors" 5 | "go-boilerplate/src/common" 6 | "go-boilerplate/src/core/db" 7 | "time" 8 | ) 9 | 10 | func ArticlesModel() *BaseModel { 11 | mod := &BaseModel{ 12 | ModelConstructor: &common.ModelConstructor{ 13 | Repository: db.GetPostgresDB(), 14 | }, 15 | } 16 | 17 | return mod 18 | } 19 | 20 | // models definitions 21 | type Article struct { 22 | ID int64 `db:"id, primarykey, autoincrement" json:"id"` 23 | Title string `db:"title" json:"title"` 24 | Content string `db:"content" json:"content"` 25 | UpdatedAt time.Time `db:"updated_at" json:"updated_at"` 26 | CreatedAt time.Time `db:"created_at" json:"created_at"` 27 | } 28 | 29 | type CreateArticleForm struct { 30 | Title string `form:"title" json:"title" binding:"required,min=3,max=100"` 31 | Content string `form:"content" json:"content" binding:"required,min=3,max=1000"` 32 | } 33 | 34 | type UpdateArticleForm struct { 35 | Title string `form:"title" json:"title"` 36 | Content string `form:"content" json:"content"` 37 | } 38 | 39 | type FindArticleForm struct { 40 | ID int64 `form:"id" json:"id" binding:"required"` 41 | } 42 | 43 | //ArticleModel ... 44 | type ArticleModel struct{} 45 | 46 | func (mod *BaseModel) GetAllArticles() (articles []Article, err error) { 47 | _, err = mod.Repository.Select(&articles, "select a.id, a.title, a.content, a.updated_at, a.created_at from public.articles a order by a.id desc") 48 | return articles, err 49 | } 50 | 51 | func (mod *BaseModel) GetOneArticle(id int64) (article Article, err error) { 52 | err = mod.Repository.SelectOne(&article, "SELECT a.id, a.title, a.content, a.updated_at, a.created_at FROM public.articles a WHERE a.id=$1 LIMIT 1", id) 53 | return article, err 54 | } 55 | 56 | func (mod *BaseModel) CreateArticle(form CreateArticleForm) (articleID int64, err error) { 57 | err = mod.Repository.QueryRow("INSERT INTO public.articles (title, content) VALUES($1, $2) RETURNING id", form.Title, form.Content).Scan(&articleID) 58 | return articleID, err 59 | } 60 | 61 | func (mod *BaseModel) UpdateArticle(id int64, form UpdateArticleForm) (err error) { 62 | _, err = mod.GetOneArticle(id) 63 | if err != nil { 64 | return err 65 | } 66 | 67 | operation, err := mod.Repository.Exec("UPDATE public.articles SET title=$2, content=$3 WHERE id=$1", id, form.Title, form.Content) 68 | if err != nil { 69 | return err 70 | } 71 | 72 | success, _ := operation.RowsAffected() 73 | if success == 0 { 74 | return errors.New("updated 0 records") 75 | } 76 | 77 | return err 78 | } 79 | 80 | func (mod *BaseModel) DeleteArticle(id int64) (err error) { 81 | operation, err := mod.Repository.Exec("DELETE FROM public.articles WHERE id=$1", id) 82 | if err != nil { 83 | return err 84 | } 85 | 86 | success, _ := operation.RowsAffected() 87 | if success == 0 { 88 | return errors.New("no records were deleted") 89 | } 90 | 91 | return err 92 | } 93 | -------------------------------------------------------------------------------- /src/models/user.model.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import ( 4 | "fmt" 5 | "go-boilerplate/src/common" 6 | "go-boilerplate/src/core/db" 7 | "time" 8 | 9 | "golang.org/x/crypto/bcrypt" 10 | ) 11 | 12 | func UsersModel() *BaseModel { 13 | mod := &BaseModel{ 14 | ModelConstructor: &common.ModelConstructor{ 15 | Gorm: db.GetGorm(), 16 | }, 17 | } 18 | 19 | return mod 20 | } 21 | 22 | func MigrateUsers() { 23 | db.GetGorm().AutoMigrate(&User{}) 24 | } 25 | 26 | // models definitions 27 | 28 | type User struct { 29 | ID uint `gorm:"autoIncrement,primaryKey"` 30 | Username string `gorm:"not null,index,unique"` 31 | Email *string 32 | FirstName string `gorm:"default:''"` 33 | LastName string `gorm:"default:''"` 34 | Password string `gorm:"not null"` 35 | CreatedAt time.Time 36 | UpdatedAt time.Time 37 | } 38 | 39 | type CreateUserForm struct { 40 | Username string `form:"Username" json:"Username" binding:"required"` 41 | Email string `form:"Email" json:"Email" binding:"required"` 42 | FirstName string `form:"FirstName" json:"FirstName" binding:"required"` 43 | LastName string `form:"LastName" json:"LastName" binding:"required"` 44 | Password string `form:"Password" json:"Password" binding:"required"` 45 | } 46 | 47 | type UsersResponse struct { 48 | Users []User `json:"users"` 49 | Count int `json:"count"` 50 | } 51 | type UsersFindParam struct { 52 | ID uint `uri:"id" binding:"required"` 53 | } 54 | 55 | func VerifyPassword(password, hashedPassword string) error { 56 | return bcrypt.CompareHashAndPassword([]byte(hashedPassword), []byte(password)) 57 | } 58 | 59 | // models methods 60 | func (mod *BaseModel) GetOneUser(userId uint) User { 61 | var user User 62 | 63 | result := mod.Gorm.Limit(1).Where("id = ?", userId).Find(&user) 64 | 65 | if result.Error != nil { 66 | fmt.Println(result.Error) 67 | return user 68 | } 69 | 70 | return user 71 | } 72 | 73 | func (mod *BaseModel) GetAllUsers(limit int, skip int, search string) ([]User, int) { 74 | var users []User 75 | var count int 76 | 77 | search = "%" + search + "%" 78 | 79 | result := mod.Gorm.Limit(limit).Offset(skip).Where("email ilike ? OR username ilike ? OR first_name ilike ? OR last_name ilike ?", search, search, search, search).Find(&users) 80 | 81 | mod.Gorm.Raw("SELECT COUNT(id) FROM users WHERE email ilike ? OR username ilike ? OR first_name ilike ? OR last_name ilike ?", search, search, search, search).Scan(&count) 82 | 83 | if result.Error != nil { 84 | fmt.Println(result.Error) 85 | return nil, 0 86 | } 87 | 88 | return users, count 89 | } 90 | 91 | func (mod *BaseModel) UpdateUser(param UsersFindParam, body User) User { 92 | body.ID = param.ID 93 | 94 | result := mod.Gorm.Save(&body) 95 | 96 | if result.Error != nil { 97 | fmt.Println(result.Error) 98 | } 99 | 100 | return body 101 | } 102 | 103 | func (mod *BaseModel) CreateUser(body User) User { 104 | hashedPassword, _ := bcrypt.GenerateFromPassword([]byte(body.Password), bcrypt.DefaultCost) 105 | body.Password = string(hashedPassword) 106 | 107 | result := mod.Gorm.Create(&body) 108 | 109 | if result.Error != nil { 110 | fmt.Println(result.Error) 111 | } 112 | 113 | return body 114 | } 115 | 116 | func (mod *BaseModel) DeleteUser(param UsersFindParam) bool { 117 | var user User 118 | user.ID = param.ID 119 | 120 | result := mod.Gorm.Delete(&user) 121 | 122 | fmt.Println(result) 123 | if result.Error != nil || result.RowsAffected == 0 { 124 | return false 125 | } 126 | 127 | return true 128 | } 129 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module go-boilerplate 2 | 3 | go 1.18 4 | 5 | require ( 6 | github.com/KyleBanks/depth v1.2.1 // indirect 7 | github.com/PuerkitoBio/purell v1.2.0 // indirect 8 | github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect 9 | github.com/bytedance/sonic v1.10.1 // indirect 10 | github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect 11 | github.com/chenzhuoyu/iasm v0.9.0 // indirect 12 | github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect 13 | github.com/gabriel-vasile/mimetype v1.4.2 // indirect 14 | github.com/ghodss/yaml v1.0.0 // indirect 15 | github.com/gin-contrib/gzip v0.0.6 // indirect 16 | github.com/gin-contrib/sse v0.1.0 // indirect 17 | github.com/gin-gonic/gin v1.9.1 // indirect 18 | github.com/go-gorp/gorp v2.2.0+incompatible // indirect 19 | github.com/go-openapi/jsonpointer v0.20.0 // indirect 20 | github.com/go-openapi/jsonreference v0.20.2 // indirect 21 | github.com/go-openapi/spec v0.20.9 // indirect 22 | github.com/go-openapi/swag v0.22.4 // indirect 23 | github.com/go-playground/locales v0.14.1 // indirect 24 | github.com/go-playground/universal-translator v0.18.1 // indirect 25 | github.com/go-playground/validator/v10 v10.15.3 // indirect 26 | github.com/go-redis/redis v6.15.9+incompatible // indirect 27 | github.com/go-redis/redis/v7 v7.4.1 // indirect 28 | github.com/goccy/go-json v0.10.2 // indirect 29 | github.com/golang-jwt/jwt v3.2.2+incompatible 30 | github.com/golang-jwt/jwt/v4 v4.5.0 // indirect 31 | github.com/golang/snappy v0.0.1 // indirect 32 | github.com/jackc/pgpassfile v1.0.0 // indirect 33 | github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect 34 | github.com/jackc/pgx/v5 v5.3.1 // indirect 35 | github.com/jinzhu/inflection v1.0.0 // indirect 36 | github.com/jinzhu/now v1.1.5 // indirect 37 | github.com/joho/godotenv v1.5.1 // indirect 38 | github.com/josharian/intern v1.0.0 // indirect 39 | github.com/json-iterator/go v1.1.12 // indirect 40 | github.com/klauspost/compress v1.13.6 // indirect 41 | github.com/klauspost/cpuid/v2 v2.2.5 // indirect 42 | github.com/leodido/go-urn v1.2.4 // indirect 43 | github.com/lib/pq v1.10.9 // indirect 44 | github.com/mailru/easyjson v0.7.7 // indirect 45 | github.com/mattn/go-isatty v0.0.19 // indirect 46 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 47 | github.com/modern-go/reflect2 v1.0.2 // indirect 48 | github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe // indirect 49 | github.com/pelletier/go-toml/v2 v2.1.0 // indirect 50 | github.com/russross/blackfriday/v2 v2.1.0 // indirect 51 | github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect 52 | github.com/swaggo/files v1.0.1 // indirect 53 | github.com/swaggo/gin-swagger v1.3.2 // indirect 54 | github.com/swaggo/swag v1.16.2 // indirect 55 | github.com/twitchyliquid64/golang-asm v0.15.1 // indirect 56 | github.com/ugorji/go/codec v1.2.11 // indirect 57 | github.com/urfave/cli/v2 v2.25.7 // indirect 58 | github.com/xdg-go/pbkdf2 v1.0.0 // indirect 59 | github.com/xdg-go/scram v1.1.2 // indirect 60 | github.com/xdg-go/stringprep v1.0.4 // indirect 61 | github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect 62 | github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect 63 | go.mongodb.org/mongo-driver v1.12.1 // indirect 64 | golang.org/x/arch v0.5.0 // indirect 65 | golang.org/x/crypto v0.13.0 // indirect 66 | golang.org/x/net v0.15.0 // indirect 67 | golang.org/x/sync v0.3.0 // indirect 68 | golang.org/x/sys v0.12.0 // indirect 69 | golang.org/x/text v0.13.0 // indirect 70 | golang.org/x/tools v0.13.0 // indirect 71 | google.golang.org/protobuf v1.31.0 // indirect 72 | gopkg.in/yaml.v2 v2.4.0 // indirect 73 | gopkg.in/yaml.v3 v3.0.1 // indirect 74 | gorm.io/driver/postgres v1.5.2 // indirect 75 | gorm.io/gorm v1.25.4 // indirect 76 | gorm.io/plugin/soft_delete v1.2.1 // indirect 77 | sigs.k8s.io/yaml v1.3.0 // indirect 78 | ) 79 | -------------------------------------------------------------------------------- /src/models/product.model.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "go-boilerplate/src/common" 7 | "go-boilerplate/src/core/db" 8 | "time" 9 | 10 | "go.mongodb.org/mongo-driver/bson" 11 | "go.mongodb.org/mongo-driver/bson/primitive" 12 | "go.mongodb.org/mongo-driver/mongo" 13 | "go.mongodb.org/mongo-driver/mongo/options" 14 | ) 15 | 16 | func ProductsModel() *BaseModel { 17 | mod := &BaseModel{ 18 | ModelConstructor: &common.ModelConstructor{ 19 | Collection: db.GetMongoDb().Collection("ProductsCollection"), 20 | }, 21 | } 22 | 23 | return mod 24 | } 25 | 26 | // models definitions 27 | type Product struct { 28 | ID primitive.ObjectID `bson:"_id"` 29 | Title string `db:"title" json:"title"` 30 | Content string `db:"content" json:"content"` 31 | Price int `db:"price" json:"price"` 32 | UpdatedAt time.Time `db:"updated_at" json:"updated_at"` 33 | CreatedAt time.Time `db:"created_at" json:"created_at"` 34 | } 35 | 36 | type CreateProductForm struct { 37 | Title string `form:"title" json:"title" binding:"required,min=3,max=100"` 38 | Content string `form:"content" json:"content" binding:"required,min=3,max=1000"` 39 | Price int `form:"price" json:"price" binding:"required"` 40 | } 41 | 42 | type UpdateProductForm struct { 43 | Title string `form:"title" json:"title"` 44 | Content string `form:"content" json:"content"` 45 | Price int `form:"price" json:"price"` 46 | } 47 | 48 | type FindProductForm struct { 49 | ID int64 `form:"id" json:"id" binding:"required"` 50 | } 51 | 52 | func (mod *BaseModel) GetAllProducts(limit int64, skip int64, search string) (products []Product, err error) { 53 | var results []Product 54 | opts := options.Find().SetLimit(limit).SetSkip(skip) 55 | 56 | filter := bson.D{{Key: "$or", Value: bson.A{bson.D{{Key: "title", Value: bson.M{"$regex": search}}}, bson.D{{Key: "content", Value: bson.M{"$regex": search}}}}}} 57 | 58 | cur, err := mod.Collection.Find(context.TODO(), filter, opts) 59 | if err != nil { 60 | if err == mongo.ErrNoDocuments { 61 | return 62 | } 63 | panic(err) 64 | } 65 | 66 | if err = cur.All(context.TODO(), &results); err != nil { 67 | panic(err) 68 | } 69 | 70 | count, err := mod.Collection.CountDocuments(context.TODO(), bson.D{}) 71 | if err != nil { 72 | panic(err) 73 | } 74 | fmt.Println(count) 75 | 76 | return results, err 77 | } 78 | 79 | func (mod *BaseModel) GetOneProduct(id string) (product Product, err error) { 80 | var result Product 81 | objID, err := primitive.ObjectIDFromHex(id) 82 | 83 | filter := bson.D{{Key: "_id", Value: objID}} 84 | err = mod.Collection.FindOne(context.TODO(), filter).Decode(&result) 85 | if err != nil { 86 | if err == mongo.ErrNoDocuments { 87 | return 88 | } 89 | panic(err) 90 | } 91 | 92 | return result, err 93 | } 94 | 95 | func (mod *BaseModel) CreateProduct(body CreateProductForm) (productID interface{}, err error) { 96 | result, err := mod.Collection.InsertOne(context.TODO(), bson.M{"title": body.Title, "content": body.Content, "price": body.Price}) 97 | 98 | if err != nil { 99 | return 100 | } 101 | 102 | return result.InsertedID, err 103 | } 104 | 105 | func (mod *BaseModel) UpdateProduct(id string, body UpdateProductForm) (result *mongo.UpdateResult, err error) { 106 | objID, _ := primitive.ObjectIDFromHex(id) 107 | filter := bson.D{{Key: "_id", Value: objID}} 108 | update := bson.D{{Key: "$set", Value: bson.M{"title": body.Title, "content": body.Content, "price": body.Price}}} 109 | 110 | res, err := mod.Collection.UpdateOne(context.TODO(), filter, update) 111 | 112 | if err != nil { 113 | panic(err) 114 | } 115 | 116 | return res, err 117 | } 118 | 119 | func (mod *BaseModel) DeleteProduct(id string) (result *mongo.DeleteResult, err error) { 120 | objID, _ := primitive.ObjectIDFromHex(id) 121 | filter := bson.D{{Key: "_id", Value: objID}} 122 | 123 | res, err := mod.Collection.DeleteOne(context.TODO(), filter) 124 | 125 | if err != nil { 126 | panic(err) 127 | } 128 | 129 | return res, err 130 | } 131 | -------------------------------------------------------------------------------- /src/controllers/users.controllers.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import ( 4 | "fmt" 5 | "go-boilerplate/src/common" 6 | "go-boilerplate/src/models" 7 | "go-boilerplate/src/utils" 8 | "net/http" 9 | 10 | "github.com/gin-gonic/gin" 11 | ) 12 | 13 | func UsersController(r *gin.Engine) *BaseController { 14 | ctr := &BaseController{ 15 | ControllerConstructor: &common.ControllerConstructor{ 16 | // Collection: core.InitMongoDB().Collection("testUsersCollection"), 17 | }, 18 | } 19 | 20 | ctr.UsersRoutes(r) 21 | 22 | return ctr 23 | } 24 | 25 | // @BasePath /users 26 | func (ctr *BaseController) UsersRoutes(r *gin.Engine) { 27 | api := r.Group("/users") 28 | { 29 | api.GET("/", func(ctx *gin.Context) { 30 | getUsers(ctx, ctr) 31 | }) 32 | api.GET("/:id", func(ctx *gin.Context) { 33 | getUser(ctx, ctr) 34 | }) 35 | api.POST("/", func(ctx *gin.Context) { 36 | createUser(ctx, ctr) 37 | }) 38 | api.PUT("/:id", func(ctx *gin.Context) { 39 | updateUser(ctx, ctr) 40 | }) 41 | api.DELETE("/:id", func(ctx *gin.Context) { 42 | deleteUser(ctx, ctr) 43 | }) 44 | } 45 | } 46 | 47 | // @Summary List Of users 48 | // @Tags users 49 | // @Produce json 50 | // @Success 200 {array} models.User 51 | // @Router /users [get] 52 | // @Param limit query int false "limit" Format(limit) 53 | // @Param skip query int false "skip" Format(skip) 54 | // @Param search query string false "search" Format(search) 55 | func getUsers(ctx *gin.Context, ctr *BaseController) { 56 | limit := utils.GetQueryInt(ctx, "limit", 20) 57 | skip := utils.GetQueryInt(ctx, "skip", 0) 58 | search := utils.GetQueryString(ctx, "search", "") 59 | 60 | users, count := models.UsersModel().GetAllUsers(limit, skip, search) 61 | 62 | response := models.UsersResponse{ 63 | Users: users, 64 | Count: count, 65 | } 66 | 67 | ctx.JSON(http.StatusOK, response) 68 | } 69 | 70 | // @Summary Get An User 71 | // @Tags users 72 | // @Produce json 73 | // @Success 200 {object} models.User 74 | // @Router /users/{id} [get] 75 | // @Param id path int false "id" Format(id) 76 | func getUser(ctx *gin.Context, ctr *BaseController) { 77 | param := utils.GetParam[models.UsersFindParam](ctx) 78 | 79 | results := models.UsersModel().GetOneUser(param.ID) 80 | 81 | if results == (models.User{}) { 82 | ctx.JSON(http.StatusNotFound, "User Not Found!") 83 | return 84 | } 85 | ctx.JSON(http.StatusOK, results) 86 | } 87 | 88 | // @Summary Create An User 89 | // @Description Create An User. 90 | // @Tags users 91 | // @Produce json 92 | // @Success 201 {object} models.User 93 | // @Router /users [post] 94 | // @Param request body models.CreateUserForm true "body" 95 | func createUser(ctx *gin.Context, ctr *BaseController) { 96 | body := utils.GetBody[models.User](ctx) 97 | 98 | result := models.UsersModel().CreateUser(body) 99 | 100 | ctx.JSON(http.StatusCreated, result) 101 | } 102 | 103 | // @Summary Update An User 104 | // @Description Update An User. 105 | // @Tags users 106 | // @Produce json 107 | // @Success 201 {object} models.User 108 | // @Router /users/{id} [put] 109 | // @Param id path int false "id" Format(id) 110 | // @Param request body models.CreateUserForm true "body" 111 | func updateUser(ctx *gin.Context, ctr *BaseController) { 112 | param := utils.GetParam[models.UsersFindParam](ctx) 113 | body := utils.GetBody[models.User](ctx) 114 | 115 | fmt.Println(param) 116 | results := models.UsersModel().UpdateUser(models.UsersFindParam{ID: param.ID}, body) 117 | 118 | ctx.JSON(http.StatusCreated, results) 119 | } 120 | 121 | // @Summary Delete An User 122 | // @Description Delete An User. 123 | // @Tags users 124 | // @Produce json 125 | // @Success 200 {object} models.User 126 | // @Router /users/{id} [delete] 127 | // @Param id path int false "id" Format(id) 128 | func deleteUser(ctx *gin.Context, ctr *BaseController) { 129 | param := utils.GetParam[models.UsersFindParam](ctx) 130 | 131 | results := models.UsersModel().DeleteUser(models.UsersFindParam{ID: param.ID}) 132 | 133 | ctx.JSON(http.StatusOK, results) 134 | } 135 | -------------------------------------------------------------------------------- /src/controllers/products.controllers.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import ( 4 | "go-boilerplate/src/common" 5 | "go-boilerplate/src/models" 6 | "go-boilerplate/src/utils" 7 | "net/http" 8 | 9 | "github.com/gin-gonic/gin" 10 | ) 11 | 12 | func ProductsController(r *gin.Engine) *BaseController { 13 | ctr := &BaseController{ 14 | ControllerConstructor: &common.ControllerConstructor{ 15 | // Collection: core.InitMongoDB().Collection("ProductsCollection"), 16 | }, 17 | } 18 | 19 | ctr.ProductsRoutes(r) 20 | 21 | return ctr 22 | } 23 | 24 | // @BasePath /products 25 | func (ctr *BaseController) ProductsRoutes(r *gin.Engine) { 26 | api := r.Group("/products") 27 | { 28 | api.GET("/", func(ctx *gin.Context) { 29 | getProducts(ctx, ctr) 30 | }) 31 | api.GET("/:id", func(ctx *gin.Context) { 32 | getProduct(ctx, ctr) 33 | }) 34 | api.POST("/", func(ctx *gin.Context) { 35 | createProduct(ctx, ctr) 36 | }) 37 | api.PUT("/:id", func(ctx *gin.Context) { 38 | updateProduct(ctx, ctr) 39 | }) 40 | api.DELETE("/:id", func(ctx *gin.Context) { 41 | deleteProduct(ctx, ctr) 42 | }) 43 | } 44 | } 45 | 46 | // @Summary Get All Products 47 | // @Tags products 48 | // @Produce json 49 | // @Success 200 {object} models.Product 50 | // @Router /products [get] 51 | // @Param limit query int false "limit" Format(limit) 52 | // @Param skip query int false "skip" Format(skip) 53 | // @Param search query string false "search" Format(search) 54 | func getProducts(ctx *gin.Context, ctr *BaseController) { 55 | limit := utils.GetQueryInt(ctx, "limit", 20) 56 | skip := utils.GetQueryInt(ctx, "skip", 0) 57 | search := utils.GetQueryString(ctx, "search", "") 58 | 59 | results, err := models.ProductsModel().GetAllProducts(int64(limit), int64(skip), search) 60 | 61 | if err != nil { 62 | ctx.JSON(http.StatusNotAcceptable, gin.H{"message": err.Error()}) 63 | return 64 | } 65 | 66 | ctx.JSON(http.StatusOK, results) 67 | } 68 | 69 | // @Summary Find a product 70 | // @Description Returns the the product with ID. 71 | // @Tags products 72 | // @Produce json 73 | // @Success 200 {object} models.Product 74 | // @Router /products/{id} [get] 75 | // @Param id path string false "id" Format(id) 76 | func getProduct(ctx *gin.Context, ctr *BaseController) { 77 | id := ctx.Param("id") 78 | 79 | results, err := models.ProductsModel().GetOneProduct(id) 80 | 81 | if err != nil { 82 | ctx.JSON(http.StatusNotFound, gin.H{"message": "Product Not Found!"}) 83 | return 84 | } 85 | 86 | ctx.JSON(http.StatusOK, results) 87 | } 88 | 89 | // @Summary Create An Product 90 | // @Description Create An Product. 91 | // @Tags products 92 | // @Produce json 93 | // @Success 200 {object} models.Product 94 | // @Router /products [post] 95 | // @Param request body models.CreateProductForm true "body" 96 | func createProduct(ctx *gin.Context, ctr *BaseController) { 97 | body := utils.GetBody[models.CreateProductForm](ctx) 98 | 99 | results, err := models.ProductsModel().CreateProduct(body) 100 | 101 | if err != nil { 102 | ctx.JSON(http.StatusNotAcceptable, gin.H{"message": err.Error()}) 103 | return 104 | } 105 | 106 | ctx.JSON(http.StatusOK, results) 107 | } 108 | 109 | // @Summary Update An Product 110 | // @Description Update An Product. 111 | // @Tags products 112 | // @Produce json 113 | // @Success 200 {object} models.Product 114 | // @Router /products/{id} [put] 115 | // @Param request body models.CreateProductForm true "body" 116 | // @Param id path string false "id" Format(id) 117 | func updateProduct(ctx *gin.Context, ctr *BaseController) { 118 | id := ctx.Param("id") 119 | 120 | body := utils.GetBody[models.UpdateProductForm](ctx) 121 | 122 | result, err := models.ProductsModel().UpdateProduct(id, body) 123 | if err != nil { 124 | ctx.JSON(http.StatusNotFound, gin.H{"message": err}) 125 | return 126 | } 127 | 128 | ctx.JSON(http.StatusOK, result) 129 | } 130 | 131 | // @Summary Delete An Product 132 | // @Description Delete An Product. 133 | // @Tags products 134 | // @Produce json 135 | // @Success 200 {object} models.Product 136 | // @Router /products/{id} [delete] 137 | // @Param id path string false "id" Format(id) 138 | func deleteProduct(ctx *gin.Context, ctr *BaseController) { 139 | id := ctx.Param("id") 140 | 141 | result, err := models.ProductsModel().DeleteProduct(id) 142 | if err != nil { 143 | ctx.JSON(http.StatusNotFound, gin.H{"message": err}) 144 | return 145 | } 146 | 147 | ctx.JSON(http.StatusOK, result) 148 | } 149 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Go Boilerplate 2 | ![](logo.png) 3 | 4 | A great starting point for building RESTful APIs in Go using Gin framework, connecting to a PostgreSQL database with both pure and gorm, MongoDB database, Redis database. 5 | 6 | ### Features 7 | 8 | - Implements the Clean Architecture pattern for a scalable and maintainable 9 | - Uses the Gin framework for efficient and fast handling of HTTP requests 10 | - Integrates with Gorm TypeOrm for powerful and flexible database operations (https://gorm.io/) 11 | - Integrates with MongoDb database 12 | - Integrates with Redis database 13 | - Uses Go Swag for Generating Rest Docs and Swagger panel (https://github.com/swaggo/swag) 14 | - Uses Air for live reload app (https://github.com/cosmtrek/air) 15 | 16 | ##### Authentication 17 | 18 | - Supports JWT authentication with configurable expiration and issuer, allowing for flexible and secure authentication processes. 19 | 20 | ### Getting Started 21 | 22 | ##### Prerequisites 23 | 24 | - Go version 1.18.1 or higher 25 | 26 | To get up and running with the Go-Boilerplate, follow these simple steps: 27 | 28 | ``` 29 | $ git clone https://github.com/Mehdikarimian/go-boilerplate 30 | $ cd go-boilerplate 31 | $ cp internal/config/.env.example internal/config/.env # create a copy of the example environment file, and also follow configuration steps on the difference section below 32 | $ go src/main.go Or air 33 | ``` 34 | 35 | #### Configuration 36 | Generate Swagger doc files 37 | ``` 38 | $ swag init -d src/ 39 | ``` 40 | ### File Structure 41 | .. 42 | ├── docs # Document for swagger. 43 | ├── src # 44 | │ ├── common # Common Types And Struct. 45 | | │ └── controller.go # Base Controller Structure Type. 46 | │ │ └── model.go # Base Model Structure Type. 47 | | ├── config # Configs 48 | | | └── config.go # Base Config Module and Env Init. 49 | │ └── controllers # Controllers 50 | │ │ └── articles.controllers.go # Article Controller (example). 51 | │ │ └── base.go # Base Controller Structure. 52 | │ │ └── products.controllers.go # Products Controller (example). 53 | │ │ └── swagger.controllers.go # Swagger Controller 54 | │ │ └── users.controllers.go # Users Controller (example). 55 | │ │ └── auth.controllers.go # Auth Controller. 56 | │ │ # 57 | │ └── middleware # Middlewares 58 | │ │ └── jwt.middleware.go # jwt Middlewares. 59 | | | # 60 | │ ├── core # Core Configures 61 | │ │ └── db # Db Configures 62 | │ │ └── gorm.go # Gorm (Golang Typeorm) Configure File 63 | │ │ └── mongo.go # MongoDb Configure File 64 | │ │ └── postgres.go # PostgresSQL Configure File 65 | │ │ └── redis.go # Redis Configure File 66 | │ │ # 67 | │ ├── models # Models 68 | │ │ └── base.go # Base Model Structure. 69 | │ │ └── article.model.go # Article Model (example). 70 | │ │ └── cache.model.go # Cache Model (example). 71 | │ │ └── product.model.go # Product Model (example). 72 | │ │ └── user.model.go # User Model (example). 73 | │ │ └── auth.model.go # Auth Model. 74 | │ │ # 75 | │ ├── utils # Utils. 76 | │ │ └── http.go # Http Utils 77 | │ │ └── token.go # Token Utils 78 | │ │ # 79 | │ ├── main.go # Main File. 80 | │ │ # 81 | ├── .env.example # Enviroment Example File 82 | ├── Dockerfile # Dockerfile 83 | ├── docker-compose.yml # docker compose file 84 | ├── .air.toml # air configure 85 | └── ... -------------------------------------------------------------------------------- /src/controllers/articles.controllers.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import ( 4 | "encoding/json" 5 | "go-boilerplate/src/common" 6 | "go-boilerplate/src/models" 7 | "go-boilerplate/src/utils" 8 | "net/http" 9 | "strconv" 10 | 11 | "github.com/gin-gonic/gin" 12 | ) 13 | 14 | func ArticlesController(r *gin.Engine) *BaseController { 15 | ctr := &BaseController{ 16 | ControllerConstructor: &common.ControllerConstructor{ 17 | // Collection: core.InitMongoDB().Collection("testArticlesCollection"), 18 | }, 19 | } 20 | 21 | ctr.ArticlesRoutes(r) 22 | 23 | return ctr 24 | } 25 | 26 | // @BasePath /articles 27 | func (ctr *BaseController) ArticlesRoutes(r *gin.Engine) { 28 | api := r.Group("/articles") 29 | { 30 | api.GET("/", func(ctx *gin.Context) { 31 | getArticles(ctx, ctr) 32 | }) 33 | api.GET("/:id", func(ctx *gin.Context) { 34 | getArticle(ctx, ctr) 35 | }) 36 | api.POST("/", func(ctx *gin.Context) { 37 | createArticle(ctx, ctr) 38 | }) 39 | api.PUT("/:id", func(ctx *gin.Context) { 40 | updateArticle(ctx, ctr) 41 | }) 42 | api.DELETE("/:id", func(ctx *gin.Context) { 43 | deleteArticle(ctx, ctr) 44 | }) 45 | } 46 | } 47 | 48 | // @Summary Get All Articles 49 | // @Tags articles 50 | // @Produce json 51 | // @Success 200 {object} models.Article 52 | // @Router /articles [get] 53 | func getArticles(ctx *gin.Context, ctr *BaseController) { 54 | results, err := models.ArticlesModel().GetAllArticles() 55 | 56 | if err != nil { 57 | ctx.JSON(http.StatusNotAcceptable, gin.H{"message": err.Error()}) 58 | return 59 | } 60 | 61 | ctx.JSON(http.StatusOK, results) 62 | } 63 | 64 | // @Summary Find a article 65 | // @Description Returns the the article with ID. 66 | // @Tags articles 67 | // @Produce json 68 | // @Success 200 {object} models.Article 69 | // @Router /articles/{id} [get] 70 | // @Param id path int false "id" Format(id) 71 | func getArticle(ctx *gin.Context, ctr *BaseController) { 72 | id := ctx.Param("id") 73 | getID, err := strconv.ParseInt(id, 10, 64) 74 | if getID == 0 || err != nil { 75 | ctx.AbortWithStatusJSON(http.StatusNotFound, gin.H{"Message": "Invalid parameter"}) 76 | return 77 | } 78 | 79 | res := models.CacheModel().GetCache("article_" + strconv.Itoa(int(getID))) 80 | if len(res) > 0 { 81 | var article models.Article 82 | err := json.Unmarshal([]byte(res), &article) 83 | if err != nil { 84 | ctx.AbortWithStatusJSON(http.StatusNotFound, gin.H{"Message": "Invalid Cache"}) 85 | } 86 | 87 | ctx.JSON(http.StatusOK, article) 88 | return 89 | } 90 | 91 | results, err := models.ArticlesModel().GetOneArticle(getID) 92 | 93 | if err != nil { 94 | ctx.JSON(http.StatusNotFound, gin.H{"message": "Article Not Found!"}) 95 | return 96 | } 97 | 98 | bs, _ := json.Marshal(results) 99 | 100 | models.CacheModel().SetCache("article_"+strconv.Itoa(int(getID)), bs, 0) 101 | 102 | ctx.JSON(http.StatusOK, results) 103 | } 104 | 105 | // @Summary Create An Article 106 | // @Description Create An Article. 107 | // @Tags articles 108 | // @Produce json 109 | // @Success 200 {object} models.Article 110 | // @Router /articles [post] 111 | // @Param request body models.CreateArticleForm true "body" 112 | func createArticle(ctx *gin.Context, ctr *BaseController) { 113 | body := utils.GetBody[models.CreateArticleForm](ctx) 114 | 115 | results, err := models.ArticlesModel().CreateArticle(body) 116 | 117 | if err != nil { 118 | ctx.JSON(http.StatusNotAcceptable, gin.H{"message": err.Error()}) 119 | return 120 | } 121 | 122 | ctx.JSON(http.StatusOK, results) 123 | } 124 | 125 | // @Summary Update An Article 126 | // @Description Update An Article. 127 | // @Tags articles 128 | // @Produce json 129 | // @Success 200 {object} models.Article 130 | // @Router /articles/{id} [put] 131 | // @Param request body models.CreateArticleForm true "body" 132 | // @Param id path int false "id" Format(id) 133 | func updateArticle(ctx *gin.Context, ctr *BaseController) { 134 | id := ctx.Param("id") 135 | getID, err := strconv.ParseInt(id, 10, 64) 136 | if getID == 0 || err != nil { 137 | ctx.AbortWithStatusJSON(http.StatusNotFound, gin.H{"Message": "Invalid parameter"}) 138 | return 139 | } 140 | body := utils.GetBody[models.UpdateArticleForm](ctx) 141 | 142 | err = models.ArticlesModel().UpdateArticle(getID, body) 143 | if err != nil { 144 | ctx.JSON(http.StatusNotFound, gin.H{"message": err}) 145 | return 146 | } 147 | 148 | ctx.JSON(http.StatusOK, "Article Updated") 149 | } 150 | 151 | // @Summary Delete An Article 152 | // @Description Delete An Article. 153 | // @Tags articles 154 | // @Produce json 155 | // @Success 200 {object} models.Article 156 | // @Router /articles/{id} [delete] 157 | // @Param id path int false "id" Format(id) 158 | func deleteArticle(ctx *gin.Context, ctr *BaseController) { 159 | id := ctx.Param("id") 160 | getID, err := strconv.ParseInt(id, 10, 64) 161 | if getID == 0 || err != nil { 162 | ctx.AbortWithStatusJSON(http.StatusNotFound, gin.H{"Message": "Invalid parameter"}) 163 | return 164 | } 165 | 166 | err = models.ArticlesModel().DeleteArticle(getID) 167 | if err != nil { 168 | ctx.JSON(http.StatusNotFound, gin.H{"message": err}) 169 | return 170 | } 171 | 172 | ctx.JSON(http.StatusOK, "Article Deleted") 173 | } 174 | -------------------------------------------------------------------------------- /docs/swagger.yaml: -------------------------------------------------------------------------------- 1 | definitions: 2 | models.Article: 3 | properties: 4 | content: 5 | type: string 6 | created_at: 7 | type: string 8 | id: 9 | type: integer 10 | title: 11 | type: string 12 | updated_at: 13 | type: string 14 | type: object 15 | models.CreateArticleForm: 16 | properties: 17 | content: 18 | maxLength: 1000 19 | minLength: 3 20 | type: string 21 | title: 22 | maxLength: 100 23 | minLength: 3 24 | type: string 25 | required: 26 | - content 27 | - title 28 | type: object 29 | models.CreateProductForm: 30 | properties: 31 | content: 32 | maxLength: 1000 33 | minLength: 3 34 | type: string 35 | price: 36 | type: integer 37 | title: 38 | maxLength: 100 39 | minLength: 3 40 | type: string 41 | required: 42 | - content 43 | - price 44 | - title 45 | type: object 46 | models.CreateUserForm: 47 | properties: 48 | Email: 49 | type: string 50 | FirstName: 51 | type: string 52 | LastName: 53 | type: string 54 | Password: 55 | type: string 56 | Username: 57 | type: string 58 | required: 59 | - Email 60 | - FirstName 61 | - LastName 62 | - Password 63 | - Username 64 | type: object 65 | models.LoginInput: 66 | properties: 67 | password: 68 | type: string 69 | username: 70 | type: string 71 | required: 72 | - password 73 | - username 74 | type: object 75 | models.Product: 76 | properties: 77 | content: 78 | type: string 79 | created_at: 80 | type: string 81 | id: 82 | type: string 83 | price: 84 | type: integer 85 | title: 86 | type: string 87 | updated_at: 88 | type: string 89 | type: object 90 | models.User: 91 | properties: 92 | createdAt: 93 | type: string 94 | email: 95 | type: string 96 | firstName: 97 | type: string 98 | id: 99 | type: integer 100 | lastName: 101 | type: string 102 | password: 103 | type: string 104 | updatedAt: 105 | type: string 106 | username: 107 | type: string 108 | type: object 109 | info: 110 | contact: {} 111 | paths: 112 | /articles: 113 | get: 114 | produces: 115 | - application/json 116 | responses: 117 | "200": 118 | description: OK 119 | schema: 120 | $ref: '#/definitions/models.Article' 121 | summary: Get All Articles 122 | tags: 123 | - articles 124 | post: 125 | description: Create An Article. 126 | parameters: 127 | - description: body 128 | in: body 129 | name: request 130 | required: true 131 | schema: 132 | $ref: '#/definitions/models.CreateArticleForm' 133 | produces: 134 | - application/json 135 | responses: 136 | "200": 137 | description: OK 138 | schema: 139 | $ref: '#/definitions/models.Article' 140 | summary: Create An Article 141 | tags: 142 | - articles 143 | /articles/{id}: 144 | delete: 145 | description: Delete An Article. 146 | parameters: 147 | - description: id 148 | format: id 149 | in: path 150 | name: id 151 | type: integer 152 | produces: 153 | - application/json 154 | responses: 155 | "200": 156 | description: OK 157 | schema: 158 | $ref: '#/definitions/models.Article' 159 | summary: Delete An Article 160 | tags: 161 | - articles 162 | get: 163 | description: Returns the the article with ID. 164 | parameters: 165 | - description: id 166 | format: id 167 | in: path 168 | name: id 169 | type: integer 170 | produces: 171 | - application/json 172 | responses: 173 | "200": 174 | description: OK 175 | schema: 176 | $ref: '#/definitions/models.Article' 177 | summary: Find a article 178 | tags: 179 | - articles 180 | put: 181 | description: Update An Article. 182 | parameters: 183 | - description: body 184 | in: body 185 | name: request 186 | required: true 187 | schema: 188 | $ref: '#/definitions/models.CreateArticleForm' 189 | - description: id 190 | format: id 191 | in: path 192 | name: id 193 | type: integer 194 | produces: 195 | - application/json 196 | responses: 197 | "200": 198 | description: OK 199 | schema: 200 | $ref: '#/definitions/models.Article' 201 | summary: Update An Article 202 | tags: 203 | - articles 204 | /auth/login: 205 | post: 206 | parameters: 207 | - description: body 208 | in: body 209 | name: request 210 | required: true 211 | schema: 212 | $ref: '#/definitions/models.LoginInput' 213 | produces: 214 | - application/json 215 | responses: 216 | "200": 217 | description: OK 218 | schema: 219 | items: 220 | $ref: '#/definitions/models.User' 221 | type: array 222 | summary: Login 223 | tags: 224 | - auth 225 | /auth/profile: 226 | get: 227 | produces: 228 | - application/json 229 | responses: 230 | "200": 231 | description: OK 232 | schema: 233 | items: 234 | $ref: '#/definitions/models.User' 235 | type: array 236 | security: 237 | - jwtTokenAuth: [] 238 | summary: Profile 239 | tags: 240 | - auth 241 | /healthcheck: 242 | get: 243 | consumes: 244 | - application/json 245 | description: do health check 246 | produces: 247 | - application/json 248 | responses: 249 | "200": 250 | description: OK 251 | schema: 252 | type: string 253 | summary: health check 254 | tags: 255 | - example 256 | /products: 257 | get: 258 | parameters: 259 | - description: limit 260 | format: limit 261 | in: query 262 | name: limit 263 | type: integer 264 | - description: skip 265 | format: skip 266 | in: query 267 | name: skip 268 | type: integer 269 | - description: search 270 | format: search 271 | in: query 272 | name: search 273 | type: string 274 | produces: 275 | - application/json 276 | responses: 277 | "200": 278 | description: OK 279 | schema: 280 | $ref: '#/definitions/models.Product' 281 | summary: Get All Products 282 | tags: 283 | - products 284 | post: 285 | description: Create An Product. 286 | parameters: 287 | - description: body 288 | in: body 289 | name: request 290 | required: true 291 | schema: 292 | $ref: '#/definitions/models.CreateProductForm' 293 | produces: 294 | - application/json 295 | responses: 296 | "200": 297 | description: OK 298 | schema: 299 | $ref: '#/definitions/models.Product' 300 | summary: Create An Product 301 | tags: 302 | - products 303 | /products/{id}: 304 | delete: 305 | description: Delete An Product. 306 | parameters: 307 | - description: id 308 | format: id 309 | in: path 310 | name: id 311 | type: string 312 | produces: 313 | - application/json 314 | responses: 315 | "200": 316 | description: OK 317 | schema: 318 | $ref: '#/definitions/models.Product' 319 | summary: Delete An Product 320 | tags: 321 | - products 322 | get: 323 | description: Returns the the product with ID. 324 | parameters: 325 | - description: id 326 | format: id 327 | in: path 328 | name: id 329 | type: string 330 | produces: 331 | - application/json 332 | responses: 333 | "200": 334 | description: OK 335 | schema: 336 | $ref: '#/definitions/models.Product' 337 | summary: Find a product 338 | tags: 339 | - products 340 | put: 341 | description: Update An Product. 342 | parameters: 343 | - description: body 344 | in: body 345 | name: request 346 | required: true 347 | schema: 348 | $ref: '#/definitions/models.CreateProductForm' 349 | - description: id 350 | format: id 351 | in: path 352 | name: id 353 | type: string 354 | produces: 355 | - application/json 356 | responses: 357 | "200": 358 | description: OK 359 | schema: 360 | $ref: '#/definitions/models.Product' 361 | summary: Update An Product 362 | tags: 363 | - products 364 | /users: 365 | get: 366 | parameters: 367 | - description: limit 368 | format: limit 369 | in: query 370 | name: limit 371 | type: integer 372 | - description: skip 373 | format: skip 374 | in: query 375 | name: skip 376 | type: integer 377 | - description: search 378 | format: search 379 | in: query 380 | name: search 381 | type: string 382 | produces: 383 | - application/json 384 | responses: 385 | "200": 386 | description: OK 387 | schema: 388 | items: 389 | $ref: '#/definitions/models.User' 390 | type: array 391 | summary: List Of users 392 | tags: 393 | - users 394 | post: 395 | description: Create An User. 396 | parameters: 397 | - description: body 398 | in: body 399 | name: request 400 | required: true 401 | schema: 402 | $ref: '#/definitions/models.CreateUserForm' 403 | produces: 404 | - application/json 405 | responses: 406 | "201": 407 | description: Created 408 | schema: 409 | $ref: '#/definitions/models.User' 410 | summary: Create An User 411 | tags: 412 | - users 413 | /users/{id}: 414 | delete: 415 | description: Delete An User. 416 | parameters: 417 | - description: id 418 | format: id 419 | in: path 420 | name: id 421 | type: integer 422 | produces: 423 | - application/json 424 | responses: 425 | "200": 426 | description: OK 427 | schema: 428 | $ref: '#/definitions/models.User' 429 | summary: Delete An User 430 | tags: 431 | - users 432 | get: 433 | parameters: 434 | - description: id 435 | format: id 436 | in: path 437 | name: id 438 | type: integer 439 | produces: 440 | - application/json 441 | responses: 442 | "200": 443 | description: OK 444 | schema: 445 | $ref: '#/definitions/models.User' 446 | summary: Get An User 447 | tags: 448 | - users 449 | put: 450 | description: Update An User. 451 | parameters: 452 | - description: id 453 | format: id 454 | in: path 455 | name: id 456 | type: integer 457 | - description: body 458 | in: body 459 | name: request 460 | required: true 461 | schema: 462 | $ref: '#/definitions/models.CreateUserForm' 463 | produces: 464 | - application/json 465 | responses: 466 | "201": 467 | description: Created 468 | schema: 469 | $ref: '#/definitions/models.User' 470 | summary: Update An User 471 | tags: 472 | - users 473 | swagger: "2.0" 474 | -------------------------------------------------------------------------------- /docs/swagger.json: -------------------------------------------------------------------------------- 1 | { 2 | "swagger": "2.0", 3 | "info": { 4 | "contact": {} 5 | }, 6 | "paths": { 7 | "/articles": { 8 | "get": { 9 | "produces": [ 10 | "application/json" 11 | ], 12 | "tags": [ 13 | "articles" 14 | ], 15 | "summary": "Get All Articles", 16 | "responses": { 17 | "200": { 18 | "description": "OK", 19 | "schema": { 20 | "$ref": "#/definitions/models.Article" 21 | } 22 | } 23 | } 24 | }, 25 | "post": { 26 | "description": "Create An Article.", 27 | "produces": [ 28 | "application/json" 29 | ], 30 | "tags": [ 31 | "articles" 32 | ], 33 | "summary": "Create An Article", 34 | "parameters": [ 35 | { 36 | "description": "body", 37 | "name": "request", 38 | "in": "body", 39 | "required": true, 40 | "schema": { 41 | "$ref": "#/definitions/models.CreateArticleForm" 42 | } 43 | } 44 | ], 45 | "responses": { 46 | "200": { 47 | "description": "OK", 48 | "schema": { 49 | "$ref": "#/definitions/models.Article" 50 | } 51 | } 52 | } 53 | } 54 | }, 55 | "/articles/{id}": { 56 | "get": { 57 | "description": "Returns the the article with ID.", 58 | "produces": [ 59 | "application/json" 60 | ], 61 | "tags": [ 62 | "articles" 63 | ], 64 | "summary": "Find a article", 65 | "parameters": [ 66 | { 67 | "type": "integer", 68 | "format": "id", 69 | "description": "id", 70 | "name": "id", 71 | "in": "path" 72 | } 73 | ], 74 | "responses": { 75 | "200": { 76 | "description": "OK", 77 | "schema": { 78 | "$ref": "#/definitions/models.Article" 79 | } 80 | } 81 | } 82 | }, 83 | "put": { 84 | "description": "Update An Article.", 85 | "produces": [ 86 | "application/json" 87 | ], 88 | "tags": [ 89 | "articles" 90 | ], 91 | "summary": "Update An Article", 92 | "parameters": [ 93 | { 94 | "description": "body", 95 | "name": "request", 96 | "in": "body", 97 | "required": true, 98 | "schema": { 99 | "$ref": "#/definitions/models.CreateArticleForm" 100 | } 101 | }, 102 | { 103 | "type": "integer", 104 | "format": "id", 105 | "description": "id", 106 | "name": "id", 107 | "in": "path" 108 | } 109 | ], 110 | "responses": { 111 | "200": { 112 | "description": "OK", 113 | "schema": { 114 | "$ref": "#/definitions/models.Article" 115 | } 116 | } 117 | } 118 | }, 119 | "delete": { 120 | "description": "Delete An Article.", 121 | "produces": [ 122 | "application/json" 123 | ], 124 | "tags": [ 125 | "articles" 126 | ], 127 | "summary": "Delete An Article", 128 | "parameters": [ 129 | { 130 | "type": "integer", 131 | "format": "id", 132 | "description": "id", 133 | "name": "id", 134 | "in": "path" 135 | } 136 | ], 137 | "responses": { 138 | "200": { 139 | "description": "OK", 140 | "schema": { 141 | "$ref": "#/definitions/models.Article" 142 | } 143 | } 144 | } 145 | } 146 | }, 147 | "/auth/login": { 148 | "post": { 149 | "produces": [ 150 | "application/json" 151 | ], 152 | "tags": [ 153 | "auth" 154 | ], 155 | "summary": "Login", 156 | "parameters": [ 157 | { 158 | "description": "body", 159 | "name": "request", 160 | "in": "body", 161 | "required": true, 162 | "schema": { 163 | "$ref": "#/definitions/models.LoginInput" 164 | } 165 | } 166 | ], 167 | "responses": { 168 | "200": { 169 | "description": "OK", 170 | "schema": { 171 | "type": "array", 172 | "items": { 173 | "$ref": "#/definitions/models.User" 174 | } 175 | } 176 | } 177 | } 178 | } 179 | }, 180 | "/auth/profile": { 181 | "get": { 182 | "security": [ 183 | { 184 | "jwtTokenAuth": [] 185 | } 186 | ], 187 | "produces": [ 188 | "application/json" 189 | ], 190 | "tags": [ 191 | "auth" 192 | ], 193 | "summary": "Profile", 194 | "responses": { 195 | "200": { 196 | "description": "OK", 197 | "schema": { 198 | "type": "array", 199 | "items": { 200 | "$ref": "#/definitions/models.User" 201 | } 202 | } 203 | } 204 | } 205 | } 206 | }, 207 | "/healthcheck": { 208 | "get": { 209 | "description": "do health check", 210 | "consumes": [ 211 | "application/json" 212 | ], 213 | "produces": [ 214 | "application/json" 215 | ], 216 | "tags": [ 217 | "example" 218 | ], 219 | "summary": "health check", 220 | "responses": { 221 | "200": { 222 | "description": "OK", 223 | "schema": { 224 | "type": "string" 225 | } 226 | } 227 | } 228 | } 229 | }, 230 | "/products": { 231 | "get": { 232 | "produces": [ 233 | "application/json" 234 | ], 235 | "tags": [ 236 | "products" 237 | ], 238 | "summary": "Get All Products", 239 | "parameters": [ 240 | { 241 | "type": "integer", 242 | "format": "limit", 243 | "description": "limit", 244 | "name": "limit", 245 | "in": "query" 246 | }, 247 | { 248 | "type": "integer", 249 | "format": "skip", 250 | "description": "skip", 251 | "name": "skip", 252 | "in": "query" 253 | }, 254 | { 255 | "type": "string", 256 | "format": "search", 257 | "description": "search", 258 | "name": "search", 259 | "in": "query" 260 | } 261 | ], 262 | "responses": { 263 | "200": { 264 | "description": "OK", 265 | "schema": { 266 | "$ref": "#/definitions/models.Product" 267 | } 268 | } 269 | } 270 | }, 271 | "post": { 272 | "description": "Create An Product.", 273 | "produces": [ 274 | "application/json" 275 | ], 276 | "tags": [ 277 | "products" 278 | ], 279 | "summary": "Create An Product", 280 | "parameters": [ 281 | { 282 | "description": "body", 283 | "name": "request", 284 | "in": "body", 285 | "required": true, 286 | "schema": { 287 | "$ref": "#/definitions/models.CreateProductForm" 288 | } 289 | } 290 | ], 291 | "responses": { 292 | "200": { 293 | "description": "OK", 294 | "schema": { 295 | "$ref": "#/definitions/models.Product" 296 | } 297 | } 298 | } 299 | } 300 | }, 301 | "/products/{id}": { 302 | "get": { 303 | "description": "Returns the the product with ID.", 304 | "produces": [ 305 | "application/json" 306 | ], 307 | "tags": [ 308 | "products" 309 | ], 310 | "summary": "Find a product", 311 | "parameters": [ 312 | { 313 | "type": "string", 314 | "format": "id", 315 | "description": "id", 316 | "name": "id", 317 | "in": "path" 318 | } 319 | ], 320 | "responses": { 321 | "200": { 322 | "description": "OK", 323 | "schema": { 324 | "$ref": "#/definitions/models.Product" 325 | } 326 | } 327 | } 328 | }, 329 | "put": { 330 | "description": "Update An Product.", 331 | "produces": [ 332 | "application/json" 333 | ], 334 | "tags": [ 335 | "products" 336 | ], 337 | "summary": "Update An Product", 338 | "parameters": [ 339 | { 340 | "description": "body", 341 | "name": "request", 342 | "in": "body", 343 | "required": true, 344 | "schema": { 345 | "$ref": "#/definitions/models.CreateProductForm" 346 | } 347 | }, 348 | { 349 | "type": "string", 350 | "format": "id", 351 | "description": "id", 352 | "name": "id", 353 | "in": "path" 354 | } 355 | ], 356 | "responses": { 357 | "200": { 358 | "description": "OK", 359 | "schema": { 360 | "$ref": "#/definitions/models.Product" 361 | } 362 | } 363 | } 364 | }, 365 | "delete": { 366 | "description": "Delete An Product.", 367 | "produces": [ 368 | "application/json" 369 | ], 370 | "tags": [ 371 | "products" 372 | ], 373 | "summary": "Delete An Product", 374 | "parameters": [ 375 | { 376 | "type": "string", 377 | "format": "id", 378 | "description": "id", 379 | "name": "id", 380 | "in": "path" 381 | } 382 | ], 383 | "responses": { 384 | "200": { 385 | "description": "OK", 386 | "schema": { 387 | "$ref": "#/definitions/models.Product" 388 | } 389 | } 390 | } 391 | } 392 | }, 393 | "/users": { 394 | "get": { 395 | "produces": [ 396 | "application/json" 397 | ], 398 | "tags": [ 399 | "users" 400 | ], 401 | "summary": "List Of users", 402 | "parameters": [ 403 | { 404 | "type": "integer", 405 | "format": "limit", 406 | "description": "limit", 407 | "name": "limit", 408 | "in": "query" 409 | }, 410 | { 411 | "type": "integer", 412 | "format": "skip", 413 | "description": "skip", 414 | "name": "skip", 415 | "in": "query" 416 | }, 417 | { 418 | "type": "string", 419 | "format": "search", 420 | "description": "search", 421 | "name": "search", 422 | "in": "query" 423 | } 424 | ], 425 | "responses": { 426 | "200": { 427 | "description": "OK", 428 | "schema": { 429 | "type": "array", 430 | "items": { 431 | "$ref": "#/definitions/models.User" 432 | } 433 | } 434 | } 435 | } 436 | }, 437 | "post": { 438 | "description": "Create An User.", 439 | "produces": [ 440 | "application/json" 441 | ], 442 | "tags": [ 443 | "users" 444 | ], 445 | "summary": "Create An User", 446 | "parameters": [ 447 | { 448 | "description": "body", 449 | "name": "request", 450 | "in": "body", 451 | "required": true, 452 | "schema": { 453 | "$ref": "#/definitions/models.CreateUserForm" 454 | } 455 | } 456 | ], 457 | "responses": { 458 | "201": { 459 | "description": "Created", 460 | "schema": { 461 | "$ref": "#/definitions/models.User" 462 | } 463 | } 464 | } 465 | } 466 | }, 467 | "/users/{id}": { 468 | "get": { 469 | "produces": [ 470 | "application/json" 471 | ], 472 | "tags": [ 473 | "users" 474 | ], 475 | "summary": "Get An User", 476 | "parameters": [ 477 | { 478 | "type": "integer", 479 | "format": "id", 480 | "description": "id", 481 | "name": "id", 482 | "in": "path" 483 | } 484 | ], 485 | "responses": { 486 | "200": { 487 | "description": "OK", 488 | "schema": { 489 | "$ref": "#/definitions/models.User" 490 | } 491 | } 492 | } 493 | }, 494 | "put": { 495 | "description": "Update An User.", 496 | "produces": [ 497 | "application/json" 498 | ], 499 | "tags": [ 500 | "users" 501 | ], 502 | "summary": "Update An User", 503 | "parameters": [ 504 | { 505 | "type": "integer", 506 | "format": "id", 507 | "description": "id", 508 | "name": "id", 509 | "in": "path" 510 | }, 511 | { 512 | "description": "body", 513 | "name": "request", 514 | "in": "body", 515 | "required": true, 516 | "schema": { 517 | "$ref": "#/definitions/models.CreateUserForm" 518 | } 519 | } 520 | ], 521 | "responses": { 522 | "201": { 523 | "description": "Created", 524 | "schema": { 525 | "$ref": "#/definitions/models.User" 526 | } 527 | } 528 | } 529 | }, 530 | "delete": { 531 | "description": "Delete An User.", 532 | "produces": [ 533 | "application/json" 534 | ], 535 | "tags": [ 536 | "users" 537 | ], 538 | "summary": "Delete An User", 539 | "parameters": [ 540 | { 541 | "type": "integer", 542 | "format": "id", 543 | "description": "id", 544 | "name": "id", 545 | "in": "path" 546 | } 547 | ], 548 | "responses": { 549 | "200": { 550 | "description": "OK", 551 | "schema": { 552 | "$ref": "#/definitions/models.User" 553 | } 554 | } 555 | } 556 | } 557 | } 558 | }, 559 | "definitions": { 560 | "models.Article": { 561 | "type": "object", 562 | "properties": { 563 | "content": { 564 | "type": "string" 565 | }, 566 | "created_at": { 567 | "type": "string" 568 | }, 569 | "id": { 570 | "type": "integer" 571 | }, 572 | "title": { 573 | "type": "string" 574 | }, 575 | "updated_at": { 576 | "type": "string" 577 | } 578 | } 579 | }, 580 | "models.CreateArticleForm": { 581 | "type": "object", 582 | "required": [ 583 | "content", 584 | "title" 585 | ], 586 | "properties": { 587 | "content": { 588 | "type": "string", 589 | "maxLength": 1000, 590 | "minLength": 3 591 | }, 592 | "title": { 593 | "type": "string", 594 | "maxLength": 100, 595 | "minLength": 3 596 | } 597 | } 598 | }, 599 | "models.CreateProductForm": { 600 | "type": "object", 601 | "required": [ 602 | "content", 603 | "price", 604 | "title" 605 | ], 606 | "properties": { 607 | "content": { 608 | "type": "string", 609 | "maxLength": 1000, 610 | "minLength": 3 611 | }, 612 | "price": { 613 | "type": "integer" 614 | }, 615 | "title": { 616 | "type": "string", 617 | "maxLength": 100, 618 | "minLength": 3 619 | } 620 | } 621 | }, 622 | "models.CreateUserForm": { 623 | "type": "object", 624 | "required": [ 625 | "Email", 626 | "FirstName", 627 | "LastName", 628 | "Password", 629 | "Username" 630 | ], 631 | "properties": { 632 | "Email": { 633 | "type": "string" 634 | }, 635 | "FirstName": { 636 | "type": "string" 637 | }, 638 | "LastName": { 639 | "type": "string" 640 | }, 641 | "Password": { 642 | "type": "string" 643 | }, 644 | "Username": { 645 | "type": "string" 646 | } 647 | } 648 | }, 649 | "models.LoginInput": { 650 | "type": "object", 651 | "required": [ 652 | "password", 653 | "username" 654 | ], 655 | "properties": { 656 | "password": { 657 | "type": "string" 658 | }, 659 | "username": { 660 | "type": "string" 661 | } 662 | } 663 | }, 664 | "models.Product": { 665 | "type": "object", 666 | "properties": { 667 | "content": { 668 | "type": "string" 669 | }, 670 | "created_at": { 671 | "type": "string" 672 | }, 673 | "id": { 674 | "type": "string" 675 | }, 676 | "price": { 677 | "type": "integer" 678 | }, 679 | "title": { 680 | "type": "string" 681 | }, 682 | "updated_at": { 683 | "type": "string" 684 | } 685 | } 686 | }, 687 | "models.User": { 688 | "type": "object", 689 | "properties": { 690 | "createdAt": { 691 | "type": "string" 692 | }, 693 | "email": { 694 | "type": "string" 695 | }, 696 | "firstName": { 697 | "type": "string" 698 | }, 699 | "id": { 700 | "type": "integer" 701 | }, 702 | "lastName": { 703 | "type": "string" 704 | }, 705 | "password": { 706 | "type": "string" 707 | }, 708 | "updatedAt": { 709 | "type": "string" 710 | }, 711 | "username": { 712 | "type": "string" 713 | } 714 | } 715 | } 716 | } 717 | } -------------------------------------------------------------------------------- /docs/docs.go: -------------------------------------------------------------------------------- 1 | // Package docs Code generated by swaggo/swag. DO NOT EDIT 2 | package docs 3 | 4 | import "github.com/swaggo/swag" 5 | 6 | const docTemplate = `{ 7 | "schemes": {{ marshal .Schemes }}, 8 | "swagger": "2.0", 9 | "info": { 10 | "description": "{{escape .Description}}", 11 | "title": "{{.Title}}", 12 | "contact": {}, 13 | "version": "{{.Version}}" 14 | }, 15 | "host": "{{.Host}}", 16 | "basePath": "{{.BasePath}}", 17 | "paths": { 18 | "/articles": { 19 | "get": { 20 | "produces": [ 21 | "application/json" 22 | ], 23 | "tags": [ 24 | "articles" 25 | ], 26 | "summary": "Get All Articles", 27 | "responses": { 28 | "200": { 29 | "description": "OK", 30 | "schema": { 31 | "$ref": "#/definitions/models.Article" 32 | } 33 | } 34 | } 35 | }, 36 | "post": { 37 | "description": "Create An Article.", 38 | "produces": [ 39 | "application/json" 40 | ], 41 | "tags": [ 42 | "articles" 43 | ], 44 | "summary": "Create An Article", 45 | "parameters": [ 46 | { 47 | "description": "body", 48 | "name": "request", 49 | "in": "body", 50 | "required": true, 51 | "schema": { 52 | "$ref": "#/definitions/models.CreateArticleForm" 53 | } 54 | } 55 | ], 56 | "responses": { 57 | "200": { 58 | "description": "OK", 59 | "schema": { 60 | "$ref": "#/definitions/models.Article" 61 | } 62 | } 63 | } 64 | } 65 | }, 66 | "/articles/{id}": { 67 | "get": { 68 | "description": "Returns the the article with ID.", 69 | "produces": [ 70 | "application/json" 71 | ], 72 | "tags": [ 73 | "articles" 74 | ], 75 | "summary": "Find a article", 76 | "parameters": [ 77 | { 78 | "type": "integer", 79 | "format": "id", 80 | "description": "id", 81 | "name": "id", 82 | "in": "path" 83 | } 84 | ], 85 | "responses": { 86 | "200": { 87 | "description": "OK", 88 | "schema": { 89 | "$ref": "#/definitions/models.Article" 90 | } 91 | } 92 | } 93 | }, 94 | "put": { 95 | "description": "Update An Article.", 96 | "produces": [ 97 | "application/json" 98 | ], 99 | "tags": [ 100 | "articles" 101 | ], 102 | "summary": "Update An Article", 103 | "parameters": [ 104 | { 105 | "description": "body", 106 | "name": "request", 107 | "in": "body", 108 | "required": true, 109 | "schema": { 110 | "$ref": "#/definitions/models.CreateArticleForm" 111 | } 112 | }, 113 | { 114 | "type": "integer", 115 | "format": "id", 116 | "description": "id", 117 | "name": "id", 118 | "in": "path" 119 | } 120 | ], 121 | "responses": { 122 | "200": { 123 | "description": "OK", 124 | "schema": { 125 | "$ref": "#/definitions/models.Article" 126 | } 127 | } 128 | } 129 | }, 130 | "delete": { 131 | "description": "Delete An Article.", 132 | "produces": [ 133 | "application/json" 134 | ], 135 | "tags": [ 136 | "articles" 137 | ], 138 | "summary": "Delete An Article", 139 | "parameters": [ 140 | { 141 | "type": "integer", 142 | "format": "id", 143 | "description": "id", 144 | "name": "id", 145 | "in": "path" 146 | } 147 | ], 148 | "responses": { 149 | "200": { 150 | "description": "OK", 151 | "schema": { 152 | "$ref": "#/definitions/models.Article" 153 | } 154 | } 155 | } 156 | } 157 | }, 158 | "/auth/login": { 159 | "post": { 160 | "produces": [ 161 | "application/json" 162 | ], 163 | "tags": [ 164 | "auth" 165 | ], 166 | "summary": "Login", 167 | "parameters": [ 168 | { 169 | "description": "body", 170 | "name": "request", 171 | "in": "body", 172 | "required": true, 173 | "schema": { 174 | "$ref": "#/definitions/models.LoginInput" 175 | } 176 | } 177 | ], 178 | "responses": { 179 | "200": { 180 | "description": "OK", 181 | "schema": { 182 | "type": "array", 183 | "items": { 184 | "$ref": "#/definitions/models.User" 185 | } 186 | } 187 | } 188 | } 189 | } 190 | }, 191 | "/auth/profile": { 192 | "get": { 193 | "security": [ 194 | { 195 | "jwtTokenAuth": [] 196 | } 197 | ], 198 | "produces": [ 199 | "application/json" 200 | ], 201 | "tags": [ 202 | "auth" 203 | ], 204 | "summary": "Profile", 205 | "responses": { 206 | "200": { 207 | "description": "OK", 208 | "schema": { 209 | "type": "array", 210 | "items": { 211 | "$ref": "#/definitions/models.User" 212 | } 213 | } 214 | } 215 | } 216 | } 217 | }, 218 | "/healthcheck": { 219 | "get": { 220 | "description": "do health check", 221 | "consumes": [ 222 | "application/json" 223 | ], 224 | "produces": [ 225 | "application/json" 226 | ], 227 | "tags": [ 228 | "example" 229 | ], 230 | "summary": "health check", 231 | "responses": { 232 | "200": { 233 | "description": "OK", 234 | "schema": { 235 | "type": "string" 236 | } 237 | } 238 | } 239 | } 240 | }, 241 | "/products": { 242 | "get": { 243 | "produces": [ 244 | "application/json" 245 | ], 246 | "tags": [ 247 | "products" 248 | ], 249 | "summary": "Get All Products", 250 | "parameters": [ 251 | { 252 | "type": "integer", 253 | "format": "limit", 254 | "description": "limit", 255 | "name": "limit", 256 | "in": "query" 257 | }, 258 | { 259 | "type": "integer", 260 | "format": "skip", 261 | "description": "skip", 262 | "name": "skip", 263 | "in": "query" 264 | }, 265 | { 266 | "type": "string", 267 | "format": "search", 268 | "description": "search", 269 | "name": "search", 270 | "in": "query" 271 | } 272 | ], 273 | "responses": { 274 | "200": { 275 | "description": "OK", 276 | "schema": { 277 | "$ref": "#/definitions/models.Product" 278 | } 279 | } 280 | } 281 | }, 282 | "post": { 283 | "description": "Create An Product.", 284 | "produces": [ 285 | "application/json" 286 | ], 287 | "tags": [ 288 | "products" 289 | ], 290 | "summary": "Create An Product", 291 | "parameters": [ 292 | { 293 | "description": "body", 294 | "name": "request", 295 | "in": "body", 296 | "required": true, 297 | "schema": { 298 | "$ref": "#/definitions/models.CreateProductForm" 299 | } 300 | } 301 | ], 302 | "responses": { 303 | "200": { 304 | "description": "OK", 305 | "schema": { 306 | "$ref": "#/definitions/models.Product" 307 | } 308 | } 309 | } 310 | } 311 | }, 312 | "/products/{id}": { 313 | "get": { 314 | "description": "Returns the the product with ID.", 315 | "produces": [ 316 | "application/json" 317 | ], 318 | "tags": [ 319 | "products" 320 | ], 321 | "summary": "Find a product", 322 | "parameters": [ 323 | { 324 | "type": "string", 325 | "format": "id", 326 | "description": "id", 327 | "name": "id", 328 | "in": "path" 329 | } 330 | ], 331 | "responses": { 332 | "200": { 333 | "description": "OK", 334 | "schema": { 335 | "$ref": "#/definitions/models.Product" 336 | } 337 | } 338 | } 339 | }, 340 | "put": { 341 | "description": "Update An Product.", 342 | "produces": [ 343 | "application/json" 344 | ], 345 | "tags": [ 346 | "products" 347 | ], 348 | "summary": "Update An Product", 349 | "parameters": [ 350 | { 351 | "description": "body", 352 | "name": "request", 353 | "in": "body", 354 | "required": true, 355 | "schema": { 356 | "$ref": "#/definitions/models.CreateProductForm" 357 | } 358 | }, 359 | { 360 | "type": "string", 361 | "format": "id", 362 | "description": "id", 363 | "name": "id", 364 | "in": "path" 365 | } 366 | ], 367 | "responses": { 368 | "200": { 369 | "description": "OK", 370 | "schema": { 371 | "$ref": "#/definitions/models.Product" 372 | } 373 | } 374 | } 375 | }, 376 | "delete": { 377 | "description": "Delete An Product.", 378 | "produces": [ 379 | "application/json" 380 | ], 381 | "tags": [ 382 | "products" 383 | ], 384 | "summary": "Delete An Product", 385 | "parameters": [ 386 | { 387 | "type": "string", 388 | "format": "id", 389 | "description": "id", 390 | "name": "id", 391 | "in": "path" 392 | } 393 | ], 394 | "responses": { 395 | "200": { 396 | "description": "OK", 397 | "schema": { 398 | "$ref": "#/definitions/models.Product" 399 | } 400 | } 401 | } 402 | } 403 | }, 404 | "/users": { 405 | "get": { 406 | "produces": [ 407 | "application/json" 408 | ], 409 | "tags": [ 410 | "users" 411 | ], 412 | "summary": "List Of users", 413 | "parameters": [ 414 | { 415 | "type": "integer", 416 | "format": "limit", 417 | "description": "limit", 418 | "name": "limit", 419 | "in": "query" 420 | }, 421 | { 422 | "type": "integer", 423 | "format": "skip", 424 | "description": "skip", 425 | "name": "skip", 426 | "in": "query" 427 | }, 428 | { 429 | "type": "string", 430 | "format": "search", 431 | "description": "search", 432 | "name": "search", 433 | "in": "query" 434 | } 435 | ], 436 | "responses": { 437 | "200": { 438 | "description": "OK", 439 | "schema": { 440 | "type": "array", 441 | "items": { 442 | "$ref": "#/definitions/models.User" 443 | } 444 | } 445 | } 446 | } 447 | }, 448 | "post": { 449 | "description": "Create An User.", 450 | "produces": [ 451 | "application/json" 452 | ], 453 | "tags": [ 454 | "users" 455 | ], 456 | "summary": "Create An User", 457 | "parameters": [ 458 | { 459 | "description": "body", 460 | "name": "request", 461 | "in": "body", 462 | "required": true, 463 | "schema": { 464 | "$ref": "#/definitions/models.CreateUserForm" 465 | } 466 | } 467 | ], 468 | "responses": { 469 | "201": { 470 | "description": "Created", 471 | "schema": { 472 | "$ref": "#/definitions/models.User" 473 | } 474 | } 475 | } 476 | } 477 | }, 478 | "/users/{id}": { 479 | "get": { 480 | "produces": [ 481 | "application/json" 482 | ], 483 | "tags": [ 484 | "users" 485 | ], 486 | "summary": "Get An User", 487 | "parameters": [ 488 | { 489 | "type": "integer", 490 | "format": "id", 491 | "description": "id", 492 | "name": "id", 493 | "in": "path" 494 | } 495 | ], 496 | "responses": { 497 | "200": { 498 | "description": "OK", 499 | "schema": { 500 | "$ref": "#/definitions/models.User" 501 | } 502 | } 503 | } 504 | }, 505 | "put": { 506 | "description": "Update An User.", 507 | "produces": [ 508 | "application/json" 509 | ], 510 | "tags": [ 511 | "users" 512 | ], 513 | "summary": "Update An User", 514 | "parameters": [ 515 | { 516 | "type": "integer", 517 | "format": "id", 518 | "description": "id", 519 | "name": "id", 520 | "in": "path" 521 | }, 522 | { 523 | "description": "body", 524 | "name": "request", 525 | "in": "body", 526 | "required": true, 527 | "schema": { 528 | "$ref": "#/definitions/models.CreateUserForm" 529 | } 530 | } 531 | ], 532 | "responses": { 533 | "201": { 534 | "description": "Created", 535 | "schema": { 536 | "$ref": "#/definitions/models.User" 537 | } 538 | } 539 | } 540 | }, 541 | "delete": { 542 | "description": "Delete An User.", 543 | "produces": [ 544 | "application/json" 545 | ], 546 | "tags": [ 547 | "users" 548 | ], 549 | "summary": "Delete An User", 550 | "parameters": [ 551 | { 552 | "type": "integer", 553 | "format": "id", 554 | "description": "id", 555 | "name": "id", 556 | "in": "path" 557 | } 558 | ], 559 | "responses": { 560 | "200": { 561 | "description": "OK", 562 | "schema": { 563 | "$ref": "#/definitions/models.User" 564 | } 565 | } 566 | } 567 | } 568 | } 569 | }, 570 | "definitions": { 571 | "models.Article": { 572 | "type": "object", 573 | "properties": { 574 | "content": { 575 | "type": "string" 576 | }, 577 | "created_at": { 578 | "type": "string" 579 | }, 580 | "id": { 581 | "type": "integer" 582 | }, 583 | "title": { 584 | "type": "string" 585 | }, 586 | "updated_at": { 587 | "type": "string" 588 | } 589 | } 590 | }, 591 | "models.CreateArticleForm": { 592 | "type": "object", 593 | "required": [ 594 | "content", 595 | "title" 596 | ], 597 | "properties": { 598 | "content": { 599 | "type": "string", 600 | "maxLength": 1000, 601 | "minLength": 3 602 | }, 603 | "title": { 604 | "type": "string", 605 | "maxLength": 100, 606 | "minLength": 3 607 | } 608 | } 609 | }, 610 | "models.CreateProductForm": { 611 | "type": "object", 612 | "required": [ 613 | "content", 614 | "price", 615 | "title" 616 | ], 617 | "properties": { 618 | "content": { 619 | "type": "string", 620 | "maxLength": 1000, 621 | "minLength": 3 622 | }, 623 | "price": { 624 | "type": "integer" 625 | }, 626 | "title": { 627 | "type": "string", 628 | "maxLength": 100, 629 | "minLength": 3 630 | } 631 | } 632 | }, 633 | "models.CreateUserForm": { 634 | "type": "object", 635 | "required": [ 636 | "Email", 637 | "FirstName", 638 | "LastName", 639 | "Password", 640 | "Username" 641 | ], 642 | "properties": { 643 | "Email": { 644 | "type": "string" 645 | }, 646 | "FirstName": { 647 | "type": "string" 648 | }, 649 | "LastName": { 650 | "type": "string" 651 | }, 652 | "Password": { 653 | "type": "string" 654 | }, 655 | "Username": { 656 | "type": "string" 657 | } 658 | } 659 | }, 660 | "models.LoginInput": { 661 | "type": "object", 662 | "required": [ 663 | "password", 664 | "username" 665 | ], 666 | "properties": { 667 | "password": { 668 | "type": "string" 669 | }, 670 | "username": { 671 | "type": "string" 672 | } 673 | } 674 | }, 675 | "models.Product": { 676 | "type": "object", 677 | "properties": { 678 | "content": { 679 | "type": "string" 680 | }, 681 | "created_at": { 682 | "type": "string" 683 | }, 684 | "id": { 685 | "type": "string" 686 | }, 687 | "price": { 688 | "type": "integer" 689 | }, 690 | "title": { 691 | "type": "string" 692 | }, 693 | "updated_at": { 694 | "type": "string" 695 | } 696 | } 697 | }, 698 | "models.User": { 699 | "type": "object", 700 | "properties": { 701 | "createdAt": { 702 | "type": "string" 703 | }, 704 | "email": { 705 | "type": "string" 706 | }, 707 | "firstName": { 708 | "type": "string" 709 | }, 710 | "id": { 711 | "type": "integer" 712 | }, 713 | "lastName": { 714 | "type": "string" 715 | }, 716 | "password": { 717 | "type": "string" 718 | }, 719 | "updatedAt": { 720 | "type": "string" 721 | }, 722 | "username": { 723 | "type": "string" 724 | } 725 | } 726 | } 727 | } 728 | }` 729 | 730 | // SwaggerInfo holds exported Swagger Info so clients can modify it 731 | var SwaggerInfo = &swag.Spec{ 732 | Version: "", 733 | Host: "", 734 | BasePath: "", 735 | Schemes: []string{}, 736 | Title: "", 737 | Description: "", 738 | InfoInstanceName: "swagger", 739 | SwaggerTemplate: docTemplate, 740 | LeftDelim: "{{", 741 | RightDelim: "}}", 742 | } 743 | 744 | func init() { 745 | swag.Register(SwaggerInfo.InstanceName(), SwaggerInfo) 746 | } 747 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 2 | github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc= 3 | github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE= 4 | github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= 5 | github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= 6 | github.com/PuerkitoBio/purell v1.2.0 h1:/Jdm5QfyM8zdlqT6WVZU4cfP23sot6CEHA4CS49Ezig= 7 | github.com/PuerkitoBio/purell v1.2.0/go.mod h1:OhLRTaaIzhvIyofkJfB24gokC7tM42Px5UhoT32THBk= 8 | github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= 9 | github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= 10 | github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= 11 | github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= 12 | github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM= 13 | github.com/bytedance/sonic v1.10.1 h1:7a1wuFXL1cMy7a3f7/VFcEtriuXQnUBhtoVfOZiaysc= 14 | github.com/bytedance/sonic v1.10.1/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4= 15 | github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= 16 | github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= 17 | github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d h1:77cEq6EriyTZ0g/qfRdp61a3Uu/AWrgIq2s0ClJV1g0= 18 | github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d/go.mod h1:8EPpVsBuRksnlj1mLy4AWzRNQYxauNi62uWcE3to6eA= 19 | github.com/chenzhuoyu/iasm v0.9.0 h1:9fhXjVzq5hUy2gkhhgHl95zG2cEAhw9OSGs8toWWAwo= 20 | github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog= 21 | github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= 22 | github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= 23 | github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= 24 | github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 25 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 26 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 27 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 28 | github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= 29 | github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= 30 | github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= 31 | github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= 32 | github.com/gin-contrib/gzip v0.0.1/go.mod h1:fGBJBCdt6qCZuCAOwWuFhBB4OOq9EFqlo5dEaFhhu5w= 33 | github.com/gin-contrib/gzip v0.0.6 h1:NjcunTcGAj5CO1gn4N8jHOSIeRFHIbn51z6K+xaN4d4= 34 | github.com/gin-contrib/gzip v0.0.6/go.mod h1:QOJlmV2xmayAjkNS2Y8NQsMneuRShOU/kjovCXNuzzk= 35 | github.com/gin-contrib/sse v0.0.0-20170109093832-22d885f9ecc7/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s= 36 | github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s= 37 | github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= 38 | github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= 39 | github.com/gin-gonic/gin v1.3.0/go.mod h1:7cKuhb5qV2ggCFctp2fJQ+ErvciLZrIeoOSOm6mUr7Y= 40 | github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM= 41 | github.com/gin-gonic/gin v1.7.0/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY= 42 | github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk= 43 | github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= 44 | github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= 45 | github.com/go-gorp/gorp v2.2.0+incompatible h1:xAUh4QgEeqPPhK3vxZN+bzrim1z5Av6q837gtjUlshc= 46 | github.com/go-gorp/gorp v2.2.0+incompatible/go.mod h1:7IfkAQnO7jfT/9IQ3R9wL1dFhukN6aQxzKTHnkxzA/E= 47 | github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= 48 | github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= 49 | github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= 50 | github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= 51 | github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= 52 | github.com/go-openapi/jsonpointer v0.20.0 h1:ESKJdU9ASRfaPNOPRx12IUyA1vn3R9GiE3KYD14BXdQ= 53 | github.com/go-openapi/jsonpointer v0.20.0/go.mod h1:6PGzBjjIIumbLYysB73Klnms1mwnU4G3YHOECG3CedA= 54 | github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= 55 | github.com/go-openapi/jsonreference v0.19.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= 56 | github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= 57 | github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= 58 | github.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXymS4zJbYVCZmcgkasdo= 59 | github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= 60 | github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= 61 | github.com/go-openapi/spec v0.19.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= 62 | github.com/go-openapi/spec v0.19.4/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= 63 | github.com/go-openapi/spec v0.20.9 h1:xnlYNQAwKd2VQRRfwTEI0DcK+2cbuvI/0c7jx3gA8/8= 64 | github.com/go-openapi/spec v0.20.9/go.mod h1:2OpW+JddWPrpXSCIX8eOx7lZ5iyuWj3RYR6VaaBKcWA= 65 | github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= 66 | github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= 67 | github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= 68 | github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= 69 | github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= 70 | github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU= 71 | github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= 72 | github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= 73 | github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= 74 | github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= 75 | github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= 76 | github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= 77 | github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= 78 | github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= 79 | github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= 80 | github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= 81 | github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= 82 | github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos= 83 | github.com/go-playground/validator/v10 v10.15.3 h1:S+sSpunYjNPDuXkWbK+x+bA7iXiW296KG4dL3X7xUZo= 84 | github.com/go-playground/validator/v10 v10.15.3/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= 85 | github.com/go-redis/redis v6.15.9+incompatible h1:K0pv1D7EQUjfyoMql+r/jZqCLizCGKFlFgcHWWmHQjg= 86 | github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= 87 | github.com/go-redis/redis/v7 v7.4.1 h1:PASvf36gyUpr2zdOUS/9Zqc80GbM+9BDyiJSJDDOrTI= 88 | github.com/go-redis/redis/v7 v7.4.1/go.mod h1:JDNMw23GTyLNC4GZu9njt15ctBQVn7xjRfnwdHj/Dcg= 89 | github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= 90 | github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= 91 | github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= 92 | github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= 93 | github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= 94 | github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= 95 | github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= 96 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 97 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 98 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 99 | github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 100 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 101 | github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= 102 | github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 103 | github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 104 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 105 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 106 | github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= 107 | github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= 108 | github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= 109 | github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk= 110 | github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= 111 | github.com/jackc/pgx/v5 v5.3.1 h1:Fcr8QJ1ZeLi5zsPZqQeUZhNhxfkkKBOgJuYkJHoBOtU= 112 | github.com/jackc/pgx/v5 v5.3.1/go.mod h1:t3JDKnCBlYIc0ewLF0Q7B8MXmoIaBOZj/ic7iHozM/8= 113 | github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= 114 | github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= 115 | github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= 116 | github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= 117 | github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= 118 | github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= 119 | github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= 120 | github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= 121 | github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= 122 | github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= 123 | github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= 124 | github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= 125 | github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 126 | github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= 127 | github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= 128 | github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc= 129 | github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= 130 | github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= 131 | github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg= 132 | github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= 133 | github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= 134 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 135 | github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= 136 | github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= 137 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 138 | github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= 139 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 140 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 141 | github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= 142 | github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= 143 | github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= 144 | github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= 145 | github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= 146 | github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= 147 | github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= 148 | github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= 149 | github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= 150 | github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= 151 | github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= 152 | github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= 153 | github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= 154 | github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= 155 | github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= 156 | github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= 157 | github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= 158 | github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= 159 | github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 160 | github.com/mattn/go-sqlite3 v1.14.3/go.mod h1:WVKg1VTActs4Qso6iwGbiFih2UIHo0ENGwNd0Lj+XmI= 161 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 162 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= 163 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 164 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 165 | github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 166 | github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= 167 | github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= 168 | github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe h1:iruDEfMl2E6fbMZ9s0scYfZQ84/6SPL6zC8ACM2oIL0= 169 | github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= 170 | github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= 171 | github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 172 | github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 173 | github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= 174 | github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo= 175 | github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4= 176 | github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= 177 | github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= 178 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 179 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 180 | github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= 181 | github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= 182 | github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 183 | github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= 184 | github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 185 | github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= 186 | github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= 187 | github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= 188 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 189 | github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= 190 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 191 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 192 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 193 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 194 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 195 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 196 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 197 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 198 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 199 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 200 | github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 201 | github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 202 | github.com/swaggo/files v0.0.0-20190704085106-630677cd5c14/go.mod h1:gxQT6pBGRuIGunNf/+tSOB5OHvguWi8Tbt82WOkf35E= 203 | github.com/swaggo/files v1.0.1 h1:J1bVJ4XHZNq0I46UU90611i9/YzdrF7x92oX1ig5IdE= 204 | github.com/swaggo/files v1.0.1/go.mod h1:0qXmMNH6sXNf+73t65aKeB+ApmgxdnkQzVTAj2uaMUg= 205 | github.com/swaggo/gin-swagger v1.2.0/go.mod h1:qlH2+W7zXGZkczuL+r2nEBR2JTT+/lX05Nn6vPhc7OI= 206 | github.com/swaggo/gin-swagger v1.3.2 h1:v4x39WgGCpJh9smvidElXep42uFZEiSU7hHfmCAB5+I= 207 | github.com/swaggo/gin-swagger v1.3.2/go.mod h1:8GN8KIlwgjawtEvE+B8sx3q9SPJuX/ZPxyuoFVrl6gM= 208 | github.com/swaggo/gin-swagger v1.6.0 h1:y8sxvQ3E20/RCyrXeFfg60r6H0Z+SwpTjMYsMm+zy8M= 209 | github.com/swaggo/gin-swagger v1.6.0/go.mod h1:BG00cCEy294xtVpyIAHG6+e2Qzj/xKlRdOqDkvq0uzo= 210 | github.com/swaggo/swag v1.5.1/go.mod h1:1Bl9F/ZBpVWh22nY0zmYyASPO1lI/zIwRDrpZU+tv8Y= 211 | github.com/swaggo/swag v1.6.7 h1:e8GC2xDllJZr3omJkm9YfmK0Y56+rMO3cg0JBKNz09s= 212 | github.com/swaggo/swag v1.6.7/go.mod h1:xDhTyuFIujYiN3DKWC/H/83xcfHp+UE/IzWWampG7Zc= 213 | github.com/swaggo/swag v1.16.2 h1:28Pp+8DkQoV+HLzLx8RGJZXNGKbFqnuvSbAAtoxiY04= 214 | github.com/swaggo/swag v1.16.2/go.mod h1:6YzXnDcpr0767iOejs318CwYkCQqyGer6BizOg03f+E= 215 | github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= 216 | github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= 217 | github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= 218 | github.com/ugorji/go v1.1.5-pre/go.mod h1:FwP/aQVg39TXzItUBMwnWp9T9gPQnXw4Poh4/oBQZ/0= 219 | github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= 220 | github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M= 221 | github.com/ugorji/go/codec v0.0.0-20181022190402-e5e69e061d4f/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= 222 | github.com/ugorji/go/codec v1.1.5-pre/go.mod h1:tULtS6Gy1AE1yCENaw4Vb//HLH5njI2tfCQDUqRd8fI= 223 | github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= 224 | github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= 225 | github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= 226 | github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= 227 | github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= 228 | github.com/urfave/cli/v2 v2.1.1/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ= 229 | github.com/urfave/cli/v2 v2.25.7 h1:VAzn5oq403l5pHjc4OhD54+XGO9cdKVL/7lDjF+iKUs= 230 | github.com/urfave/cli/v2 v2.25.7/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ= 231 | github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= 232 | github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= 233 | github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY= 234 | github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4= 235 | github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8= 236 | github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM= 237 | github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= 238 | github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= 239 | github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM6xpdFEAYOk8iySO56hMFq6uLyA= 240 | github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= 241 | github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 242 | go.mongodb.org/mongo-driver v1.12.1 h1:nLkghSU8fQNaK7oUmDhQFsnrtcoNy7Z6LVFKsEecqgE= 243 | go.mongodb.org/mongo-driver v1.12.1/go.mod h1:/rGBTebI3XYboVmgz+Wv3Bcbl3aD0QF9zl6kDDw18rQ= 244 | golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= 245 | golang.org/x/arch v0.5.0 h1:jpGode6huXQxcskEIpOCvrU+tzo81b6+oFLUYXWtH/Y= 246 | golang.org/x/arch v0.5.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= 247 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 248 | golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 249 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 250 | golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 251 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 252 | golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= 253 | golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck= 254 | golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= 255 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= 256 | golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 257 | golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 258 | golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 259 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 260 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 261 | golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 262 | golang.org/x/net v0.0.0-20190611141213-3f473d35a33a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 263 | golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 264 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 265 | golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 266 | golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 267 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 268 | golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 269 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 270 | golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= 271 | golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8= 272 | golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= 273 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 274 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 275 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 276 | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw= 277 | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 278 | golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= 279 | golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= 280 | golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 281 | golang.org/x/sys v0.0.0-20181228144115-9a3f9b0469bb/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 282 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 283 | golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 284 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 285 | golang.org/x/sys v0.0.0-20190610200419-93c9922d18ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 286 | golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 287 | golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 288 | golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 289 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 290 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 291 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 292 | golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 293 | golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 294 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 295 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 296 | golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 297 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 298 | golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= 299 | golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 300 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 301 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 302 | golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= 303 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 304 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 305 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 306 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 307 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 308 | golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= 309 | golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 310 | golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= 311 | golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= 312 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 313 | golang.org/x/tools v0.0.0-20190606050223-4d9ae51c2468/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 314 | golang.org/x/tools v0.0.0-20190611222205-d73e1c7e250b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 315 | golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 316 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 317 | golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= 318 | golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ= 319 | golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= 320 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 321 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 322 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 323 | google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= 324 | google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= 325 | google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= 326 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 327 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 328 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 329 | gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 330 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= 331 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 332 | gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= 333 | gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= 334 | gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y= 335 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= 336 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 337 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 338 | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 339 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 340 | gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= 341 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 342 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 343 | gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 344 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 345 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 346 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 347 | gorm.io/driver/postgres v1.5.2 h1:ytTDxxEv+MplXOfFe3Lzm7SjG09fcdb3Z/c056DTBx0= 348 | gorm.io/driver/postgres v1.5.2/go.mod h1:fmpX0m2I1PKuR7mKZiEluwrP3hbs+ps7JIGMUBpCgl8= 349 | gorm.io/driver/sqlite v1.1.3/go.mod h1:AKDgRWk8lcSQSw+9kxCJnX/yySj8G3rdwYlU57cB45c= 350 | gorm.io/gorm v1.20.1/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw= 351 | gorm.io/gorm v1.23.0/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk= 352 | gorm.io/gorm v1.25.4 h1:iyNd8fNAe8W9dvtlgeRI5zSVZPsq3OpcTu37cYcpCmw= 353 | gorm.io/gorm v1.25.4/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k= 354 | gorm.io/plugin/soft_delete v1.2.1 h1:qx9D/c4Xu6w5KT8LviX8DgLcB9hkKl6JC9f44Tj7cGU= 355 | gorm.io/plugin/soft_delete v1.2.1/go.mod h1:Zv7vQctOJTGOsJ/bWgrN1n3od0GBAZgnLjEx+cApLGk= 356 | nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= 357 | rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= 358 | sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= 359 | sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= 360 | --------------------------------------------------------------------------------