├── .air.toml ├── .env.sample ├── .gitignore ├── README.md ├── api └── v1 │ ├── login.go │ ├── password.go │ └── register.go ├── config └── config.go ├── db ├── db.go ├── entity │ └── user.go └── repository │ └── user.go ├── docs └── .keep ├── go.mod ├── go.sum ├── log └── log.go ├── main.go ├── router └── router.go └── tmp └── .keep /.air.toml: -------------------------------------------------------------------------------- 1 | root = "." 2 | tmp_dir = "tmp" 3 | 4 | [build] 5 | bin = "./tmp/air/main" 6 | cmd = "go build -o ./tmp/air/main ." 7 | delay = 1000 8 | exclude_dir = ["assets", "tmp", "vendor"] 9 | exclude_file = [] 10 | exclude_regex = [] 11 | exclude_unchanged = false 12 | follow_symlink = false 13 | full_bin = "" 14 | include_dir = [] 15 | include_ext = ["go", "tpl", "tmpl", "html"] 16 | kill_delay = "0s" 17 | log = "logs/air-build-errors.log" 18 | send_interrupt = false 19 | stop_on_error = true 20 | 21 | [color] 22 | app = "" 23 | build = "yellow" 24 | main = "magenta" 25 | runner = "green" 26 | watcher = "cyan" 27 | 28 | [log] 29 | time = false 30 | 31 | [misc] 32 | clean_on_exit = false 33 | -------------------------------------------------------------------------------- /.env.sample: -------------------------------------------------------------------------------- 1 | APP_ENV=development 2 | APP_NAME=fibo 3 | APP_HOST=localhost 4 | APP_PORT=3000 5 | APP_DEBUG=true 6 | 7 | DB_DRIVER= 8 | DB_HOST= 9 | DB_USER= 10 | DB_PASS= 11 | DB_NAME= 12 | DB_PORT=3306 13 | DB_TABLE_PREFIX=tbl_ 14 | 15 | JWT_EXPIRE=3600 16 | JWT_SECRET=1894cde6c936a294a478cff0a9227fd276d86df6573b51af5dc59c9064edf426 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .env 2 | sqlite.db 3 | 4 | tmp/* 5 | !tmp/.keep 6 | 7 | docs/* 8 | !docs/.keep -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Fibo - Go Fiber API Boilerplate 2 | > A starter project with Golang, Fiber and Gorm 3 | 4 | Golang Fiber boilerplate with MySQL resource. Supports multiple configuration environments. 5 | 6 | ### Features 7 | - Basic Auth with Login, Register 8 | - Email confirmation on Registration 9 | - Email forgot password 10 | - REST API Authentication with JWT 11 | - PostgresSQL or MySQL with GORM V2 12 | - Logging via zap with file rotation 13 | - Use of Redis for Cache and Session 14 | - Hot Reload with Air 15 | - Easy Config Settings based on .env 16 | - Setup for Docker 17 | - Easy and Almost Zero Downtime Production Deployment 18 | 19 | ### Boilerplate structure 20 | ``` 21 | ├── api 22 | | └── v1 23 | │ ├── login.go 24 | │ ├── password.go 25 | | └── register.go 26 | ├── config 27 | │ └── config.go 28 | ├── db 29 | │ └── db.go 30 | ├── docs 31 | │ ├── docs.go 32 | │ ├── swagger.json 33 | │ └── swagger.yaml 34 | ├── log 35 | │ └── log.go 36 | ├── router 37 | │ └── router.go 38 | ├── tmp 39 | ├── .air.toml 40 | ├── main.go 41 | ``` 42 | 43 | ### Installation 44 | - Clone the repo git clone https://github.com/toandp/fibo.git 45 | - Make sure you have installed: Redis, MySQL or Postgres 46 | - Copy .env.sample to .env 47 | 48 | ### Development 49 | ```bash 50 | air -c .air.toml 51 | ``` 52 | Go to [http://localhost:3000/](http://localhost:3000/) 53 | 54 | ### Production 55 | ```bash 56 | docker build -t fibo . 57 | docker run -d -p 3000:3000 fibo 58 | ``` 59 | 60 | Thanks to following libraries: 61 | 62 | * [Fiber](https://github.com/gofiber/fiber/v2) 63 | * [Gorm](https://github.com/go-gorm/gorm) 64 | * [Zap](https://github.com/uber-go/zap) 65 | * [Air](https://github.com/cosmtrek/air) -------------------------------------------------------------------------------- /api/v1/login.go: -------------------------------------------------------------------------------- 1 | package apiv1 2 | 3 | import ( 4 | "github.com/gofiber/fiber/v2" 5 | "github.com/gookit/validate" 6 | "golang.org/x/crypto/bcrypt" 7 | "team1.asia/fibo/config" 8 | "team1.asia/fibo/db" 9 | "team1.asia/fibo/db/entity" 10 | "team1.asia/fibo/db/repository" 11 | "team1.asia/fibo/log" 12 | ) 13 | 14 | type M struct{} 15 | 16 | // Login is a function to authentication from database. 17 | // @Summary The user authentication 18 | // @Description The user authentication 19 | // @Tags auth 20 | // @Accept json 21 | // @Produce json 22 | // @Success 200 {object} M{} 23 | // @Failure 400 {object} M{} 24 | // @Failure 401 {object} M{} 25 | // @Router /api/v1/login [post] 26 | func Login(c *fiber.Ctx) error { 27 | user := c.Locals("user").(*entity.User) 28 | token := user.CreateJWTToken(config.App.JWT.Secret) 29 | 30 | c.Append("X-Access-Token", token.Hash) 31 | 32 | return c.JSON(fiber.Map{ 33 | "data": user, 34 | }) 35 | } 36 | 37 | // Validate the POST login request. 38 | func ValidateLoginRequest(c *fiber.Ctx) error { 39 | var data entity.UserLoginForm 40 | 41 | if err := c.BodyParser(&data); err != nil { 42 | log.Error(err.Error()) 43 | return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ 44 | "error": err.Error(), 45 | }) 46 | } 47 | 48 | v := validate.Struct(data) 49 | 50 | if !v.Validate() { 51 | log.Error(v.Errors.String()) 52 | return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ 53 | "error": v.Errors, 54 | }) 55 | } 56 | 57 | repo := repository.New(db.ORM) 58 | user := repo.GetByUsername(data.Username) 59 | 60 | if user == nil { 61 | log.Error("User not found.") 62 | return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{ 63 | "error": "User not found.", 64 | }) 65 | } 66 | 67 | match := ComparePasswordHash(data.Password, user.PasswordHash) 68 | 69 | if !match { 70 | log.Error("Invalid email or password.") 71 | return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{ 72 | "error": "Invalid email or password.", 73 | }) 74 | } 75 | 76 | c.Locals("user", user) 77 | 78 | return c.Next() 79 | } 80 | 81 | // Compares a bcrypt hashed password with user password. 82 | func ComparePasswordHash(password string, hash string) bool { 83 | err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password)) 84 | 85 | return err != nil 86 | } 87 | -------------------------------------------------------------------------------- /api/v1/password.go: -------------------------------------------------------------------------------- 1 | package apiv1 2 | -------------------------------------------------------------------------------- /api/v1/register.go: -------------------------------------------------------------------------------- 1 | package apiv1 2 | 3 | import ( 4 | "github.com/gofiber/fiber/v2" 5 | "github.com/gookit/validate" 6 | "golang.org/x/crypto/bcrypt" 7 | "team1.asia/fibo/config" 8 | "team1.asia/fibo/db" 9 | "team1.asia/fibo/db/entity" 10 | "team1.asia/fibo/db/repository" 11 | "team1.asia/fibo/log" 12 | ) 13 | 14 | type H struct{} 15 | 16 | // Register is a function to create the user. 17 | // @Summary The user creator 18 | // @Description The user creator 19 | // @Tags auth 20 | // @Accept json 21 | // @Produce json 22 | // @Success 200 {object} H{} 23 | // @Failure 400 {object} H{} 24 | // @Failure 401 {object} H{} 25 | // @Router /api/v1/register [post] 26 | func Register(c *fiber.Ctx) error { 27 | user := c.Locals("user").(*entity.User) 28 | repo := c.Locals("repository").(repository.UserRepositoryInterface) 29 | 30 | repo.Create(user) 31 | 32 | token := user.CreateJWTToken(config.App.JWT.Secret) 33 | 34 | c.Append("X-Access-Token", token.Hash) 35 | 36 | return c.JSON(fiber.Map{ 37 | "data": user, 38 | }) 39 | } 40 | 41 | // Validate the POST register request. 42 | func ValidateRegisterRequest(c *fiber.Ctx) error { 43 | var data entity.UserRegisterForm 44 | 45 | if err := c.BodyParser(&data); err != nil { 46 | return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ 47 | "error": err.Error(), 48 | }) 49 | } 50 | 51 | v := validate.Struct(data) 52 | 53 | if !v.Validate() { 54 | return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ 55 | "error": v.Errors, 56 | }) 57 | } 58 | 59 | repo := repository.New(db.ORM) 60 | 61 | record := repo.GetByUsername(data.Username) 62 | 63 | if record != nil { 64 | return c.Status(fiber.StatusUnprocessableEntity).JSON(fiber.Map{ 65 | "error": "User already exists", 66 | }) 67 | } 68 | 69 | var user entity.User 70 | 71 | user.Username = data.Username 72 | user.PasswordHash = CreatePasswordHash(data.Password) 73 | 74 | c.Locals("user", &user) 75 | c.Locals("repository", repo) 76 | 77 | return c.Next() 78 | } 79 | 80 | // Create the password hash. 81 | func CreatePasswordHash(password string) string { 82 | hash, err := bcrypt.GenerateFromPassword([]byte(password), 10) 83 | 84 | if err != nil { 85 | log.Error(err.Error()) 86 | panic(err) 87 | } 88 | 89 | return string(hash) 90 | } 91 | -------------------------------------------------------------------------------- /config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/ilyakaznacheev/cleanenv" 7 | "github.com/joho/godotenv" 8 | ) 9 | 10 | // AppConfig is a struct holding the application settings. 11 | type AppConfig struct { 12 | Env string `env:"APP_ENV" env-default:"development"` 13 | Debug bool `env:"APP_DEBUG" env-default:"false"` 14 | Timezone string `env:"APP_TZ" env-default:"Asia/Ho_Chi_Minh"` 15 | Server ServerConfig 16 | DB DatabaseConfig 17 | JWT JWTConfig 18 | Log LogConfig 19 | } 20 | 21 | // ServerConfig is a struct holding the server settings. 22 | type ServerConfig struct { 23 | Name string `env:"APP_NAME" env-default:"fibo"` 24 | Host string `env:"APP_HOST" env-default:"localhost"` 25 | Port string `env:"APP_PORT" env-default:"8080"` 26 | ProxyHeader string `mapstructure:"PROXY_HEADER" env:"PROXY_HEADER" env-default:"*"` 27 | UploadSize int `mapstructure:"UPLOAD_SIZE" env:"UPLOAD_SIZE" env-default:"400"` 28 | } 29 | 30 | // DatabaseConfig is a struct holding the database settings. 31 | type DatabaseConfig struct { 32 | Driver string `env:"DB_DRIVER" env-default:"mysql"` 33 | Host string `env:"DB_HOST" env-default:"127.0.0.1"` 34 | Username string `env:"DB_USER" env-default:"root"` 35 | Password string `env:"DB_PASS" env-default:""` 36 | DBName string `env:"DB_NAME" env-default:"fibo_dev"` 37 | Port int `env:"DB_PORT" env-default:"3306"` 38 | TablePrefix string `env:"DB_TABLE_PREFIX" env-default:"tbl_"` 39 | SSLMode string `env:"DB_SSL_MODE" env-default:"disable"` 40 | SQLiteFile string `env:"DB_SQLITE_FILE" env-default:"sqlite.db"` 41 | } 42 | 43 | // JWTConfig is a struct holding the JWT settings. 44 | type JWTConfig struct { 45 | Expire int64 `env:"JWT_EXPIRE" env-default:"3600"` 46 | Secret string `env:"JWT_SECRET" env-default:"1894cde6c936a294a478cff0a9227fd276d86df6573b51af5dc59c9064edf426"` 47 | } 48 | 49 | // LogConfig is a struct holding the JWT settings. 50 | type LogConfig struct { 51 | FilePath string `env:"LOG_FILE_FORMAT" env-default:"./tmp/logs/%s-%s.log"` 52 | } 53 | 54 | var App AppConfig 55 | 56 | // Read configuration from the environment variables. 57 | func LoadConfigFromEnv() { 58 | var err error 59 | 60 | // Load env variables 61 | err = godotenv.Load() 62 | 63 | if err != nil { 64 | fmt.Println(err) 65 | panic(err) 66 | } 67 | 68 | // Bind configuration 69 | err = cleanenv.ReadEnv(&App) 70 | 71 | if err != nil { 72 | fmt.Println(err) 73 | panic(err) 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /db/db.go: -------------------------------------------------------------------------------- 1 | package db 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | 7 | "gorm.io/driver/mysql" 8 | "gorm.io/driver/postgres" 9 | "gorm.io/driver/sqlite" 10 | "gorm.io/gorm" 11 | "gorm.io/gorm/schema" 12 | "gorm.io/plugin/dbresolver" 13 | "moul.io/zapgorm2" 14 | "team1.asia/fibo/config" 15 | "team1.asia/fibo/db/entity" 16 | "team1.asia/fibo/log" 17 | ) 18 | 19 | var ORM *gorm.DB 20 | 21 | // Establishes a DB connection. 22 | func Connect() { 23 | var ( 24 | err error 25 | dsn string 26 | cfg *gorm.Config 27 | ) 28 | 29 | logger := zapgorm2.New(log.Zap) 30 | logger.SetAsDefault() 31 | 32 | cfg = &gorm.Config{ 33 | NamingStrategy: schema.NamingStrategy{ 34 | TablePrefix: config.App.DB.TablePrefix, 35 | SingularTable: false, 36 | }, 37 | Logger: logger, 38 | } 39 | 40 | switch config.App.DB.Driver { 41 | case "mysql": 42 | dsn = fmt.Sprintf( 43 | "%s:%s@tcp(%s:%d)/%s?charset=utf8&parseTime=True&loc=Local", 44 | config.App.DB.Username, 45 | config.App.DB.Password, 46 | config.App.DB.Host, 47 | config.App.DB.Port, 48 | config.App.DB.DBName, 49 | ) 50 | 51 | ORM, err = gorm.Open(mysql.Open(dsn), cfg) 52 | case "postgres": 53 | dsn = fmt.Sprintf( 54 | "host=%s user=%s password=%s dbname=%s port=%d sslmode=%s TimeZone=%s", 55 | config.App.DB.Host, 56 | config.App.DB.Username, 57 | config.App.DB.Password, 58 | config.App.DB.DBName, 59 | config.App.DB.Port, 60 | config.App.DB.SSLMode, 61 | config.App.Timezone, 62 | ) 63 | 64 | ORM, err = gorm.Open(postgres.Open(dsn), cfg) 65 | default: 66 | ORM, err = gorm.Open(sqlite.Open(config.App.DB.SQLiteFile), cfg) 67 | } 68 | 69 | if err != nil { 70 | log.Error(err.Error()) 71 | panic(err) 72 | } 73 | 74 | ORM.Use( 75 | dbresolver.Register(dbresolver.Config{}). 76 | SetConnMaxLifetime(24 * time.Hour). 77 | SetMaxIdleConns(100). 78 | SetMaxOpenConns(100), 79 | ) 80 | } 81 | 82 | // Execute the DB migration. 83 | func Migrate() { 84 | log.Info("Initiating migration...") 85 | 86 | err := ORM.Migrator().AutoMigrate( 87 | &entity.User{}, 88 | ) 89 | 90 | if err != nil { 91 | log.Error(err.Error()) 92 | panic(err) 93 | } 94 | 95 | log.Info("Migration Completed.") 96 | } 97 | -------------------------------------------------------------------------------- /db/entity/user.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/golang-jwt/jwt/v4" 7 | "gorm.io/gorm" 8 | "team1.asia/fibo/config" 9 | "team1.asia/fibo/log" 10 | ) 11 | 12 | type JWTToken struct { 13 | Hash string `json:"access_token"` 14 | Expire int64 `json:"expires_in"` 15 | } 16 | 17 | type User struct { 18 | gorm.Model 19 | Username string `json:"username" gorm:"username,index,unique"` 20 | PasswordHash string `json:"-" gorm:"password"` 21 | } 22 | 23 | type UserLoginForm struct { 24 | Username string `json:"username" form:"username" validate:"required"` 25 | Password string `json:"password" form:"password" validate:"required"` 26 | } 27 | 28 | type UserRegisterForm struct { 29 | Username string `json:"username" form:"username" validate:"required"` 30 | Password string `json:"password" form:"password" validate:"required"` 31 | CPassword string `json:"c_password" form:"c_password" validate:"required|eq_field:password"` 32 | } 33 | 34 | // Create the user JWT token. 35 | func (u *User) CreateJWTToken(secret string, expires ...int64) *JWTToken { 36 | token := jwt.New(jwt.SigningMethodHS256) 37 | claims := token.Claims.(jwt.MapClaims) 38 | 39 | expire := config.App.JWT.Expire 40 | 41 | if len(expires) > 0 { 42 | expire = expires[0] 43 | } 44 | 45 | expiresIn := time.Now().Add(time.Duration(expire) * time.Second).Unix() 46 | 47 | claims["username"] = u.Username 48 | claims["exp"] = expiresIn 49 | 50 | tokenHash, err := token.SignedString([]byte(secret)) 51 | 52 | if err != nil { 53 | log.Error(err.Error()) 54 | panic(err) 55 | } 56 | 57 | return &JWTToken{ 58 | Hash: tokenHash, 59 | Expire: expiresIn, 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /db/repository/user.go: -------------------------------------------------------------------------------- 1 | package repository 2 | 3 | import ( 4 | "gorm.io/gorm" 5 | "team1.asia/fibo/db/entity" 6 | "team1.asia/fibo/log" 7 | ) 8 | 9 | type UserRepository struct { 10 | ORM *gorm.DB 11 | } 12 | 13 | type UserRepositoryInterface interface { 14 | GetByUsername(username string) *entity.User 15 | Create(user *entity.User) *entity.User 16 | } 17 | 18 | // Create a new user repository instance. 19 | func New(orm *gorm.DB) UserRepositoryInterface { 20 | return &UserRepository{ 21 | ORM: orm, 22 | } 23 | } 24 | 25 | // Get the user by username. 26 | func (repo *UserRepository) GetByUsername(username string) *entity.User { 27 | var user entity.User 28 | 29 | if err := repo.ORM.Where(&entity.User{Username: username}).First(&user).Error; err != nil { 30 | log.Error(err.Error()) 31 | panic(err) 32 | } 33 | 34 | return &user 35 | } 36 | 37 | // Create a new user. 38 | func (repo *UserRepository) Create(user *entity.User) *entity.User { 39 | repo.ORM.Create(&user) 40 | 41 | return user 42 | } 43 | -------------------------------------------------------------------------------- /docs/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/toandp/fibo/40e0d18d8072fd5a4a93a356fcf62ba0c838fc93/docs/.keep -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module team1.asia/fibo 2 | 3 | go 1.17 4 | 5 | require ( 6 | github.com/gofiber/fiber/v2 v2.20.2 7 | github.com/gofiber/jwt/v3 v3.2.0 8 | github.com/joho/godotenv v1.4.0 9 | ) 10 | 11 | require ( 12 | github.com/KyleBanks/depth v1.2.1 // indirect 13 | github.com/PuerkitoBio/purell v1.1.1 // indirect 14 | github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect 15 | github.com/arsmn/fiber-swagger/v2 v2.20.0 // indirect 16 | github.com/cpuguy83/go-md2man/v2 v2.0.1 // indirect 17 | github.com/ghodss/yaml v1.0.0 // indirect 18 | github.com/go-openapi/jsonpointer v0.19.5 // indirect 19 | github.com/go-openapi/jsonreference v0.19.6 // indirect 20 | github.com/go-openapi/spec v0.20.4 // indirect 21 | github.com/go-openapi/swag v0.19.15 // indirect 22 | github.com/jackc/chunkreader/v2 v2.0.1 // indirect 23 | github.com/jackc/pgconn v1.10.0 // indirect 24 | github.com/jackc/pgio v1.0.0 // indirect 25 | github.com/jackc/pgpassfile v1.0.0 // indirect 26 | github.com/jackc/pgproto3/v2 v2.1.1 // indirect 27 | github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect 28 | github.com/jackc/pgtype v1.8.1 // indirect 29 | github.com/jackc/pgx/v4 v4.13.0 // indirect 30 | github.com/josharian/intern v1.0.0 // indirect 31 | github.com/mailru/easyjson v0.7.7 // indirect 32 | github.com/mattn/go-sqlite3 v1.14.9 // indirect 33 | github.com/russross/blackfriday/v2 v2.1.0 // indirect 34 | github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect 35 | github.com/swaggo/files v0.0.0-20210815190702-a29dd2bc99b2 // indirect 36 | github.com/swaggo/swag v1.7.4 // indirect 37 | github.com/urfave/cli/v2 v2.3.0 // indirect 38 | go.uber.org/atomic v1.7.0 // indirect 39 | go.uber.org/multierr v1.6.0 // indirect 40 | golang.org/x/net v0.0.0-20211020060615-d418f374d309 // indirect 41 | golang.org/x/text v0.3.7 // indirect 42 | golang.org/x/tools v0.1.7 // indirect 43 | gorm.io/driver/postgres v1.2.1 // indirect 44 | gorm.io/driver/sqlite v1.2.3 // indirect 45 | ) 46 | 47 | require ( 48 | github.com/BurntSushi/toml v0.3.1 // indirect 49 | github.com/golang-jwt/jwt/v4 v4.1.0 50 | github.com/gookit/filter v1.1.2 // indirect 51 | github.com/gookit/goutil v0.3.12 // indirect 52 | github.com/mitchellh/go-homedir v1.1.0 // indirect 53 | gopkg.in/yaml.v2 v2.4.0 // indirect 54 | olympos.io/encoding/edn v0.0.0-20200308123125-93e3b8dd0e24 // indirect 55 | ) 56 | 57 | require ( 58 | github.com/andybalholm/brotli v1.0.3 // indirect 59 | github.com/go-sql-driver/mysql v1.6.0 // indirect 60 | github.com/gookit/validate v1.2.11 61 | github.com/ilyakaznacheev/cleanenv v1.2.5 62 | github.com/jinzhu/inflection v1.0.0 // indirect 63 | github.com/jinzhu/now v1.1.2 // indirect 64 | github.com/klauspost/compress v1.13.6 // indirect 65 | github.com/valyala/bytebufferpool v1.0.0 // indirect 66 | github.com/valyala/fasthttp v1.31.0 // indirect 67 | github.com/valyala/tcplisten v1.0.0 // indirect 68 | go.uber.org/zap v1.19.1 69 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 70 | golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359 // indirect 71 | gorm.io/driver/mysql v1.1.2 72 | gorm.io/gorm v1.22.2 73 | gorm.io/plugin/dbresolver v1.1.0 74 | moul.io/zapgorm2 v1.1.0 75 | ) 76 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= 2 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 3 | github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc= 4 | github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE= 5 | github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= 6 | github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= 7 | github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= 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/andybalholm/brotli v1.0.2/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= 11 | github.com/andybalholm/brotli v1.0.3 h1:fpcw+r1N1h0Poc1F/pHbW40cUm/lMEQslZtCkBQ0UnM= 12 | github.com/andybalholm/brotli v1.0.3/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= 13 | github.com/arsmn/fiber-swagger/v2 v2.20.0 h1:R52mPrpLeisAPg94D3sFmghfx1miselfHozR1seszB4= 14 | github.com/arsmn/fiber-swagger/v2 v2.20.0/go.mod h1:UztCvFUeytKZPp5Lg8Y6eEw16bhtKpLzAEhGxcc2IB0= 15 | github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= 16 | github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= 17 | github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= 18 | github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= 19 | github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= 20 | github.com/cpuguy83/go-md2man/v2 v2.0.1 h1:r/myEWzV9lfsM1tFLgDyu0atFtJ1fXn261LKYj/3DxU= 21 | github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= 22 | github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= 23 | github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 24 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 25 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 26 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 27 | github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= 28 | github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= 29 | github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= 30 | github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= 31 | github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= 32 | github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= 33 | github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= 34 | github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg= 35 | github.com/go-openapi/jsonreference v0.19.6 h1:UBIxjkht+AWIgYzCDSv2GN+E/togfwXUJFRTWhl2Jjs= 36 | github.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns= 37 | github.com/go-openapi/spec v0.20.3/go.mod h1:gG4F8wdEDN+YPBMVnzE85Rbhf+Th2DTvA9nFPQ5AYEg= 38 | github.com/go-openapi/spec v0.20.4 h1:O8hJrt0UMnhHcluhIdUgCLRWyM2x7QkBXRvOs7m+O1M= 39 | github.com/go-openapi/spec v0.20.4/go.mod h1:faYFR1CvsJZ0mNsmsphTMSoRrNV3TEDoAM7FOEWeq8I= 40 | github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= 41 | github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= 42 | github.com/go-openapi/swag v0.19.15 h1:D2NRCBzS9/pEY3gP9Nl8aDqGUcPFrwG2p+CNFrLyrCM= 43 | github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= 44 | github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= 45 | github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= 46 | github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= 47 | github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= 48 | github.com/gofiber/fiber/v2 v2.20.1/go.mod h1:/LdZHMUXZvTTo7gU4+b1hclqCAdoQphNQ9bi9gutPyI= 49 | github.com/gofiber/fiber/v2 v2.20.2 h1:dqizbjO1pCmH6K+b+kBk7TCJK4rmgjJXvX8/MZDbK60= 50 | github.com/gofiber/fiber/v2 v2.20.2/go.mod h1:/LdZHMUXZvTTo7gU4+b1hclqCAdoQphNQ9bi9gutPyI= 51 | github.com/gofiber/jwt/v3 v3.2.0 h1:brHGfuuAJI2NxdPQO0Yoa7L01I0Uc/CKZ3Z2lYE5W30= 52 | github.com/gofiber/jwt/v3 v3.2.0/go.mod h1:Z05kGvvdRqbWMvb3uYmAPwfFyCV8/n/QVorzq4XjwvU= 53 | github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= 54 | github.com/golang-jwt/jwt/v4 v4.1.0 h1:XUgk2Ex5veyVFVeLm0xhusUTQybEbexJXrvPNOKkSY0= 55 | github.com/golang-jwt/jwt/v4 v4.1.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= 56 | github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 57 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 58 | github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= 59 | github.com/gookit/color v1.3.8 h1:w2WcSwaCa1ojRWO60Mm4GJUJomBNKR9G+x9DwaaCL1c= 60 | github.com/gookit/color v1.3.8/go.mod h1:R3ogXq2B9rTbXoSHJ1HyUVAZ3poOJHpd9nQmyGZsfvQ= 61 | github.com/gookit/filter v1.1.2 h1:mp6zSRaRhGuoGZNUlZR4W0/1OTwKRUI5qCXEtD02BR0= 62 | github.com/gookit/filter v1.1.2/go.mod h1:pVXLLDD+A8yH9GRztq2Cp7zwZocnuTUpbZs9Q+awAKM= 63 | github.com/gookit/goutil v0.3.12 h1:FG7aYlftz17uq8szHlvR/Ao6k8wXkEWhiYh2lkk8Xxg= 64 | github.com/gookit/goutil v0.3.12/go.mod h1:ITj7Lw0muhJNOX+QRa+j+HH0+RNoQVuTmZx5d5LE1vE= 65 | github.com/gookit/validate v1.2.11 h1:zUMsezhMrW3Cy8St3cQJgCKB1ZIbKOWK8e7WMSuVIRc= 66 | github.com/gookit/validate v1.2.11/go.mod h1:wXo0Vr+AzFUCEUCbTFXgKlPfT+V/V0wPK3zLp49jQq0= 67 | github.com/ilyakaznacheev/cleanenv v1.2.5 h1:/SlcF9GaIvefWqFJzsccGG/NJdoaAwb7Mm7ImzhO3DM= 68 | github.com/ilyakaznacheev/cleanenv v1.2.5/go.mod h1:/i3yhzwZ3s7hacNERGFwvlhwXMDcaqwIzmayEhbRplk= 69 | github.com/jackc/chunkreader v1.0.0 h1:4s39bBR8ByfqH+DKm8rQA3E1LHZWB9XWcrz8fqaZbe0= 70 | github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= 71 | github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= 72 | github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8= 73 | github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= 74 | github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA= 75 | github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE= 76 | github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s= 77 | github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o= 78 | github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY= 79 | github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI= 80 | github.com/jackc/pgconn v1.10.0 h1:4EYhlDVEMsJ30nNj0mmgwIUXoq7e9sMJrVC2ED6QlCU= 81 | github.com/jackc/pgconn v1.10.0/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI= 82 | github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE= 83 | github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8= 84 | github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE= 85 | github.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd/go.mod h1:hrBW0Enj2AZTNpt/7Y5rr2xe/9Mn757Wtb2xeBzPv2c= 86 | github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak= 87 | github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= 88 | github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= 89 | github.com/jackc/pgproto3 v1.1.0 h1:FYYE4yRw+AgI8wXIinMlNjBbp/UitDJwfj5LqqewP1A= 90 | github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78= 91 | github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA= 92 | github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg= 93 | github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= 94 | github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= 95 | github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= 96 | github.com/jackc/pgproto3/v2 v2.1.1 h1:7PQ/4gLoqnl87ZxL7xjO0DR5gYuviDCZxQJsUlFW1eI= 97 | github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= 98 | github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b h1:C8S2+VttkHFdOOCXJe+YGfa4vHYwlt4Zx+IVXQ97jYg= 99 | github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= 100 | github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg= 101 | github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc= 102 | github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw= 103 | github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM= 104 | github.com/jackc/pgtype v1.8.1 h1:9k0IXtdJXHJbyAWQgbWr1lU+MEhPXZz6RIXxfR5oxXs= 105 | github.com/jackc/pgtype v1.8.1/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4= 106 | github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y= 107 | github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM= 108 | github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc= 109 | github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs= 110 | github.com/jackc/pgx/v4 v4.13.0 h1:JCjhT5vmhMAf/YwBHLvrBn4OGdIQBiFG6ym8Zmdx570= 111 | github.com/jackc/pgx/v4 v4.13.0/go.mod h1:9P4X524sErlaxj0XSGZk7s+LD0eOyu1ZDUrrpznYDF0= 112 | github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= 113 | github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= 114 | github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= 115 | github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= 116 | github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= 117 | github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= 118 | github.com/jinzhu/now v1.1.2 h1:eVKgfIdy9b6zbWBMgFpfDPoAMifwSZagU9HmEU6zgiI= 119 | github.com/jinzhu/now v1.1.2/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= 120 | github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= 121 | github.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg= 122 | github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= 123 | github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= 124 | github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= 125 | github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 126 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 127 | github.com/klauspost/compress v1.13.4/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= 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/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 131 | github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 132 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 133 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 134 | github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= 135 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 136 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 137 | github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= 138 | github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= 139 | github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= 140 | github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= 141 | github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= 142 | github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= 143 | github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= 144 | github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= 145 | github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= 146 | github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= 147 | github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= 148 | github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= 149 | github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= 150 | github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= 151 | github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= 152 | github.com/mattn/go-sqlite3 v1.14.9 h1:10HX2Td0ocZpYEjhilsuo6WWtUqttj2Kb0KtD86/KYA= 153 | github.com/mattn/go-sqlite3 v1.14.9/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= 154 | github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= 155 | github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 156 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 157 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 158 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 159 | github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 160 | github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= 161 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 162 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 163 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 164 | github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 165 | github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= 166 | github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= 167 | github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= 168 | github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 169 | github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= 170 | github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 171 | github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= 172 | github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= 173 | github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= 174 | github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= 175 | github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= 176 | github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= 177 | github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= 178 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 179 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 180 | github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= 181 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 182 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 183 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 184 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 185 | github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= 186 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 187 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 188 | github.com/swaggo/files v0.0.0-20210815190702-a29dd2bc99b2 h1:+iNTcqQJy0OZ5jk6a5NLib47eqXK8uYcPX+O4+cBpEM= 189 | github.com/swaggo/files v0.0.0-20210815190702-a29dd2bc99b2/go.mod h1:lKJPbtWzJ9JhsTN1k1gZgleJWY/cqq0psdoMmaThG3w= 190 | github.com/swaggo/swag v1.7.3/go.mod h1:zD8h6h4SPv7t3l+4BKdRquqW1ASWjKZgT6Qv9z3kNqI= 191 | github.com/swaggo/swag v1.7.4 h1:up+ixy8yOqJKiFcuhMgkuYuF4xnevuhnFAXXF8OSfNg= 192 | github.com/swaggo/swag v1.7.4/go.mod h1:zD8h6h4SPv7t3l+4BKdRquqW1ASWjKZgT6Qv9z3kNqI= 193 | github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M= 194 | github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= 195 | github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= 196 | github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= 197 | github.com/valyala/fasthttp v1.29.0/go.mod h1:2rsYD01CKFrjjsvFxx75KlEUNpWNBY9JWD3K/7o2Cus= 198 | github.com/valyala/fasthttp v1.31.0 h1:lrauRLII19afgCs2fnWRJ4M5IkV0lo2FqA61uGkNBfE= 199 | github.com/valyala/fasthttp v1.31.0/go.mod h1:2rsYD01CKFrjjsvFxx75KlEUNpWNBY9JWD3K/7o2Cus= 200 | github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= 201 | github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= 202 | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 203 | github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= 204 | github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= 205 | go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= 206 | go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= 207 | go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= 208 | go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= 209 | go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= 210 | go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= 211 | go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= 212 | go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= 213 | go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= 214 | go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= 215 | go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= 216 | go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= 217 | go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= 218 | go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= 219 | go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= 220 | go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= 221 | go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ= 222 | go.uber.org/zap v1.19.1 h1:ue41HOKd1vGURxrmeKIgELGb3jPW9DMUDGtsinblHwI= 223 | go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI= 224 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 225 | golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= 226 | golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 227 | golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 228 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 229 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 230 | golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= 231 | golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= 232 | golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= 233 | golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 234 | golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 235 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg= 236 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 237 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 238 | golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= 239 | golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 240 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 241 | golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 242 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 243 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 244 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 245 | golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 246 | golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 247 | golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 248 | golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 249 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 250 | golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= 251 | golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM= 252 | golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 253 | golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 254 | golang.org/x/net v0.0.0-20211020060615-d418f374d309 h1:A0lJIi+hcTR6aajJH4YqKWwohY4aW9RO7oRMcdv+HKI= 255 | golang.org/x/net v0.0.0-20211020060615-d418f374d309/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 256 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 257 | golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 258 | golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 259 | golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 260 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 261 | golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 262 | golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 263 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 264 | golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 265 | golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 266 | golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 267 | golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 268 | golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 269 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 270 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 271 | golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 272 | golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 273 | golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 274 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 275 | golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 276 | golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 277 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 278 | golang.org/x/sys v0.0.0-20211015200801-69063c4bb744 h1:KzbpndAYEM+4oHRp9JmB2ewj0NHHxO3Z0g7Gus2O1kk= 279 | golang.org/x/sys v0.0.0-20211015200801-69063c4bb744/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 280 | golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359 h1:2B5p2L5IfGiD7+b9BOoRMC6DgObAVZV+Fsp050NqXik= 281 | golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 282 | golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= 283 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 284 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 285 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 286 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 287 | golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 288 | golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 289 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 290 | golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= 291 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 292 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 293 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 294 | golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 295 | golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 296 | golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 297 | golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 298 | golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 299 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 300 | golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 301 | golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= 302 | golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= 303 | golang.org/x/tools v0.1.7 h1:6j8CgantCy3yc8JGBqkDLMKWqZ0RDU2g1HVgacojGWQ= 304 | golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= 305 | golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 306 | golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 307 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 308 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 309 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 310 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 311 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 312 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 313 | gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 314 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 315 | gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= 316 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 317 | gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 318 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 319 | gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= 320 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 321 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= 322 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 323 | gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 324 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 325 | gorm.io/driver/mysql v1.0.3/go.mod h1:twGxftLBlFgNVNakL7F+P/x9oYqoymG3YYT8cAfI9oI= 326 | gorm.io/driver/mysql v1.1.2 h1:OofcyE2lga734MxwcCW9uB4mWNXMr50uaGRVwQL2B0M= 327 | gorm.io/driver/mysql v1.1.2/go.mod h1:4P/X9vSc3WTrhTLZ259cpFd6xKNYiSSdSZngkSBGIMM= 328 | gorm.io/driver/postgres v1.2.1 h1:JDQKnF7MC51dgL09Vbydc5kl83KkVDlcXfSPJ+xhh68= 329 | gorm.io/driver/postgres v1.2.1/go.mod h1:SHRZhu+D0tLOHV5qbxZRUM6kBcf3jp/kxPz2mYMTsNY= 330 | gorm.io/driver/sqlite v1.2.3 h1:OwKm0xRAnsZMWAl5BtXJ9BsXAZHIt802DOTVMQuzWN8= 331 | gorm.io/driver/sqlite v1.2.3/go.mod h1:wkiGvZF3le/8vjCRYg0bT8TSw6APZ5rtgKW8uQYE3sc= 332 | gorm.io/gorm v1.20.4/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw= 333 | gorm.io/gorm v1.20.11/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw= 334 | gorm.io/gorm v1.21.9/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0= 335 | gorm.io/gorm v1.21.12/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0= 336 | gorm.io/gorm v1.21.16 h1:YBIQLtP5PLfZQz59qfrq7xbrK7KWQ+JsXXCH/THlMqs= 337 | gorm.io/gorm v1.21.16/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0= 338 | gorm.io/gorm v1.22.0/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0= 339 | gorm.io/gorm v1.22.2 h1:1iKcvyJnR5bHydBhDqTwasOkoo6+o4Ms5cknSt6qP7I= 340 | gorm.io/gorm v1.22.2/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0= 341 | gorm.io/plugin/dbresolver v1.1.0 h1:cegr4DeprR6SkLIQlKhJLYxH8muFbJ4SmnojXvoeb00= 342 | gorm.io/plugin/dbresolver v1.1.0/go.mod h1:tpImigFAEejCALOttyhWqsy4vfa2Uh/vAUVnL5IRF7Y= 343 | honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= 344 | moul.io/zapgorm2 v1.1.0 h1:qwAlMBYf+qJkJ7PAzJl4oCe6eS6QGiKAXUPeis0+RBE= 345 | moul.io/zapgorm2 v1.1.0/go.mod h1:emRfKjNqSzVj5lcgasBdovIXY1jSOwFz2GQZn1Rddks= 346 | olympos.io/encoding/edn v0.0.0-20200308123125-93e3b8dd0e24 h1:sreVOrDp0/ezb0CHKVek/l7YwpxPJqv+jT3izfSphA4= 347 | olympos.io/encoding/edn v0.0.0-20200308123125-93e3b8dd0e24/go.mod h1:oVgVk4OWVDi43qWBEyGhXgYxt7+ED4iYNpTngSLX2Iw= 348 | -------------------------------------------------------------------------------- /log/log.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | import ( 4 | "fmt" 5 | 6 | "go.uber.org/zap" 7 | "team1.asia/fibo/config" 8 | ) 9 | 10 | var Zap *zap.Logger 11 | 12 | // Setup Zap logger. 13 | func SetupLogger() { 14 | var ( 15 | err error 16 | zapCfg zap.Config 17 | ) 18 | 19 | switch config.App.Env { 20 | case "production": 21 | zapCfg = zap.NewProductionConfig() 22 | default: 23 | zapCfg = zap.NewDevelopmentConfig() 24 | } 25 | 26 | zapCfg.OutputPaths = []string{ 27 | "stdout", 28 | fmt.Sprintf(config.App.Log.FilePath, config.App.Server.Name, config.App.Env), 29 | } 30 | 31 | Zap, err = zapCfg.Build() 32 | 33 | if err != nil { 34 | fmt.Println(err) 35 | panic(err) 36 | } 37 | 38 | defer Zap.Sync() 39 | } 40 | 41 | // Debug logs a message at DebugLevel. The message includes any fields passed 42 | // at the log site, as well as any fields accumulated on the logger. 43 | func Debug(msg string, fields ...zap.Field) { 44 | Zap.Debug(msg, fields...) 45 | } 46 | 47 | // Info logs a message at InfoLevel. The message includes any fields passed 48 | // at the log site, as well as any fields accumulated on the logger. 49 | func Info(msg string, fields ...zap.Field) { 50 | Zap.Info(msg, fields...) 51 | } 52 | 53 | // Warn logs a message at WarnLevel. The message includes any fields passed 54 | // at the log site, as well as any fields accumulated on the logger. 55 | func Warn(msg string, fields ...zap.Field) { 56 | Zap.Warn(msg, fields...) 57 | } 58 | 59 | // Error logs a message at ErrorLevel. The message includes any fields passed 60 | // at the log site, as well as any fields accumulated on the logger. 61 | func Error(msg string, fields ...zap.Field) { 62 | Zap.Error(msg, fields...) 63 | } 64 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | 6 | "github.com/gofiber/fiber/v2" 7 | "github.com/gofiber/fiber/v2/middleware/compress" 8 | "github.com/gofiber/fiber/v2/middleware/cors" 9 | "github.com/gofiber/fiber/v2/middleware/etag" 10 | "github.com/gofiber/fiber/v2/middleware/pprof" 11 | "github.com/gofiber/fiber/v2/middleware/recover" 12 | "team1.asia/fibo/config" 13 | "team1.asia/fibo/db" 14 | _ "team1.asia/fibo/docs" 15 | "team1.asia/fibo/log" 16 | "team1.asia/fibo/router" 17 | ) 18 | 19 | // @title Fibo App 20 | // @version 1.0 21 | // @description This is an API for Fibo Application 22 | 23 | // @contact.name Toan Dinh 24 | // @contact.email toandptech@gmail.com 25 | 26 | // @license.name Apache 2.0 27 | // @license.url http://www.apache.org/licenses/LICENSE-2.0.html 28 | 29 | // @BasePath / 30 | func main() { 31 | // Read configuration from the environment variables 32 | config.LoadConfigFromEnv() 33 | 34 | // Zap logger 35 | log.SetupLogger() 36 | 37 | // DB Connection 38 | db.Connect() 39 | 40 | migrate := flag.Bool("migrate", false, "Check the migration request") 41 | flag.Parse() 42 | 43 | if *migrate { 44 | // DB Migration 45 | db.Migrate() 46 | } else { 47 | // Create new Fiber application 48 | app := fiber.New(fiber.Config{ 49 | Concurrency: 256 * 1024 * 1024, 50 | ServerHeader: config.App.Server.Name, 51 | BodyLimit: config.App.Server.UploadSize, 52 | ReduceMemoryUsage: true, 53 | DisableStartupMessage: true, 54 | ProxyHeader: config.App.Server.ProxyHeader, 55 | }) 56 | 57 | // Middlewares 58 | app.Use( 59 | cors.New(), 60 | recover.New(), 61 | etag.New(), 62 | compress.New(compress.Config{ 63 | Level: 1, 64 | }), 65 | ) 66 | 67 | if config.App.Debug { 68 | app.Use(pprof.New()) 69 | } 70 | 71 | // Register Routes 72 | router.RegisterRoutes(app) 73 | 74 | // Run application 75 | app.Listen(config.App.Server.Host + ":" + config.App.Server.Port) 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /router/router.go: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | import ( 4 | swagger "github.com/arsmn/fiber-swagger/v2" 5 | "github.com/gofiber/fiber/v2" 6 | jwtware "github.com/gofiber/jwt/v3" 7 | apiv1 "team1.asia/fibo/api/v1" 8 | "team1.asia/fibo/config" 9 | "team1.asia/fibo/log" 10 | ) 11 | 12 | // JWT authentication. 13 | func JWTAuthenticate(cfg *config.AppConfig) fiber.Handler { 14 | return jwtware.New(jwtware.Config{ 15 | SigningKey: []byte(cfg.JWT.Secret), 16 | ErrorHandler: func(ctx *fiber.Ctx, err error) error { 17 | return ctx.Status(401).JSON(fiber.Map{ 18 | "error": "Unauthorized", 19 | }) 20 | }, 21 | }) 22 | } 23 | 24 | // Regsiter application routes. 25 | func RegisterRoutes(app *fiber.App) { 26 | group := app.Group("api") 27 | 28 | // V1 29 | registerV1Routes(group) 30 | 31 | // Swagger router 32 | app.Get("/api-docs/*", swagger.Handler) 33 | 34 | // Handle not founds 35 | app.Use(func(c *fiber.Ctx) error { 36 | log.Error("404 not found.") 37 | return c.Status(404).JSON(fiber.Map{ 38 | "error": "404 not found.", 39 | }) 40 | }) 41 | } 42 | 43 | // Register API V1 routes. 44 | func registerV1Routes(group fiber.Router) { 45 | v1 := group.Group("v1") 46 | 47 | v1.Post("/login", apiv1.ValidateLoginRequest, apiv1.Login) 48 | v1.Post("/register", apiv1.ValidateRegisterRequest, apiv1.Register) 49 | } 50 | -------------------------------------------------------------------------------- /tmp/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/toandp/fibo/40e0d18d8072fd5a4a93a356fcf62ba0c838fc93/tmp/.keep --------------------------------------------------------------------------------