├── .DS_Store ├── .gitignore ├── .vscode └── settings.json ├── Dockerfile ├── Makefile ├── README.md ├── admin.sh ├── conf └── config.yaml ├── config └── config.go ├── docker-compose.yml ├── docs ├── docs.go ├── swagger.json └── swagger.yaml ├── go.mod ├── go.sum ├── handler ├── block │ └── block.go ├── handler.go ├── sd │ └── check.go └── user │ ├── create.go │ ├── delete.go │ ├── get.go │ ├── list.go │ ├── login.go │ ├── update.go │ └── user.go ├── log ├── apiserver.log └── apiserver.log.20200114155145312.zip ├── main.go ├── model ├── db.sql ├── init.go ├── model.go └── user.go ├── pkg ├── auth │ └── auth.go ├── response │ ├── code.go │ └── error.go ├── token │ └── token.go └── version │ └── version.go ├── router ├── middleware │ ├── auth.go │ ├── header.go │ ├── logging.go │ └── requestid.go └── router.go └── util ├── cpu.profile ├── util.go ├── util.test └── util_test.go /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChinaArJun/RESTfulGo/bf921f20e1994dbf2ff1d817fdc04d24c015cde1/.DS_Store -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.idea 2 | log -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "go.inferGopath": false 3 | } -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:latest 2 | 3 | # 设置代理 4 | ENV GOPROXY https://goproxy.cn,direct 5 | WORKDIR $GOPATH/src/github.com/RESTfulGo 6 | COPY . $GOPATH/src/github.com/RESTfulGo 7 | RUN pwd 8 | RUN make 9 | #RUN go build -v . 10 | 11 | EXPOSE 9090 12 | ENTRYPOINT ["./RESTfulGo"] -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | SHELL := /bin/bash 2 | BASEDIR = $(shell pwd) 3 | 4 | # build with verison infos 设置版本信息 5 | versionDir = "RESTfulGo/pkg/version" 6 | gitTag = $(shell if [ "`git describe --tags --abbrev=0 2>/dev/null`" != "" ];then git describe --tags --abbrev=0; else git log --pretty=format:'%h' -n 1; fi) 7 | buildDate = $(shell TZ=Asia/Shanghai date +%FT%T%z) 8 | gitCommit = $(shell git log --pretty=format:'%H' -n 1) 9 | gitTreeState = $(shell if git status|grep -q 'clean';then echo clean; else echo dirty; fi) 10 | 11 | ldflags="-w -X ${versionDir}.gitTag=${gitTag} -X ${versionDir}.buildDate=${buildDate} -X ${versionDir}.gitCommit=${gitCommit} -X ${versionDir}.gitTreeState=${gitTreeState}" 12 | 13 | 14 | all: gotool 15 | # @go build -v . 16 | # 编译时添加参数 17 | @go build -v -ldflags ${ldflags} . 18 | clean: 19 | rm -f RESTfulGo 20 | find . -name "[._]*.s[a-w][a-z]" | xargs -i rm -f {} 21 | gotool: 22 | gofmt -w . 23 | # go tool vet . |& grep -v vendor;true 24 | # go tool vet . | grep -v vendor;true 25 | ca: 26 | openssl req -new -nodes -x509 -out conf/server.crt -keyout conf/server.key -days 3650 -subj "/C=DE/ST=NRW/L=Earth/O=Random Company/OU=IT/CN=127.0.0.1/emailAddress=xxxxx@qq.com" 27 | 28 | help: 29 | @echo "make - compile the source code" 30 | @echo "make clean - remove binary file and vim swp files" 31 | @echo "make gotool - run go tool 'fmt' and 'vet'" 32 | @echo "make ca - generate ca files" 33 | 34 | .PHONY: clean gotool ca help -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RESTfulGo 2 | 3 | ## 手把手教你整合最简洁的GO开发框架:gin + grom + jwt 4 | ## Enterprise-level RESTful API services Project in Go 5 | 6 | > 运行打开 http://localhost:7777/swagger/index.html 查看接口文档 7 | 8 | ## 编译项目 9 | ```go 10 | Golang 支持在一个平台下生成另一个平台可执行程序的交叉编译功能。 11 | 12 | 1.Mac 13 | Mac下编译Linux, Windows平台的64位可执行程序: 14 | 15 | CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build main.go 16 | CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build main.go 17 | 18 | 2.Linux 19 | Linux下编译Mac, Windows平台的64位可执行程序: 20 | 21 | CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build main.go 22 | CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build main.go 23 | 24 | 3.Windows 25 | Windows下编译Mac, Linux平台的64位可执行程序: 26 | SET CGO_ENABLED=0 27 | SET GOOS=darwin3 28 | SET GOARCH=amd64 29 | go build main.go 30 | 31 | SET CGO_ENABLED=0 32 | SET GOOS=linux 33 | SET GOARCH=amd64 34 | go build main.go 35 | GOOS:目标可执行程序运行操作系统,支持 darwin,freebsd,linux,windows 36 | GOARCH:目标可执行程序操作系统构架,包括 386,amd64,arm 37 | ``` 38 | 39 | ## 项目用到的技术 40 | ``` 41 | Http (gin) #网络框架 42 | middleware (gin) #gin中间件实现请求日志和请求拦截 43 | Token(jwt) #API身份效验 44 | Mysql (gorm) #数据库 45 | FileConf (Viper) #配置文件读取 46 | CodeBcrypt (bcrypt) #哈希密码加密 47 | APIDocSwagger (gin-swagger) #API文档 48 | TTestAPI (testing) #测试框架 49 | APIPProf (go/PProf) #接口性能测试 50 | Makefile #管理API项目 51 | ``` 52 | 53 | ### 目录结构 54 | ``` 55 | ├── admin.sh # 进程的start|stop|status|restart控制文件 56 | ├── conf # 配置文件统一存放目录 57 | │ ├── config.yaml # 配置文件 58 | │ ├── server.crt # TLS配置文件 59 | │ └── server.key 60 | ├── config # 专门用来处理配置和配置文件的Go package 61 | │ └── config.go 62 | ├── db.sql # 在部署新环境时,可以登录MySQL客户端,执行source db.sql创建数据库和表 63 | ├── docs # swagger文档,执行 swag init 生成的 64 | │ ├── docs.go 65 | │ └── swagger 66 | │ ├── swagger.json 67 | │ └── swagger.yaml 68 | ├── handler # 类似MVC架构中的C,用来读取输入,并将处理流程转发给实际的处理函数,最后返回结果 69 | │ ├── handler.go 70 | │ ├── sd # 健康检查handler 71 | │ │ └── check.go 72 | │ └── user # 核心:用户业务逻辑handler 73 | │ ├── create.go # 新增用户 74 | │ ├── delete.go # 删除用户 75 | │ ├── get.go # 获取指定的用户信息 76 | │ ├── list.go # 查询用户列表 77 | │ ├── login.go # 用户登录 78 | │ ├── update.go # 更新用户 79 | │ └── user.go # 存放用户handler公用的函数、结构体等 80 | ├── main.go # Go程序唯一入口 81 | ├── Makefile # Makefile文件,一般大型软件系统都是采用make来作为编译工具 82 | ├── model # 数据库相关的操作统一放在这里,包括数据库初始化和对表的增删改查 83 | │ ├── init.go # 初始化和连接数据库 84 | │ ├── model.go # 存放一些公用的go struct 85 | │ └── user.go # 用户相关的数据库CURD操作 86 | ├── pkg # 引用的包 87 | │ ├── auth # 认证包 88 | │ │ └── auth.go 89 | │ ├── constvar # 常量统一存放位置 90 | │ │ └── constvar.go 91 | │ ├── errno # 错误码存放位置 92 | │ │ ├── code.go 93 | │ │ └── errno.go 94 | │ ├── token 95 | │ │ └── token.go 96 | │ └── version # 版本包 97 | │ ├── base.go 98 | │ ├── doc.go 99 | │ └── version.go 100 | ├── README.md # API目录README 101 | ├── router # 路由相关处理 102 | │ ├── middleware # API服务器用的是Gin Web框架,Gin中间件存放位置 103 | │ │ ├── auth.go # 104 | │ │ ├── header.go # 统一请求头 105 | │ │ ├── logging.go # 日志记录中间件 106 | │ │ └── requestid.go # 生成请求ID中间件 107 | │ └── router.go 108 | ├── service # 实际业务处理函数存放位置 109 | │ └── service.go 110 | ├── util # 工具类函数存放目录 111 | │ ├── util.go 112 | │ └── util_test.go 113 | └── vendor # vendor目录用来管理依赖包 114 | ├── github.com 115 | ├── golang.org 116 | ├── gopkg.in 117 | └── vendor.json 118 | ``` 119 | 120 | ## License 121 | 122 | [MIT license](http://opensource.org/licenses/MIT) © [ArJun](https://github.com/ArJun) -------------------------------------------------------------------------------- /admin.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | SERVER="restful" 4 | BASE_DIR=$PWD 5 | INTERVAL=2 6 | 7 | # 命令行参数,需要手动指定 8 | ARGS="" 9 | 10 | function start() 11 | { 12 | if [ "`pgrep $SERVER -u $UID`" != "" ];then 13 | echo "$SERVER already running" 14 | exit 1 15 | fi 16 | 17 | nohup $BASE_DIR/$SERVER $ARGS server &>/dev/null & 18 | 19 | echo "sleeping..." && sleep $INTERVAL 20 | 21 | # check status 22 | if [ "`pgrep $SERVER -u $UID`" == "" ];then 23 | echo "$SERVER start failed" 24 | exit 1 25 | fi 26 | } 27 | 28 | function status() 29 | { 30 | if [ "`pgrep $SERVER -u $UID`" != "" ];then 31 | echo $SERVER is running 32 | else 33 | echo $SERVER is not running 34 | fi 35 | } 36 | 37 | function stop() 38 | { 39 | if [ "`pgrep $SERVER -u $UID`" != "" ];then 40 | kill -9 `pgrep $SERVER -u $UID` 41 | fi 42 | 43 | echo "sleeping..." && sleep $INTERVAL 44 | 45 | if [ "`pgrep $SERVER -u $UID`" != "" ];then 46 | echo "$SERVER stop failed" 47 | exit 1 48 | fi 49 | } 50 | 51 | case "$1" in 52 | 'start') 53 | start 54 | ;; 55 | 'stop') 56 | stop 57 | ;; 58 | 'status') 59 | status 60 | ;; 61 | 'restart') 62 | stop && start 63 | ;; 64 | *) 65 | echo "usage: $0 {start|stop|restart|status}" 66 | exit 1 67 | ;; 68 | esac 69 | -------------------------------------------------------------------------------- /conf/config.yaml: -------------------------------------------------------------------------------- 1 | runmode: debug # 开发模式, debug, release, test 2 | addr: :9090 # HTTP绑定端口 3 | name: restful # API Server的名字 4 | url: http://127.0.0.1:9090 # pingServer函数请求的API服务器的ip:port 5 | max_ping_count: 10 # pingServer函数尝试的次数 6 | jwt_secret: eyJpYXQiOjE1MjgwMTY5MjIsImlkIjowLCJuYmY #jwt Token Signature 签名秘钥 7 | log: 8 | writers: file,stdout 9 | logger_level: DEBUG # 日志级别,DEBUG、INFO、WARN、ERROR、FATAL 10 | logger_file: log/apiserver.log # 日志文件 11 | log_format_text: false 12 | rollingPolicy: size # 这里将日志转存策略设置为 size,转存大小设置为 1 MB 13 | log_rotate_date: 1 # 14 | log_rotate_size: 1 15 | log_backup_count: 7 #当日志文件达到转存标准时,log 系统会将该日志文件进行压缩备份,这里指定了备份文件的最大个数 16 | db: 17 | name: restful 18 | addr: 127.0.0.1:3306 19 | username: root 20 | password: root 21 | docker_db: 22 | name: db_apiserver 23 | addr: 127.0.0.1:3306 24 | username: root 25 | password: root -------------------------------------------------------------------------------- /config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "github.com/fsnotify/fsnotify" 5 | "github.com/lexkong/log" 6 | "github.com/spf13/viper" 7 | "strings" 8 | ) 9 | 10 | type Config struct { 11 | Name string 12 | } 13 | 14 | func Init(cfg string) error { 15 | c := Config{ 16 | Name: cfg, 17 | } 18 | // 初始化配置文件 19 | if err := c.initConfig(); err != nil { 20 | // 初始化失败 21 | return err 22 | } 23 | 24 | // 初始化日志包 25 | c.initLog() 26 | 27 | // 监控配置文件变化并热加载程序 28 | c.watchConfig() 29 | 30 | return nil 31 | } 32 | 33 | func (c *Config) initConfig() error { 34 | if c.Name != "" { 35 | // 指定配置文件,捷信指定的文件 36 | viper.SetConfigFile(c.Name) 37 | } else { 38 | // 默认文件 39 | viper.AddConfigPath("conf") // 目录 40 | viper.SetConfigName("config") // 文件名 41 | } 42 | // 设置配置文件格式为yaml格式 43 | viper.SetConfigType("yaml") 44 | // 自动匹配环境变量 45 | viper.AutomaticEnv() 46 | // 读取环境变量的前缀为APISERVER 47 | viper.SetEnvPrefix("APISERVER") 48 | replacer := strings.NewReplacer(".", "_") 49 | viper.SetEnvKeyReplacer(replacer) 50 | 51 | if err := viper.ReadInConfig(); err != nil { 52 | // viper解析文件错误 53 | return err 54 | } 55 | return nil 56 | } 57 | 58 | func (c *Config) initLog() { 59 | passLagerCfg := log.PassLagerCfg{ 60 | Writers: viper.GetString("log.writers"), 61 | LoggerLevel: viper.GetString("log.logger_level"), 62 | LoggerFile: viper.GetString("log.logger_file"), 63 | LogFormatText: viper.GetBool("log.log_format_text"), 64 | RollingPolicy: viper.GetString("log.rollingPolicy"), 65 | LogRotateDate: viper.GetInt("log.log_rotate_date"), 66 | LogRotateSize: viper.GetInt("log.log_rotate_size"), 67 | LogBackupCount: viper.GetInt("log.log_backup_count"), 68 | } 69 | log.InitWithConfig(&passLagerCfg) 70 | } 71 | 72 | func (c *Config) watchConfig() { 73 | viper.WatchConfig() 74 | viper.OnConfigChange(func(in fsnotify.Event) { 75 | log.Infof("config file changed : %s", in.Name) 76 | }) 77 | } 78 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | # 关于Compose文件的具体说明,请参考以下链接: 2 | # https://docs.docker.com/compose/compose-file/ 3 | 4 | version: "3" 5 | networks: 6 | basic: 7 | 8 | services: 9 | world: 10 | container_name: world 11 | image: go-blog 12 | ports: 13 | - "8099:80" 14 | volumes: 15 | - ./app/go/world:/go/src/app:rw 16 | networks: 17 | - basic 18 | # 数据库的各种配置参数,请参考以下链接: 19 | # https://github.com/piexlmax/gin-vue-admin/blob/master/QMPlusServer/db/qmplus.sql#L4-L8 20 | # https://github.com/piexlmax/gin-vue-admin/blob/master/QMPlusServer/static/config/config.json#L8-L14 21 | # database: 22 | # image: mysql:5.6 23 | # ports: 24 | # - 3306:3306 25 | # volumes: 26 | # - ./QMPlusServer/db:/docker-entrypoint-initdb.d 27 | # environment: 28 | # MYSQL_ROOT_PASSWORD: Aa@6447985 29 | # MYSQL_DATABASE: qmPlus 30 | # user: root -------------------------------------------------------------------------------- /docs/docs.go: -------------------------------------------------------------------------------- 1 | // GENERATED BY THE COMMAND ABOVE; DO NOT EDIT 2 | // This file was generated by swaggo/swag at 3 | // 2020-04-19 09:30:04.558439 +0800 CST m=+0.046952706 4 | 5 | package docs 6 | 7 | import ( 8 | "bytes" 9 | "encoding/json" 10 | "strings" 11 | 12 | "github.com/alecthomas/template" 13 | "github.com/swaggo/swag" 14 | ) 15 | 16 | var doc = `{ 17 | "schemes": {{ marshal .Schemes }}, 18 | "swagger": "2.0", 19 | "info": { 20 | "description": "{{.Description}}", 21 | "title": "{{.Title}}", 22 | "contact": {}, 23 | "license": {}, 24 | "version": "{{.Version}}" 25 | }, 26 | "host": "{{.Host}}", 27 | "basePath": "{{.BasePath}}", 28 | "paths": { 29 | "/v1/user": { 30 | "get": { 31 | "description": "Delete a new user", 32 | "consumes": [ 33 | "application/json" 34 | ], 35 | "produces": [ 36 | "application/json" 37 | ], 38 | "tags": [ 39 | "user" 40 | ], 41 | "summary": "获取用户列表", 42 | "responses": { 43 | "200": { 44 | "description": "{\"code\":0,\"message\":\"OK\",\"data\":[{\"username\":\"kong\"},{\"username\":\"kong\"}]}", 45 | "schema": { 46 | "$ref": "#/definitions/user.ListResponse" 47 | } 48 | } 49 | } 50 | }, 51 | "post": { 52 | "description": "Add a new user", 53 | "consumes": [ 54 | "application/json" 55 | ], 56 | "produces": [ 57 | "application/json" 58 | ], 59 | "tags": [ 60 | "user" 61 | ], 62 | "summary": "Add new user to the database", 63 | "parameters": [ 64 | { 65 | "description": "Create a new user", 66 | "name": "user", 67 | "in": "body", 68 | "required": true, 69 | "schema": { 70 | "type": "object", 71 | "$ref": "#/definitions/user.CreateRequest" 72 | } 73 | } 74 | ], 75 | "responses": { 76 | "200": { 77 | "description": "{\"code\":0,\"message\":\"OK\",\"data\":{\"username\":\"kong\"}}", 78 | "schema": { 79 | "$ref": "#/definitions/user.CreateResponse" 80 | } 81 | } 82 | } 83 | }, 84 | "delete": { 85 | "description": "Delete a new user", 86 | "consumes": [ 87 | "application/json" 88 | ], 89 | "produces": [ 90 | "application/json" 91 | ], 92 | "tags": [ 93 | "user" 94 | ], 95 | "summary": "Delete new user to the database", 96 | "parameters": [ 97 | { 98 | "description": "Delete a new user", 99 | "name": "user", 100 | "in": "body", 101 | "required": true, 102 | "schema": { 103 | "type": "object", 104 | "$ref": "#/definitions/user.CreateRequest" 105 | } 106 | } 107 | ], 108 | "responses": { 109 | "200": { 110 | "description": "{\"code\":0,\"message\":\"OK\",\"data\":{\"username\":\"kong\"}}", 111 | "schema": { 112 | "$ref": "#/definitions/handler.Result" 113 | } 114 | } 115 | } 116 | } 117 | } 118 | }, 119 | "definitions": { 120 | "handler.Result": { 121 | "type": "object", 122 | "properties": { 123 | "code": { 124 | "type": "integer" 125 | }, 126 | "data": { 127 | "type": "object" 128 | }, 129 | "message": { 130 | "type": "string" 131 | } 132 | } 133 | }, 134 | "model.UserInfo": { 135 | "type": "object", 136 | "properties": { 137 | "createdAt": { 138 | "type": "string" 139 | }, 140 | "id": { 141 | "type": "integer" 142 | }, 143 | "password": { 144 | "type": "string" 145 | }, 146 | "sayHello": { 147 | "type": "string" 148 | }, 149 | "updatedAt": { 150 | "type": "string" 151 | }, 152 | "username": { 153 | "type": "string" 154 | } 155 | } 156 | }, 157 | "user.CreateRequest": { 158 | "type": "object", 159 | "properties": { 160 | "password": { 161 | "type": "string" 162 | }, 163 | "username": { 164 | "type": "string" 165 | } 166 | } 167 | }, 168 | "user.CreateResponse": { 169 | "type": "object", 170 | "properties": { 171 | "username": { 172 | "type": "string" 173 | } 174 | } 175 | }, 176 | "user.GetUserResponse": { 177 | "type": "object", 178 | "properties": { 179 | "id": { 180 | "type": "string" 181 | } 182 | } 183 | }, 184 | "user.ListResponse": { 185 | "type": "object", 186 | "properties": { 187 | "total_count": { 188 | "type": "integer" 189 | }, 190 | "user_list": { 191 | "type": "array", 192 | "items": { 193 | "$ref": "#/definitions/model.UserInfo" 194 | } 195 | } 196 | } 197 | } 198 | } 199 | }` 200 | 201 | type swaggerInfo struct { 202 | Version string 203 | Host string 204 | BasePath string 205 | Schemes []string 206 | Title string 207 | Description string 208 | } 209 | 210 | // SwaggerInfo holds exported Swagger Info so clients can modify it 211 | var SwaggerInfo = swaggerInfo{ 212 | Version: "", 213 | Host: "", 214 | BasePath: "", 215 | Schemes: []string{}, 216 | Title: "", 217 | Description: "", 218 | } 219 | 220 | type s struct{} 221 | 222 | func (s *s) ReadDoc() string { 223 | sInfo := SwaggerInfo 224 | sInfo.Description = strings.Replace(sInfo.Description, "\n", "\\n", -1) 225 | 226 | t, err := template.New("swagger_info").Funcs(template.FuncMap{ 227 | "marshal": func(v interface{}) string { 228 | a, _ := json.Marshal(v) 229 | return string(a) 230 | }, 231 | }).Parse(doc) 232 | if err != nil { 233 | return doc 234 | } 235 | 236 | var tpl bytes.Buffer 237 | if err := t.Execute(&tpl, sInfo); err != nil { 238 | return doc 239 | } 240 | 241 | return tpl.String() 242 | } 243 | 244 | func init() { 245 | swag.Register(swag.Name, &s{}) 246 | } 247 | -------------------------------------------------------------------------------- /docs/swagger.json: -------------------------------------------------------------------------------- 1 | { 2 | "swagger": "2.0", 3 | "info": { 4 | "contact": {}, 5 | "license": {} 6 | }, 7 | "paths": { 8 | "/v1/user": { 9 | "get": { 10 | "description": "Delete a new user", 11 | "consumes": [ 12 | "application/json" 13 | ], 14 | "produces": [ 15 | "application/json" 16 | ], 17 | "tags": [ 18 | "user" 19 | ], 20 | "summary": "获取用户列表", 21 | "responses": { 22 | "200": { 23 | "description": "{\"code\":0,\"message\":\"OK\",\"data\":[{\"username\":\"kong\"},{\"username\":\"kong\"}]}", 24 | "schema": { 25 | "$ref": "#/definitions/user.ListResponse" 26 | } 27 | } 28 | } 29 | }, 30 | "post": { 31 | "description": "Add a new user", 32 | "consumes": [ 33 | "application/json" 34 | ], 35 | "produces": [ 36 | "application/json" 37 | ], 38 | "tags": [ 39 | "user" 40 | ], 41 | "summary": "Add new user to the database", 42 | "parameters": [ 43 | { 44 | "description": "Create a new user", 45 | "name": "user", 46 | "in": "body", 47 | "required": true, 48 | "schema": { 49 | "type": "object", 50 | "$ref": "#/definitions/user.CreateRequest" 51 | } 52 | } 53 | ], 54 | "responses": { 55 | "200": { 56 | "description": "{\"code\":0,\"message\":\"OK\",\"data\":{\"username\":\"kong\"}}", 57 | "schema": { 58 | "$ref": "#/definitions/user.CreateResponse" 59 | } 60 | } 61 | } 62 | }, 63 | "delete": { 64 | "description": "Delete a new user", 65 | "consumes": [ 66 | "application/json" 67 | ], 68 | "produces": [ 69 | "application/json" 70 | ], 71 | "tags": [ 72 | "user" 73 | ], 74 | "summary": "Delete new user to the database", 75 | "parameters": [ 76 | { 77 | "description": "Delete a new user", 78 | "name": "user", 79 | "in": "body", 80 | "required": true, 81 | "schema": { 82 | "type": "object", 83 | "$ref": "#/definitions/user.CreateRequest" 84 | } 85 | } 86 | ], 87 | "responses": { 88 | "200": { 89 | "description": "{\"code\":0,\"message\":\"OK\",\"data\":{\"username\":\"kong\"}}", 90 | "schema": { 91 | "$ref": "#/definitions/handler.Result" 92 | } 93 | } 94 | } 95 | } 96 | } 97 | }, 98 | "definitions": { 99 | "handler.Result": { 100 | "type": "object", 101 | "properties": { 102 | "code": { 103 | "type": "integer" 104 | }, 105 | "data": { 106 | "type": "object" 107 | }, 108 | "message": { 109 | "type": "string" 110 | } 111 | } 112 | }, 113 | "model.UserInfo": { 114 | "type": "object", 115 | "properties": { 116 | "createdAt": { 117 | "type": "string" 118 | }, 119 | "id": { 120 | "type": "integer" 121 | }, 122 | "password": { 123 | "type": "string" 124 | }, 125 | "sayHello": { 126 | "type": "string" 127 | }, 128 | "updatedAt": { 129 | "type": "string" 130 | }, 131 | "username": { 132 | "type": "string" 133 | } 134 | } 135 | }, 136 | "user.CreateRequest": { 137 | "type": "object", 138 | "properties": { 139 | "password": { 140 | "type": "string" 141 | }, 142 | "username": { 143 | "type": "string" 144 | } 145 | } 146 | }, 147 | "user.CreateResponse": { 148 | "type": "object", 149 | "properties": { 150 | "username": { 151 | "type": "string" 152 | } 153 | } 154 | }, 155 | "user.GetUserResponse": { 156 | "type": "object", 157 | "properties": { 158 | "id": { 159 | "type": "string" 160 | } 161 | } 162 | }, 163 | "user.ListResponse": { 164 | "type": "object", 165 | "properties": { 166 | "total_count": { 167 | "type": "integer" 168 | }, 169 | "user_list": { 170 | "type": "array", 171 | "items": { 172 | "$ref": "#/definitions/model.UserInfo" 173 | } 174 | } 175 | } 176 | } 177 | } 178 | } -------------------------------------------------------------------------------- /docs/swagger.yaml: -------------------------------------------------------------------------------- 1 | definitions: 2 | handler.Result: 3 | properties: 4 | code: 5 | type: integer 6 | data: 7 | type: object 8 | message: 9 | type: string 10 | type: object 11 | model.UserInfo: 12 | properties: 13 | createdAt: 14 | type: string 15 | id: 16 | type: integer 17 | password: 18 | type: string 19 | sayHello: 20 | type: string 21 | updatedAt: 22 | type: string 23 | username: 24 | type: string 25 | type: object 26 | user.CreateRequest: 27 | properties: 28 | password: 29 | type: string 30 | username: 31 | type: string 32 | type: object 33 | user.CreateResponse: 34 | properties: 35 | username: 36 | type: string 37 | type: object 38 | user.GetUserResponse: 39 | properties: 40 | id: 41 | type: string 42 | type: object 43 | user.ListResponse: 44 | properties: 45 | total_count: 46 | type: integer 47 | user_list: 48 | items: 49 | $ref: '#/definitions/model.UserInfo' 50 | type: array 51 | type: object 52 | info: 53 | contact: {} 54 | license: {} 55 | paths: 56 | /v1/user: 57 | delete: 58 | consumes: 59 | - application/json 60 | description: Delete a new user 61 | parameters: 62 | - description: Delete a new user 63 | in: body 64 | name: user 65 | required: true 66 | schema: 67 | $ref: '#/definitions/user.CreateRequest' 68 | type: object 69 | produces: 70 | - application/json 71 | responses: 72 | "200": 73 | description: '{"code":0,"message":"OK","data":{"username":"kong"}}' 74 | schema: 75 | $ref: '#/definitions/handler.Result' 76 | summary: Delete new user to the database 77 | tags: 78 | - user 79 | get: 80 | consumes: 81 | - application/json 82 | description: Delete a new user 83 | produces: 84 | - application/json 85 | responses: 86 | "200": 87 | description: '{"code":0,"message":"OK","data":[{"username":"kong"},{"username":"kong"}]}' 88 | schema: 89 | $ref: '#/definitions/user.ListResponse' 90 | summary: 获取用户列表 91 | tags: 92 | - user 93 | post: 94 | consumes: 95 | - application/json 96 | description: Add a new user 97 | parameters: 98 | - description: Create a new user 99 | in: body 100 | name: user 101 | required: true 102 | schema: 103 | $ref: '#/definitions/user.CreateRequest' 104 | type: object 105 | produces: 106 | - application/json 107 | responses: 108 | "200": 109 | description: '{"code":0,"message":"OK","data":{"username":"kong"}}' 110 | schema: 111 | $ref: '#/definitions/user.CreateResponse' 112 | summary: Add new user to the database 113 | tags: 114 | - user 115 | swagger: "2.0" 116 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module RESTfulGo 2 | 3 | go 1.13 4 | 5 | require ( 6 | github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d // indirect 7 | github.com/TarsCloud/TarsGo v1.1.2 8 | github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 9 | github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect 10 | github.com/dgrijalva/jwt-go v3.2.0+incompatible 11 | github.com/fsnotify/fsnotify v1.4.7 12 | github.com/gin-contrib/pprof v1.3.0 13 | // 网络请求框架 14 | github.com/gin-gonic/gin v1.6.2 15 | github.com/go-ole/go-ole v1.2.4 // indirect 16 | github.com/go-openapi/spec v0.19.7 // indirect 17 | github.com/go-openapi/swag v0.19.9 // indirect 18 | github.com/go-playground/universal-translator v0.17.0 // indirect 19 | github.com/golang/mock v1.4.3 // indirect 20 | github.com/golang/protobuf v1.4.0 // indirect 21 | github.com/jinzhu/gorm v1.9.12 22 | github.com/json-iterator/go v1.1.9 // indirect 23 | github.com/labstack/echo/v4 v4.1.16 // indirect 24 | github.com/leodido/go-urn v1.2.0 // indirect 25 | // API日志工具 26 | github.com/lexkong/log v0.0.0-20180607165131-972f9cd951fc 27 | github.com/mailru/easyjson v0.7.1 // indirect 28 | github.com/onsi/ginkgo v1.12.0 // indirect 29 | github.com/onsi/gomega v1.9.0 // indirect 30 | github.com/satori/go.uuid v1.2.0 31 | // 工具包 32 | github.com/shirou/gopsutil v2.19.12+incompatible 33 | github.com/spf13/pflag v1.0.3 34 | // 配置文件读取 35 | github.com/spf13/viper v1.6.1 36 | github.com/swaggo/echo-swagger v1.0.0 // indirect 37 | github.com/swaggo/gin-swagger v1.2.0 38 | github.com/swaggo/swag v1.6.5 39 | github.com/teris-io/shortid v0.0.0-20171029131806-771a37caa5cf 40 | github.com/urfave/cli v1.22.4 // indirect 41 | github.com/willf/pad v0.0.0-20190207183901-eccfe5d84172 42 | golang.org/x/crypto v0.0.0-20200414173820-0848c9571904 43 | golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e // indirect 44 | golang.org/x/sys v0.0.0-20200413165638-669c56c373c4 // indirect 45 | golang.org/x/tools v0.0.0-20200417140056-c07e33ef3290 // indirect 46 | gopkg.in/go-playground/validator.v9 v9.31.0 47 | ) 48 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= 3 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 4 | github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc= 5 | github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE= 6 | github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= 7 | github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= 8 | github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= 9 | github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= 10 | github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= 11 | github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= 12 | github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d h1:G0m3OIz70MZUWq3EgK3CesDbo8upS2Vm9/P3FtgI+Jk= 13 | github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= 14 | github.com/TarsCloud/TarsGo v1.1.2 h1:5nT5geOchcQl91trousmokW6zAYJbr6fow8csA/XG0s= 15 | github.com/TarsCloud/TarsGo v1.1.2/go.mod h1:jkslu43B0kZitq9ztCH6ZM6l4rCKGkMjdqEPjHoOdjc= 16 | github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= 17 | github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM= 18 | github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= 19 | github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= 20 | github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= 21 | github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= 22 | github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= 23 | github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= 24 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 25 | github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= 26 | github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= 27 | github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= 28 | github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= 29 | github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= 30 | github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY= 31 | github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= 32 | github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM= 33 | github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= 34 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 35 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 36 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 37 | github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd h1:83Wprp6ROGeiHFAP8WJdI2RoxALQYgdllERc3N5N2DM= 38 | github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= 39 | github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= 40 | github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= 41 | github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= 42 | github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 h1:Yzb9+7DPaBjB8zlTR87/ElzFsnQfuHnVUVqpZZIcV5Y= 43 | github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0= 44 | github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= 45 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 46 | github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= 47 | github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= 48 | github.com/gin-contrib/gzip v0.0.1 h1:ezvKOL6jH+jlzdHNE4h9h8q8uMpDQjyl0NN0Jd7jozc= 49 | github.com/gin-contrib/gzip v0.0.1/go.mod h1:fGBJBCdt6qCZuCAOwWuFhBB4OOq9EFqlo5dEaFhhu5w= 50 | github.com/gin-contrib/pprof v1.3.0 h1:G9eK6HnbkSqDZBYbzG4wrjCsA4e+cvYAHUZw6W+W9K0= 51 | github.com/gin-contrib/pprof v1.3.0/go.mod h1:waMjT1H9b179t3CxuG1cV3DHpga6ybizwfBaM5OXaB0= 52 | github.com/gin-contrib/sse v0.0.0-20170109093832-22d885f9ecc7/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s= 53 | github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s= 54 | github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= 55 | github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= 56 | github.com/gin-gonic/gin v1.3.0/go.mod h1:7cKuhb5qV2ggCFctp2fJQ+ErvciLZrIeoOSOm6mUr7Y= 57 | github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM= 58 | github.com/gin-gonic/gin v1.5.0 h1:fi+bqFAx/oLK54somfCtEZs9HeH1LHVoEPUgARpTqyc= 59 | github.com/gin-gonic/gin v1.5.0/go.mod h1:Nd6IXA8m5kNZdNEHMBd93KT+mdY3+bewLgRvmCsR2Do= 60 | github.com/gin-gonic/gin v1.6.2 h1:88crIK23zO6TqlQBt+f9FrPJNKm9ZEr7qjp9vl/d5TM= 61 | github.com/gin-gonic/gin v1.6.2/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= 62 | github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= 63 | github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= 64 | github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= 65 | github.com/go-ole/go-ole v1.2.4 h1:nNBDSCOigTSiarFpYE9J/KtEA1IOW4CNeqT9TQDqCxI= 66 | github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM= 67 | github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= 68 | github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= 69 | github.com/go-openapi/jsonpointer v0.19.3 h1:gihV7YNZK1iK6Tgwwsxo2rJbD1GTbdm72325Bq8FI3w= 70 | github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= 71 | github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= 72 | github.com/go-openapi/jsonreference v0.19.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= 73 | github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= 74 | github.com/go-openapi/jsonreference v0.19.3 h1:5cxNfTy0UVC3X8JL5ymxzyoUZmo8iZb+jeTWn7tUa8o= 75 | github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= 76 | github.com/go-openapi/spec v0.19.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= 77 | github.com/go-openapi/spec v0.19.4 h1:ixzUSnHTd6hCemgtAJgluaTSGYpLNpJY4mA2DIkdOAo= 78 | github.com/go-openapi/spec v0.19.4/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= 79 | github.com/go-openapi/spec v0.19.5 h1:Xm0Ao53uqnk9QE/LlYV5DEU09UAgpliA85QoT9LzqPw= 80 | github.com/go-openapi/spec v0.19.5/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHKHb3gWUSk= 81 | github.com/go-openapi/spec v0.19.7 h1:0xWSeMd35y5avQAThZR2PkEuqSosoS5t6gDH4L8n11M= 82 | github.com/go-openapi/spec v0.19.7/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHKHb3gWUSk= 83 | github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= 84 | github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= 85 | github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY= 86 | github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= 87 | github.com/go-openapi/swag v0.19.6 h1:JSUbVWlaTLMhXeOMyArSUNCdroxZu2j1TcrsOV8Mj7Q= 88 | github.com/go-openapi/swag v0.19.6/go.mod h1:ao+8BpOPyKdpQz3AOJfbeEVpLmWAvlT1IfTe5McPyhY= 89 | github.com/go-openapi/swag v0.19.9 h1:1IxuqvBUU3S2Bi4YC7tlP9SJF1gVpCvqN0T2Qof4azE= 90 | github.com/go-openapi/swag v0.19.9/go.mod h1:ao+8BpOPyKdpQz3AOJfbeEVpLmWAvlT1IfTe5McPyhY= 91 | github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= 92 | github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= 93 | github.com/go-playground/locales v0.12.1 h1:2FITxuFt/xuCNP1Acdhv62OzaCiviiE4kotfhkmOqEc= 94 | github.com/go-playground/locales v0.12.1/go.mod h1:IUMDtCfWo/w/mtMfIE/IG2K+Ey3ygWanZIBtBW0W2TM= 95 | github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= 96 | github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= 97 | github.com/go-playground/universal-translator v0.16.0 h1:X++omBR/4cE2MNg91AoC3rmGrCjJ8eAeUP/K/EKx4DM= 98 | github.com/go-playground/universal-translator v0.16.0/go.mod h1:1AnU7NaIRDWWzGEKwgtJRd2xk99HeFyHw3yid4rvQIY= 99 | github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no= 100 | github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= 101 | github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY= 102 | github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= 103 | github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA= 104 | github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= 105 | github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= 106 | github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= 107 | github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= 108 | github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY= 109 | github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= 110 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 111 | github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 112 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 113 | github.com/golang/mock v1.4.3 h1:GV+pQPG/EUUbkh47niozDcADz6go/dUwhVzdUQHIVRw= 114 | github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 115 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 116 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 117 | github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= 118 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 119 | github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 120 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= 121 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= 122 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= 123 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 124 | github.com/golang/protobuf v1.4.0 h1:oOuy+ugB+P/kBdUnG5QaMXSIyJ1q38wWSojYCb3z5VQ= 125 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= 126 | github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 127 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 128 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 129 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 130 | github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= 131 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 132 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 133 | github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= 134 | github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= 135 | github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= 136 | github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= 137 | github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= 138 | github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= 139 | github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= 140 | github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= 141 | github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= 142 | github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= 143 | github.com/jinzhu/gorm v1.9.12 h1:Drgk1clyWT9t9ERbzHza6Mj/8FY/CqMyVzOiHviMo6Q= 144 | github.com/jinzhu/gorm v1.9.12/go.mod h1:vhTjlKSJUTWNtcbQtrMBFCxy7eXTzeCAzfL5fBZT/Qs= 145 | github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= 146 | github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= 147 | github.com/jinzhu/now v1.0.1 h1:HjfetcXq097iXP0uoPCdnM4Efp5/9MsM0/M+XOTeR3M= 148 | github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= 149 | github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= 150 | github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= 151 | github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= 152 | github.com/json-iterator/go v1.1.7 h1:KfgG9LzI+pYjr4xvmz/5H4FXjokeP+rlHLhv3iH62Fo= 153 | github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 154 | github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns= 155 | github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 156 | github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= 157 | github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= 158 | github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= 159 | github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= 160 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 161 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 162 | github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= 163 | github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= 164 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 165 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 166 | github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= 167 | github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= 168 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 169 | github.com/labstack/echo/v4 v4.0.0 h1:q1GH+caIXPP7H2StPIdzy/ez9CO0EepqYeUg6vi9SWM= 170 | github.com/labstack/echo/v4 v4.0.0/go.mod h1:tZv7nai5buKSg5h/8E6zz4LsD/Dqh9/91Mvs7Z5Zyno= 171 | github.com/labstack/echo/v4 v4.1.16 h1:8swiwjE5Jkai3RPfZoahp8kjVCRNq+y7Q0hPji2Kz0o= 172 | github.com/labstack/echo/v4 v4.1.16/go.mod h1:awO+5TzAjvL8XpibdsfXxPgHr+orhtXZJZIQCVjogKI= 173 | github.com/labstack/gommon v0.2.8 h1:JvRqmeZcfrHC5u6uVleB4NxxNbzx6gpbJiQknDbKQu0= 174 | github.com/labstack/gommon v0.2.8/go.mod h1:/tj9csK2iPSBvn+3NLM9e52usepMtrd5ilFYA+wQNJ4= 175 | github.com/labstack/gommon v0.3.0 h1:JEeO0bvc78PKdyHxloTKiF8BD5iGrH8T6MSeGvSgob0= 176 | github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= 177 | github.com/leodido/go-urn v1.1.0 h1:Sm1gr51B1kKyfD2BlRcLSiEkffoG96g6TPv6eRoEiB8= 178 | github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw= 179 | github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= 180 | github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= 181 | github.com/lexkong/log v0.0.0-20180607165131-972f9cd951fc h1:7qm429K8eO0gBJiJTbogcK3eP4teRuA+4Z+NBZZIN8k= 182 | github.com/lexkong/log v0.0.0-20180607165131-972f9cd951fc/go.mod h1:+TkR/7XPYVDBUEbQsDL4J6vVaJAmeBDzMgs1Q0GsUjc= 183 | github.com/lib/pq v1.1.1 h1:sJZmqHoEaY7f+NPP8pgLB/WxulyR3fewgCM2qaSlBb4= 184 | github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= 185 | github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= 186 | github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= 187 | github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= 188 | github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= 189 | github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e h1:hB2xlXdHp/pmPZq0y3QnmWAArdw9PqbmotexnWx/FU8= 190 | github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= 191 | github.com/mailru/easyjson v0.7.0 h1:aizVhC/NAAcKWb+5QsU1iNOZb4Yws5UO2I+aIprQITM= 192 | github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= 193 | github.com/mailru/easyjson v0.7.1 h1:mdxE1MF9o53iCb2Ghj1VfWvh7ZOwHpnVG/xwXrV90U8= 194 | github.com/mailru/easyjson v0.7.1/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= 195 | github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= 196 | github.com/mattn/go-colorable v0.1.0 h1:v2XXALHHh6zHfYTJ+cSkwtyffnaOyR1MXaA91mTrb8o= 197 | github.com/mattn/go-colorable v0.1.0/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= 198 | github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= 199 | github.com/mattn/go-colorable v0.1.6 h1:6Su7aK7lXmJ/U79bYtBjLNaha4Fs1Rg9plHpcH+vvnE= 200 | github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= 201 | github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= 202 | github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= 203 | github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= 204 | github.com/mattn/go-isatty v0.0.9 h1:d5US/mDsogSGW37IV293h//ZFaeajb69h+EHFsv2xGg= 205 | github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= 206 | github.com/mattn/go-isatty v0.0.11 h1:FxPOTFNqGkuDUGi3H/qkUbQO4ZiBa2brKq5r0l8TGeM= 207 | github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= 208 | github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= 209 | github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= 210 | github.com/mattn/go-sqlite3 v2.0.1+incompatible h1:xQ15muvnzGBHpIpdrNi1DA5x0+TcBZzsIDwmw9uTHzw= 211 | github.com/mattn/go-sqlite3 v2.0.1+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= 212 | github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= 213 | github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= 214 | github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 215 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= 216 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 217 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= 218 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 219 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg= 220 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 221 | github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= 222 | github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 223 | github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= 224 | github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= 225 | github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 226 | github.com/onsi/ginkgo v1.12.0 h1:Iw5WCbBcaAAd0fpRb1c9r5YCylv4XDoCSigm1zLevwU= 227 | github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg= 228 | github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= 229 | github.com/onsi/gomega v1.9.0 h1:R1uwffexN6Pr340GtYRIdZmAiN4J+iw6WG4wog1DUXg= 230 | github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= 231 | github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= 232 | github.com/openzipkin-contrib/zipkin-go-opentracing v0.3.5/go.mod h1:uVHyebswE1cCXr2A73cRM2frx5ld1RJUCJkFNZ90ZiI= 233 | github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= 234 | github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= 235 | github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 236 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 237 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 238 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 239 | github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= 240 | github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= 241 | github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= 242 | github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 243 | github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= 244 | github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= 245 | github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= 246 | github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= 247 | github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= 248 | github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= 249 | github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= 250 | github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 251 | github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= 252 | github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= 253 | github.com/shirou/gopsutil v2.19.12+incompatible h1:WRstheAymn1WOPesh+24+bZKFkqrdCR8JOc77v4xV3Q= 254 | github.com/shirou/gopsutil v2.19.12+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= 255 | github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= 256 | github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= 257 | github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= 258 | github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= 259 | github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= 260 | github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= 261 | github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= 262 | github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= 263 | github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= 264 | github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI= 265 | github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= 266 | github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= 267 | github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= 268 | github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= 269 | github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= 270 | github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= 271 | github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= 272 | github.com/spf13/viper v1.6.1 h1:VPZzIkznI1YhVMRi6vNFLHSwhnhReBfgTxIPccpfdZk= 273 | github.com/spf13/viper v1.6.1/go.mod h1:t3iDnF5Jlj76alVNuyFBk5oUMCvsrkbvZK0WQdfDi5k= 274 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 275 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 276 | github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= 277 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 278 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 279 | github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= 280 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 281 | github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= 282 | github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= 283 | github.com/swaggo/echo-swagger v1.0.0 h1:ppQFt6Am3/MHIUmTpZOwi4gggMZ/W9zmKP4Z9ahTe5c= 284 | github.com/swaggo/echo-swagger v1.0.0/go.mod h1:Vnz3c2TGeFpoZPSV3CkWCrvyfU0016Gq/S0j4JspQnM= 285 | github.com/swaggo/files v0.0.0-20190704085106-630677cd5c14 h1:PyYN9JH5jY9j6av01SpfRMb+1DWg/i3MbGOKPxJ2wjM= 286 | github.com/swaggo/files v0.0.0-20190704085106-630677cd5c14/go.mod h1:gxQT6pBGRuIGunNf/+tSOB5OHvguWi8Tbt82WOkf35E= 287 | github.com/swaggo/gin-swagger v1.2.0 h1:YskZXEiv51fjOMTsXrOetAjrMDfFaXD79PEoQBOe2W0= 288 | github.com/swaggo/gin-swagger v1.2.0/go.mod h1:qlH2+W7zXGZkczuL+r2nEBR2JTT+/lX05Nn6vPhc7OI= 289 | github.com/swaggo/swag v1.5.1/go.mod h1:1Bl9F/ZBpVWh22nY0zmYyASPO1lI/zIwRDrpZU+tv8Y= 290 | github.com/swaggo/swag v1.6.3/go.mod h1:wcc83tB4Mb2aNiL/HP4MFeQdpHUrca+Rp/DRNgWAUio= 291 | github.com/swaggo/swag v1.6.5 h1:2C+t+xyK6p1sujqncYO/VnMvPZcBJjNdKKyxbOdAW8o= 292 | github.com/swaggo/swag v1.6.5/go.mod h1:Y7ZLSS0d0DdxhWGVhQdu+Bu1QhaF5k0RD7FKdiAykeY= 293 | github.com/teris-io/shortid v0.0.0-20171029131806-771a37caa5cf h1:Z2X3Os7oRzpdJ75iPqWZc0HeJWFYNCvKsfpQwFpRNTA= 294 | github.com/teris-io/shortid v0.0.0-20171029131806-771a37caa5cf/go.mod h1:M8agBzgqHIhgj7wEn9/0hJUZcrvt9VY+Ln+S1I5Mha0= 295 | github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= 296 | github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= 297 | github.com/ugorji/go v1.1.5-pre/go.mod h1:FwP/aQVg39TXzItUBMwnWp9T9gPQnXw4Poh4/oBQZ/0= 298 | github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= 299 | github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= 300 | github.com/ugorji/go/codec v0.0.0-20181022190402-e5e69e061d4f/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= 301 | github.com/ugorji/go/codec v1.1.5-pre/go.mod h1:tULtS6Gy1AE1yCENaw4Vb//HLH5njI2tfCQDUqRd8fI= 302 | github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs= 303 | github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= 304 | github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= 305 | github.com/urfave/cli v1.22.2 h1:gsqYFH8bb9ekPA12kRo0hfjngWQjkJPlN9R0N78BoUo= 306 | github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= 307 | github.com/urfave/cli v1.22.4 h1:u7tSpNPPswAFymm8IehJhy4uJMlUuU/GmqSkvJ1InXA= 308 | github.com/urfave/cli v1.22.4/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= 309 | github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= 310 | github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= 311 | github.com/valyala/fasttemplate v0.0.0-20170224212429-dcecefd839c4 h1:gKMu1Bf6QINDnvyZuTaACm9ofY+PRh+5vFz4oxBZeF8= 312 | github.com/valyala/fasttemplate v0.0.0-20170224212429-dcecefd839c4/go.mod h1:50wTf68f99/Zt14pr046Tgt3Lp2vLyFZKzbFXTOabXw= 313 | github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= 314 | github.com/valyala/fasttemplate v1.1.0 h1:RZqt0yGBsps8NGvLSGW804QQqCUYYLsaOjTVHy1Ocw4= 315 | github.com/valyala/fasttemplate v1.1.0/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= 316 | github.com/wg/wrk v0.0.0-20190418015132-0896020a2a28 h1:Ygcegbp1DXD4zU6hi3a4kT3DdmSM2CXlO5Gf6gXP5D4= 317 | github.com/wg/wrk v0.0.0-20190418015132-0896020a2a28/go.mod h1:RMFe58T7IkupbjTbk30t4aZuf6NwLFzp/+xkEyCV92k= 318 | github.com/willf/pad v0.0.0-20190207183901-eccfe5d84172 h1:fXKBlHDmlnhSIrZos0W9Qwh/ISUdxUckckjHO//2G3c= 319 | github.com/willf/pad v0.0.0-20190207183901-eccfe5d84172/go.mod h1:+pVHwmjc9CH7ugBFxESIwQkXkVj0gUj4cFp63TLwP1Y= 320 | github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= 321 | github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= 322 | github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 323 | go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= 324 | go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= 325 | go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= 326 | go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= 327 | golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 328 | golang.org/x/crypto v0.0.0-20190130090550-b01c7a725664/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 329 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 330 | golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 331 | golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 332 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 333 | golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd h1:GGJVjV8waZKRHrgwvtH66z9ZGVurTD1MT0n1Bb+q4aM= 334 | golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 335 | golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 336 | golang.org/x/crypto v0.0.0-20200414173820-0848c9571904 h1:bXoxMPcSLOq08zI3/c5dEBT6lE4eh+jOh886GHrn6V8= 337 | golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 338 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 339 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 340 | golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 341 | golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ= 342 | golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 343 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 344 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 345 | golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 346 | golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 347 | golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 348 | golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 349 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 350 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 351 | golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 352 | golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 353 | golang.org/x/net v0.0.0-20190611141213-3f473d35a33a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 354 | golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 355 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 356 | golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 h1:k7pJ2yAPLPgbskkFdhRCsA77k2fySZ1zf2zCjvQCiIM= 357 | golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 358 | golang.org/x/net v0.0.0-20191204025024-5ee1b9f4859a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 359 | golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa h1:F+8P+gmewFQYRk6JoLQLwjBCTu3mcIURZfNkVweuRKA= 360 | golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 361 | golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 362 | golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e h1:3G+cUijn7XD+S4eJFddp53Pv7+slrESplyjG25HgL+k= 363 | golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 364 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 365 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 366 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 367 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 368 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 369 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 370 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 371 | golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 372 | golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 373 | golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 374 | golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 375 | golang.org/x/sys v0.0.0-20181228144115-9a3f9b0469bb/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 376 | golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 377 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 378 | golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 379 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 380 | golang.org/x/sys v0.0.0-20190610200419-93c9922d18ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 381 | golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 382 | golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a h1:aYOabOQFp6Vj6W1F80affTUvO9UxmJRx8K0gsfABByQ= 383 | golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 384 | golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 385 | golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 386 | golang.org/x/sys v0.0.0-20200116001909-b77594299b42 h1:vEOn+mP2zCOVzKckCZy6YsCtDblrpj/w7B9nxGNELpg= 387 | golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 388 | golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 389 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 390 | golang.org/x/sys v0.0.0-20200413165638-669c56c373c4 h1:opSr2sbRXk5X5/givKrrKj9HXxFpW2sdCiP8MJSKLQY= 391 | golang.org/x/sys v0.0.0-20200413165638-669c56c373c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 392 | golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 393 | golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= 394 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 395 | golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= 396 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 397 | golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 398 | golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 399 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 400 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 401 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 402 | golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 403 | golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 404 | golang.org/x/tools v0.0.0-20190606050223-4d9ae51c2468/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 405 | golang.org/x/tools v0.0.0-20190611222205-d73e1c7e250b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 406 | golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59 h1:QjA/9ArTfVTLfEhClDCG7SGrZkZixxWpwNCDiwJfh88= 407 | golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 408 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 409 | golang.org/x/tools v0.0.0-20191205060818-73c7173a9f7d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 410 | golang.org/x/tools v0.0.0-20200116062425-473961ec044c h1:D0OxfnjPaEGt7AluXNompYUYGhoY3u6+bValgqfd1vE= 411 | golang.org/x/tools v0.0.0-20200116062425-473961ec044c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 412 | golang.org/x/tools v0.0.0-20200416214402-fc959738d646 h1:7CEkhBsBejkW845gR1AmglqMfc1yGzn42FBmtM4jxyM= 413 | golang.org/x/tools v0.0.0-20200416214402-fc959738d646/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 414 | golang.org/x/tools v0.0.0-20200417140056-c07e33ef3290 h1:NXNmtp0ToD36cui5IqWy95LC4Y6vT/4y3RnPxlQPinU= 415 | golang.org/x/tools v0.0.0-20200417140056-c07e33ef3290/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 416 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 417 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 418 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= 419 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 420 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 421 | google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508= 422 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 423 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 424 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 425 | google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= 426 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 427 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 428 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= 429 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= 430 | google.golang.org/protobuf v1.21.0 h1:qdOKuR/EIArgaWNjetjgTzgVTAZ+S/WXVrq9HW9zimw= 431 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= 432 | gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 433 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 434 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 435 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= 436 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 437 | gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= 438 | gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= 439 | gopkg.in/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXadIrXTM= 440 | gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= 441 | gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y= 442 | gopkg.in/go-playground/validator.v9 v9.29.1 h1:SvGtYmN60a5CVKTOzMSyfzWDeZRxRuGvRQyEAKbw1xc= 443 | gopkg.in/go-playground/validator.v9 v9.29.1/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ= 444 | gopkg.in/go-playground/validator.v9 v9.31.0 h1:bmXmP2RSNtFES+bn4uYuHT7iJFJv7Vj+an+ZQdDaD1M= 445 | gopkg.in/go-playground/validator.v9 v9.31.0/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ= 446 | gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno= 447 | gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= 448 | gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= 449 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= 450 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= 451 | gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= 452 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 453 | gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= 454 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 455 | gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= 456 | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 457 | gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo= 458 | gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 459 | gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= 460 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 461 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 462 | rsc.io/quote/v3 v3.1.0 h1:9JKUTTIUgS6kzR9mK1YuGKv6Nl+DijDNIc0ghT58FaY= 463 | rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= 464 | rsc.io/sampler v1.3.0 h1:7uVkIFmeBqHfdjD+gZwtXXI+RODJ2Wc4O7MPEh/QiW4= 465 | rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= 466 | -------------------------------------------------------------------------------- /handler/block/block.go: -------------------------------------------------------------------------------- 1 | package block 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "log" 6 | "net/http" 7 | ) 8 | 9 | func ContractBlock(g *gin.Context) { 10 | log.Println("params:", g.Params) 11 | log.Println("params:", g.PostForm("id")) 12 | // 返回json 13 | g.JSON(http.StatusOK, gin.H{ 14 | "code": 1, 15 | "data": "testok", 16 | "msg": "ok", 17 | }) 18 | //handler.SendResponse(g, nil, gin.H{ 19 | // "code": 1, 20 | // "data": "testok", 21 | // "msg": "ok", 22 | //}) 23 | } 24 | 25 | func PatchContractBlock(g *gin.Context) { 26 | log.Println("params:", g.Params) 27 | log.Println("params:", g.PostForm("id")) 28 | // 返回json 29 | g.JSON(http.StatusOK, gin.H{ 30 | "code": 1, 31 | "data": "testok", 32 | "msg": "ok", 33 | }) 34 | //handler.SendResponse(g, nil, gin.H{ 35 | // "code": 1, 36 | // "data": "testok", 37 | // "msg": "ok", 38 | //}) 39 | } 40 | -------------------------------------------------------------------------------- /handler/handler.go: -------------------------------------------------------------------------------- 1 | package handler 2 | 3 | import ( 4 | "RESTfulGo/pkg/response" 5 | "github.com/gin-gonic/gin" 6 | "net/http" 7 | ) 8 | 9 | type Result struct { 10 | Code int `json:"code"` 11 | Message string `json:"message"` 12 | Data interface{} `json:"data"` 13 | } 14 | 15 | /* 16 | * 返回统一的函数 17 | */ 18 | func SendResponse(g *gin.Context, err error, data interface{}) { 19 | // 解析错误 20 | code, message := response.DecodeErr(err) 21 | 22 | // 返回json 23 | g.JSON(http.StatusOK, Result{ 24 | Code: code, 25 | Message: message, 26 | Data: data, 27 | }) 28 | } 29 | -------------------------------------------------------------------------------- /handler/sd/check.go: -------------------------------------------------------------------------------- 1 | package sd 2 | 3 | import ( 4 | "fmt" 5 | "github.com/gin-gonic/gin" 6 | "github.com/shirou/gopsutil/cpu" 7 | "github.com/shirou/gopsutil/disk" 8 | "github.com/shirou/gopsutil/load" 9 | "github.com/shirou/gopsutil/mem" 10 | "net/http" 11 | ) 12 | 13 | const ( 14 | B = 1 15 | KB = 1024 * B 16 | MB = 1024 * KB 17 | GB = 2014 * MB 18 | ) 19 | 20 | // 健康查询 21 | func HealthCheck(g *gin.Context) { 22 | message := "OK" 23 | // 返回状态 24 | g.String(http.StatusOK, message) 25 | } 26 | 27 | // 查询磁盘容量 28 | func DiskCheck(g *gin.Context) { 29 | u, _ := disk.Usage("/") 30 | usedMB := int(u.Used) / MB 31 | usedGB := int(u.Used) / GB 32 | totalMB := int(u.Total) / MB 33 | totalGB := int(u.Total) / GB 34 | usedPercent := int(u.UsedPercent) 35 | 36 | status := http.StatusOK 37 | text := "OK" 38 | 39 | if usedPercent >= 95 { 40 | status = http.StatusOK 41 | text = "CRITICAL" 42 | } else if usedPercent >= 90 { 43 | status = http.StatusTooManyRequests 44 | text = "WARNING" 45 | } 46 | 47 | message := fmt.Sprintf("%s - Free space: %dMB (%dGB) / %dMB (%dGB) | Used: %d%%", text, usedMB, usedGB, totalMB, totalGB, usedPercent) 48 | g.String(status, "\n"+message) 49 | } 50 | 51 | func CPUCheck(g *gin.Context) { 52 | cores, _ := cpu.Counts(false) 53 | 54 | a, _ := load.Avg() 55 | l1 := a.Load1 56 | l5 := a.Load5 57 | l15 := a.Load15 58 | 59 | status := http.StatusOK 60 | text := "OK" 61 | 62 | if l5 >= float64(cores-1) { 63 | status = http.StatusInternalServerError 64 | text = "CRITICAL" 65 | } else if l5 >= float64(cores-2) { 66 | status = http.StatusTooManyRequests 67 | text = "WARNING" 68 | } 69 | 70 | message := fmt.Sprintf("%s - Load average: %.2f, %.2f, %.2f | Cores: %d", text, l1, l5, l15, cores) 71 | g.String(status, "\n"+message) 72 | } 73 | 74 | func RAMCheck(g *gin.Context) { 75 | u, _ := mem.VirtualMemory() 76 | 77 | usedMB := int(u.Used) / MB 78 | usedGB := int(u.Used) / GB 79 | totalMB := int(u.Total) / MB 80 | totalGB := int(u.Total) / GB 81 | usedPercent := int(u.UsedPercent) 82 | 83 | status := http.StatusOK 84 | text := "OK" 85 | 86 | if usedPercent >= 95 { 87 | status = http.StatusInternalServerError 88 | text = "CRITICAL" 89 | } else if usedPercent >= 90 { 90 | status = http.StatusTooManyRequests 91 | text = "WARNING" 92 | } 93 | 94 | message := fmt.Sprintf("%s - Free space: %dMB (%dGB) / %dMB (%dGB) | Used: %d%%", text, usedMB, usedGB, totalMB, totalGB, usedPercent) 95 | g.String(status, "\n"+message) 96 | } 97 | -------------------------------------------------------------------------------- /handler/user/create.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | import ( 4 | . "RESTfulGo/handler" 5 | "RESTfulGo/model" 6 | "RESTfulGo/pkg/response" 7 | "fmt" 8 | "github.com/gin-gonic/gin" 9 | "github.com/lexkong/log" 10 | ) 11 | 12 | /** 13 | 创建用户逻辑: 14 | 从 HTTP 消息体获取参数(用户名和密码) 15 | 参数校验 16 | 加密密码 17 | 在数据库中添加数据记录 18 | 返回结果(这里是用户名) 19 | */ 20 | // @Summary Add new user to the database 21 | // @Description Add a new user 22 | // @Tags user 23 | // @Accept json 24 | // @Produce json 25 | // @Param user body user.CreateRequest true "Create a new user" 26 | // @Success 200 {object} user.CreateResponse "{"code":0,"message":"OK","data":{"username":"kong"}}" 27 | // @Router /v1/user [post] 28 | func Create(g *gin.Context) { 29 | var r = CreateRequest{ 30 | Username: g.PostForm("username"), 31 | Password: g.PostForm("password"), 32 | } 33 | var err error 34 | 35 | //if err := g.ShouldBindJSON(&r); err != nil { 36 | // log.Debugf("参数效验失败 %s", r) 37 | // //g.JSON(http.StatusOK, gin.H{"error": response.ErrBind}) 38 | // SendResponse(g, err, nil) 39 | // return 40 | //} 41 | 42 | // 效验参数 43 | if err := r.checkParam(); err != nil { 44 | SendResponse(g, err, nil) 45 | return 46 | } 47 | 48 | user := model.UserModel{ 49 | Username: r.Username, 50 | Password: r.Password, 51 | } 52 | 53 | if err := user.Validate(); err != nil { 54 | SendResponse(g, response.ErrValidation, nil) 55 | return 56 | } 57 | 58 | // 59 | username := g.Param("username") 60 | log.Infof("username = %s", username) 61 | 62 | // 63 | desc := g.Query("desc") 64 | log.Infof("desc = %s", desc) 65 | 66 | contentType := g.GetHeader("Content-Type") 67 | log.Infof("Header contentType = %s", contentType) 68 | 69 | log.Debugf("username is [%s], password is [%s]", r.Username, r.Password) 70 | 71 | // 密码加密 72 | if err := user.Encrypt(); err != nil { 73 | log.Errorf(err, "Encrypt error") 74 | SendResponse(g, response.ErrEncrypt, nil) 75 | return 76 | } 77 | 78 | // 新增用户 79 | if err := user.Create(); err != nil { 80 | log.Errorf(err, "ErrDatabase") 81 | SendResponse(g, response.ErrDatabase, nil) 82 | return 83 | } 84 | 85 | if response.IsErrUserNotFound(err) { 86 | log.Debug("err type Is ErrUserNotFound") 87 | } 88 | 89 | rsp := CreateResponse{Username: username} 90 | 91 | //code, message := response.DecodeErr(err) 92 | //g.JSON(http.StatusOK, gin.H{"code":code, "message": message}) 93 | SendResponse(g, nil, rsp) 94 | } 95 | 96 | func (r *CreateRequest) checkParam() error { 97 | if r.Username == "" { 98 | return response.New(response.ErrUserNotFound, fmt.Errorf("参数不能为空")) 99 | } 100 | 101 | if r.Password == "" { 102 | return fmt.Errorf("password is empty") 103 | } 104 | return nil 105 | } 106 | -------------------------------------------------------------------------------- /handler/user/delete.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | import ( 4 | "RESTfulGo/handler" 5 | "RESTfulGo/model" 6 | "RESTfulGo/pkg/response" 7 | "github.com/gin-gonic/gin" 8 | "github.com/lexkong/log" 9 | "strconv" 10 | ) 11 | 12 | // @Summary Delete new user to the database 13 | // @Description Delete a new user 14 | // @Tags user 15 | // @Accept json 16 | // @Produce json 17 | // @Param user body user.CreateRequest true "Delete a new user" 18 | // @Success 200 {object} handler.Result "{"code":0,"message":"OK","data":{"username":"kong"}}" 19 | // @Router /v1/user [delete] 20 | func Delete(g *gin.Context) { 21 | userId, _ := strconv.ParseUint(g.Param("id"), 10, 64) 22 | if err := model.DeleteUser(userId); err != nil { 23 | log.Fatal("delete user err:", err) 24 | handler.SendResponse(g, response.ErrDatabase, nil) 25 | return 26 | } 27 | handler.SendResponse(g, nil, nil) 28 | } 29 | -------------------------------------------------------------------------------- /handler/user/get.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | import ( 4 | "RESTfulGo/handler" 5 | "RESTfulGo/model" 6 | "github.com/gin-gonic/gin" 7 | "log" 8 | ) 9 | 10 | // @Summary 11 | // @Description 12 | // @Tags user 13 | // @Accept json 14 | // @Produce json 15 | // @Param user body user.GetUserResponse true "Get user" 16 | // @Success 200 {object} handler.Result "{"code":0,"message":"OK","data":{"username":"kong"}}" 17 | // @Router /v1/user [get] 18 | func Get(g *gin.Context) { 19 | userId := g.Query("id") 20 | user, err := model.GetUser(userId) 21 | if err != nil { 22 | log.Println("GetUser Err:", err) 23 | return 24 | } 25 | handler.SendResponse(g, nil, user) 26 | } 27 | -------------------------------------------------------------------------------- /handler/user/list.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | import ( 4 | "RESTfulGo/handler" 5 | "RESTfulGo/model" 6 | "github.com/gin-gonic/gin" 7 | "log" 8 | ) 9 | 10 | // @Summary 获取用户列表 11 | // @Description Delete a new user 12 | // @Tags user 13 | // @Accept json 14 | // @Produce json 15 | // @Success 200 {object} user.ListResponse "{"code":0,"message":"OK","data":[{"username":"kong"},{"username":"kong"}]}" 16 | // @Router /v1/user [get] 17 | func List(g *gin.Context) { 18 | log.Println("List users") 19 | var listRequest ListRequest 20 | if err := g.ShouldBind(&listRequest); err != nil{ 21 | log.Fatal("err ShouldBind listRequest") 22 | return 23 | } 24 | userList, count, err := model.GetUserList(listRequest.Pagesize, listRequest.PageNum) 25 | if err != nil { 26 | log.Fatal("getUserList err:", err) 27 | handler.SendResponse(g, err, nil) 28 | return 29 | } 30 | handler.SendResponse(g, nil, ListResponse{ 31 | TotalCount: count, 32 | UserList: userList, 33 | } ) 34 | } 35 | -------------------------------------------------------------------------------- /handler/user/login.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | import ( 4 | . "RESTfulGo/handler" 5 | "RESTfulGo/model" 6 | "RESTfulGo/pkg/auth" 7 | "RESTfulGo/pkg/response" 8 | "RESTfulGo/pkg/token" 9 | "github.com/gin-gonic/gin" 10 | ) 11 | 12 | func Login(g *gin.Context) { 13 | // binding the data with the user struct 14 | var r = model.UserModel{ 15 | Username: g.PostForm("username"), 16 | Password: g.PostForm("password"), 17 | } 18 | 19 | // get mysql user 20 | user, err := model.GetUser(r.Username) 21 | if err != nil { 22 | SendResponse(g, response.ErrUserNotFound, nil) 23 | return 24 | } 25 | 26 | // 对比密码 27 | if err := auth.Compare(user.Password, r.Password); err != nil { 28 | SendResponse(g, response.ErrPasswordIncorrect, nil) 29 | return 30 | } 31 | 32 | // 登录并获取token get token jwt 33 | t, err := token.Sign(g, token.Context{Username: user.Username, ID: user.Id}, "") 34 | if err != nil { 35 | SendResponse(g, response.ErrToken, nil) 36 | return 37 | } 38 | // 返回生成的token信息 39 | SendResponse(g, nil, model.Token{Token: t}) 40 | } 41 | -------------------------------------------------------------------------------- /handler/user/update.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | import ( 4 | "RESTfulGo/handler" 5 | "github.com/gin-gonic/gin" 6 | "net/http" 7 | ) 8 | 9 | func Update(g *gin.Context) { 10 | 11 | g.JSON(http.StatusOK, handler.Result{ 12 | Code: 200, 13 | Message: "update", 14 | Data: nil, 15 | }) 16 | } 17 | -------------------------------------------------------------------------------- /handler/user/user.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | import ( 4 | "RESTfulGo/model" 5 | ) 6 | 7 | //CreateRequest 和 CreateResponse, 8 | //并将这些结构体统一放在一个 Go 文件夹中,以方便后期维护和修改 9 | 10 | type ListRequest struct { 11 | Pagesize int `json:"pagesize"` 12 | PageNum int `json:"pagenum"` 13 | } 14 | 15 | type ListResponse struct { 16 | TotalCount int64 `json:"total_count"` 17 | UserList []*model.UserInfo `json:"user_list"` 18 | } 19 | 20 | type CreateRequest struct { 21 | Username string `json:"username"` 22 | Password string `json:"password"` 23 | } 24 | 25 | type CreateResponse struct { 26 | Username string `json:"username"` 27 | } 28 | 29 | type GetUserResponse struct { 30 | Id string `json:"id"` 31 | } -------------------------------------------------------------------------------- /log/apiserver.log.20200114155145312.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChinaArJun/RESTfulGo/bf921f20e1994dbf2ff1d817fdc04d24c015cde1/log/apiserver.log.20200114155145312.zip -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "RESTfulGo/config" 5 | "RESTfulGo/model" 6 | "RESTfulGo/router" 7 | "errors" 8 | "fmt" 9 | "github.com/gin-gonic/gin" 10 | "github.com/lexkong/log" 11 | "github.com/spf13/pflag" 12 | "github.com/spf13/viper" 13 | "net/http" 14 | _ "net/http/pprof" 15 | "runtime" 16 | "strconv" 17 | "time" 18 | ) 19 | 20 | var ( 21 | cfg = pflag.StringP("config", "c", "", "apiserver config file path.") 22 | version = pflag.BoolP("version", "v", false, "show version info") 23 | ) 24 | 25 | func main() { 26 | numbers := runtime.NumCPU() 27 | fmt.Println("nums cpu : ", numbers) 28 | //pflag.Parse() 29 | //if *version { 30 | // v := v.Get() 31 | // marshalled, err := json.MarshalIndent(&v, "", " ") 32 | // if err != nil { 33 | // fmt.Printf("%v\n", err) 34 | // os.Exit(1) 35 | // } 36 | // 37 | // fmt.Println(string(marshalled)) 38 | // return 39 | //} 40 | // init config 41 | if err := config.Init(*cfg); err != nil { 42 | panic(err) 43 | } 44 | 45 | // 配置文件设置gin运行模式 46 | gin.SetMode(viper.GetString("runmode")) 47 | 48 | // 测试日志打印转存效果 49 | //testLog() 50 | 51 | g := gin.New() 52 | 53 | middlewares := []gin.HandlerFunc{} 54 | 55 | // routers 56 | router.Load(g, middlewares...) 57 | 58 | // init db 59 | go model.DB.Init() 60 | //defer model.DB.Close() 61 | 62 | //cfg := tars.GetServerConfig() 63 | //profMux := &tars.TarsHttpMux{} 64 | //profMux.HandleFunc("/debug/pprof/", pprof.Index) 65 | //profMux.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline) 66 | //profMux.HandleFunc("/debug/pprof/profile", pprof.Profile) 67 | //profMux.HandleFunc("/debug/pprof/symbol", pprof.Symbol) 68 | //profMux.HandleFunc("/debug/pprof/trace", pprof.Trace) 69 | //tars.AddHttpServant(profMux, cfg.App+"."+cfg.Server+".ProfObj") 70 | 71 | log.Infof("Start to requests on http address: %s", viper.GetString("addr")) 72 | log.Info(http.ListenAndServe(viper.GetString("addr"), g).Error()) 73 | 74 | } 75 | 76 | func testLog(s chan<- string, index, value int) { 77 | fmt.Println("testLog:", index, value) 78 | s <- "testLog" + strconv.Itoa(value) 79 | } 80 | 81 | func testLogInfo() { 82 | for { 83 | // 延迟1秒 84 | log.Info("2333333333333333333333333333233333333333333333333333333323333333333333333333333333332333333333333333333333333333") 85 | time.Sleep(time.Second * 2) 86 | } 87 | } 88 | 89 | // API 服务器健康状态自检 90 | func pingServer() error { 91 | for i := 9; i < 10; i++ { 92 | // ping the server By Get request to "health" 93 | res, err := http.Get(viper.GetString("url") + "/sd/health") 94 | if err == nil && res.StatusCode == http.StatusOK { 95 | return nil 96 | } 97 | log.Info("time sleep 1 ") 98 | //time.Sleep(time.Second) 99 | } 100 | return errors.New("服务检查错误") 101 | } 102 | -------------------------------------------------------------------------------- /model/db.sql: -------------------------------------------------------------------------------- 1 | create database if not exists `db_apiserver` default character set utf8 ; 2 | 3 | # 选中库 4 | use `db_apiserver`; 5 | 6 | # 查询是否存在该数据库 7 | DROP table if exists `tb_users`; 8 | 9 | create table `tb_user` ( 10 | `id` int(20) unsigned not null auto_increment, 11 | `username` varchar(255) not null , 12 | `password` varchar(255) not null , 13 | `state` int(10) default '1' comment '用户状态 0默认 1删除', 14 | `createdAt` timestamp null default null, 15 | `updatedAt` timestamp null default null, 16 | `deletedAt` timestamp null default null, 17 | primary key (`id`), 18 | unique key `username` (`username`), 19 | key `idx_tb_users_deletedAt` (`deletedAt`) 20 | )ENGINE=MyISAM AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 comment '用户表' 21 | /*!40101 SET character_set_client = @saved_cs_client */; 22 | 23 | -- 24 | -- Dumping data for table `tb_users` 25 | -- 26 | 27 | LOCK TABLES `tb_users` WRITE; 28 | /*!40000 ALTER TABLE `tb_users` DISABLE KEYS */; 29 | INSERT INTO `tb_users` VALUES (0,'admin','$2a$10$veGcArz47VGj7l9xN7g2iuT9TF21jLI1YGXarGzvARNdnt4inC9PG','2018-05-27 16:25:33','2018-05-27 16:25:33',NULL); 30 | /*!40000 ALTER TABLE `tb_users` ENABLE KEYS */; 31 | UNLOCK TABLES; -------------------------------------------------------------------------------- /model/init.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "fmt" 5 | "github.com/jinzhu/gorm" 6 | _ "github.com/jinzhu/gorm/dialects/mysql" 7 | "github.com/lexkong/log" 8 | "github.com/spf13/viper" 9 | ) 10 | 11 | var DB *Database 12 | 13 | type Database struct { 14 | Self *gorm.DB 15 | Docker *gorm.DB 16 | } 17 | 18 | func (db *Database) Init() { 19 | DB = &Database{ 20 | Self: GetSelfDB(), 21 | //Docker: GetDockerDB(), 22 | } 23 | } 24 | 25 | func GetSelfDB() *gorm.DB { 26 | return InitSelfDB() 27 | } 28 | 29 | func GetDockerDB() *gorm.DB { 30 | return InitDockerDB() 31 | } 32 | 33 | func InitSelfDB() *gorm.DB { 34 | return openDB( 35 | viper.GetString("db.username"), 36 | viper.GetString("db.password"), 37 | viper.GetString("db.addr"), 38 | viper.GetString("db.name")) 39 | } 40 | 41 | func InitDockerDB() *gorm.DB { 42 | return openDB( 43 | viper.GetString("docker_db.username"), 44 | viper.GetString("docker_db.password"), 45 | viper.GetString("docker_db.addr"), 46 | viper.GetString("docker_db.name")) 47 | } 48 | 49 | func openDB(username string, password string, addr string, name string) *gorm.DB { 50 | config := fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8&parseTime=%t&loc=%s", 51 | username, 52 | password, 53 | addr, 54 | name, 55 | true, 56 | //"Asia/Shanghai"), 57 | "Local") 58 | 59 | db, err := gorm.Open("mysql", config) 60 | if err != nil { 61 | log.Errorf(err, "Database connection failed. Database name: %s", name) 62 | } 63 | // set for db connection 64 | setupDB(db) 65 | 66 | return db 67 | } 68 | 69 | func setupDB(db *gorm.DB) { 70 | db.LogMode(viper.GetBool("gormlog")) 71 | //db.DB().SetMaxIdleConns(0) // 用于设置闲置的连接数.设置闲置的连接数则当开启的一个连接使用完成后可以放在池里等候下一次使用。 72 | } 73 | 74 | func (db *Database) Close() { 75 | DB.Docker.Close() 76 | DB.Self.Close() 77 | } 78 | -------------------------------------------------------------------------------- /model/model.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import "time" 4 | 5 | type BaseModel struct { 6 | // gorm模型定义 column:数据库名称 "-" json忽略这个字段 7 | Id int `gorm:"primary_key;auto_increment;column:id" json:"-"` 8 | CreatedAt time.Time `gorm:"column:createdAt" json:"-"` 9 | UpdatedAt time.Time `gorm:"column:updatedAt" json:"-"` 10 | DeletedAt *time.Time `gorm:"column:deletedAt" sql:"index" json:"-"` 11 | //CreatedAt time.Time `gorm:"column:createdAt" json:"-"` 12 | //UpdatedAt time.Time `gorm:"column:updatedAt" json:"-"` 13 | //DeletedAt *time.Time `gorm:"column:deletedAt" sql:"index" json:"-"` 14 | } 15 | 16 | type UserInfo struct { 17 | Id int `json:"id"` 18 | Username string `json:"username"` 19 | SayHello string `json:"sayHello"` 20 | Password string `json:"password"` 21 | CreatedAt string `json:"createdAt"` 22 | UpdatedAt string `json:"updatedAt"` 23 | } 24 | 25 | type Token struct { 26 | Token string `json:"token"` 27 | } 28 | -------------------------------------------------------------------------------- /model/user.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "RESTfulGo/pkg/auth" 5 | validator "gopkg.in/go-playground/validator.v9" 6 | ) 7 | 8 | type UserModel struct { 9 | BaseModel 10 | Username string `json:"username" gorm:"column:username;not null" binding:"required" validate:"min=1,max=32"` 11 | Password string `json:"username" gorm:"column:password;not null" binding:"required" validate:"min=5,max=32"` 12 | } 13 | 14 | // 设置用户表名称 15 | func (u *UserModel) TableName() string { 16 | return "tb_users" 17 | } 18 | 19 | // 新建新用户 20 | func (u *UserModel) Create() error { 21 | return DB.Self.Create(&u).Error 22 | } 23 | 24 | func DeleteUser(userId uint64) error { 25 | db := DB.Self.Where("id = ?", userId).Update("state", "0") 26 | return db.Error 27 | } 28 | 29 | func (u *UserModel)UpdateUser() error { 30 | return DB.Self.Where("id = ?", u.Id).Update(u).Error 31 | } 32 | 33 | func GetUserList(pagesize int, pagenum int) ([]*UserInfo, int64, error) { 34 | var userList = make([]*UserInfo, 0) 35 | var count int64 36 | 37 | db := DB.Self.Where("*").Limit(pagesize).Offset(pagenum).Model(&userList).Count(&count) 38 | if db.Error != nil { 39 | return nil, 0, db.Error 40 | } 41 | return userList, count, nil 42 | } 43 | 44 | func GetUser(userId string) (*UserModel, error) { 45 | model := &UserModel{} 46 | db := DB.Self.Where("id = ?", userId).First(&model) 47 | return model, db.Error 48 | } 49 | 50 | // 加密密码 51 | func (u *UserModel) Encrypt() (err error) { 52 | u.Password, err = auth.Encrypt(u.Password) 53 | return err 54 | } 55 | 56 | /* 57 | 效验用户参数 58 | */ 59 | func (u *UserModel) Validate() error { 60 | validator := validator.New() 61 | return validator.Struct(u) 62 | } 63 | -------------------------------------------------------------------------------- /pkg/auth/auth.go: -------------------------------------------------------------------------------- 1 | package auth 2 | 3 | import "golang.org/x/crypto/bcrypt" 4 | 5 | /* 6 | 哈希加密 7 | */ 8 | func Encrypt(source string) (string, error) { 9 | hashedBytes, err := bcrypt.GenerateFromPassword([]byte(source), bcrypt.DefaultCost) 10 | return string(hashedBytes), err 11 | } 12 | 13 | /* 14 | 校对 15 | */ 16 | func Compare(hashedPasword, password string) error { 17 | return bcrypt.CompareHashAndPassword([]byte(hashedPasword), []byte(password)) 18 | } 19 | -------------------------------------------------------------------------------- /pkg/response/code.go: -------------------------------------------------------------------------------- 1 | package response 2 | 3 | var ( 4 | OK = &Errno{Code: 0, Message: "OK"} 5 | InternalServerError = &Errno{Code: 10001, Message: "Internal server error"} 6 | ErrBind = &Errno{Code: 10002, Message: "Error occurred while binding the request body to the struct"} 7 | 8 | // user errors 9 | ErrUserNotFound = &Errno{Code: 20102, Message: "The user was not found"} 10 | 11 | // validation failed 12 | ErrValidation = &Errno{Code: 20001, Message: "Validation failed."} 13 | ErrDatabase = &Errno{Code: 20002, Message: "Database error."} 14 | ErrToken = &Errno{Code: 20003, Message: "Error occurred while signing the JSON web token."} 15 | 16 | // user errors 17 | ErrEncrypt = &Errno{Code: 20101, Message: "Error occurred while encrypting the user password."} 18 | ErrTokenInvalid = &Errno{Code: 20103, Message: "The token was invalid."} 19 | ErrPasswordIncorrect = &Errno{Code: 20104, Message: "The password was incorrect."} 20 | ) 21 | -------------------------------------------------------------------------------- /pkg/response/error.go: -------------------------------------------------------------------------------- 1 | package response 2 | 3 | import "fmt" 4 | 5 | type Errno struct { 6 | Code int 7 | Message string 8 | } 9 | 10 | func (err Errno) Error() string { 11 | return err.Message 12 | } 13 | 14 | // Err represents an error 15 | type Err struct { 16 | Code int 17 | Message string 18 | Err error 19 | } 20 | 21 | func New(errno *Errno, err error) *Err { 22 | return &Err{Code: errno.Code, Message: errno.Message, Err: err} 23 | } 24 | 25 | func (err *Err) Add(message string) error { 26 | //err.Message = fmt.Sprintf("%s %s", err.Message, message) 27 | err.Message += " " + message 28 | return err 29 | } 30 | 31 | func (err *Err) Addf(format string, args ...interface{}) error { 32 | //return err.Message = fmt.Sprintf("%s %s", err.Message, fmt.Sprintf(format, args...)) 33 | err.Message += " " + fmt.Sprintf(format, args...) 34 | return err 35 | } 36 | 37 | func (err *Err) Error() string { 38 | return fmt.Sprintf("Err - code: %d, message: %s, error: %s", err.Code, err.Message, err.Err) 39 | } 40 | 41 | func IsErrUserNotFound(err error) bool { 42 | code, _ := DecodeErr(err) 43 | return code == ErrUserNotFound.Code 44 | } 45 | 46 | func DecodeErr(err error) (int, string) { 47 | if err == nil { 48 | return OK.Code, OK.Message 49 | } 50 | 51 | switch typed := err.(type) { 52 | case *Err: 53 | return typed.Code, typed.Message 54 | case *Errno: 55 | return typed.Code, typed.Message 56 | default: 57 | } 58 | 59 | return InternalServerError.Code, err.Error() 60 | } 61 | -------------------------------------------------------------------------------- /pkg/token/token.go: -------------------------------------------------------------------------------- 1 | package token 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "github.com/dgrijalva/jwt-go" 7 | "github.com/gin-gonic/gin" 8 | "github.com/spf13/viper" 9 | "time" 10 | ) 11 | 12 | var ( 13 | // ErrMissingHeader means the `Authorization` header was empty. 14 | ErrMissingHeader = errors.New("The length of the `Authorization` header is zero.") 15 | ) 16 | 17 | // Context is the context of the JSON web token. 18 | type Context struct { 19 | ID int 20 | Username string 21 | } 22 | 23 | // secretFunc validates the secret format. 24 | func secretFunc(secret string) jwt.Keyfunc { 25 | return func(token *jwt.Token) (interface{}, error) { 26 | // Make sure the `alg` is what we except. 27 | if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { 28 | return nil, jwt.ErrSignatureInvalid 29 | } 30 | 31 | return []byte(secret), nil 32 | } 33 | } 34 | 35 | // Parse validates the token with the specified secret, 36 | // and returns the context if the token was valid. 37 | func Parse(tokenString string, secret string) (*Context, error) { 38 | ctx := &Context{} 39 | 40 | // Parse the token. 41 | token, err := jwt.Parse(tokenString, secretFunc(secret)) 42 | 43 | // Parse error. 44 | if err != nil { 45 | return ctx, err 46 | 47 | // Read the token if it's valid. 48 | } else if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid { 49 | ctx.ID = int(claims["id"].(float64)) 50 | ctx.Username = claims["username"].(string) 51 | return ctx, nil 52 | 53 | // Other errors. 54 | } else { 55 | return ctx, err 56 | } 57 | } 58 | 59 | // 自动解密 60 | func ParseRequest(g *gin.Context) (*Context, error) { 61 | header := g.Request.Header.Get("Authorization") 62 | // load the jwt secret from config 63 | secret := viper.GetString("jwt_secret") 64 | if len(header) == 0 { 65 | return &Context{}, ErrMissingHeader 66 | } 67 | var t string 68 | fmt.Sscanf(header, "Bearer %s", &t) 69 | Context, error := Parse(t, secret) 70 | return Context, error 71 | } 72 | 73 | // 生成令牌 74 | func Sign(ctx *gin.Context, c Context, secret string) (tokenString string, err error) { 75 | // Load the jwt secret from the gin config 76 | if secret == "" { 77 | secret = viper.GetString("jwt_secret") 78 | } 79 | // the token content 使用id username 生成token令牌 80 | token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{ 81 | "id": c.ID, 82 | "username": c.Username, 83 | "nbf": time.Now().Unix(), 84 | "iat": time.Now().Unix(), 85 | }) 86 | tokenString, err = token.SignedString([]byte(secret)) 87 | return 88 | } 89 | -------------------------------------------------------------------------------- /pkg/version/version.go: -------------------------------------------------------------------------------- 1 | package version 2 | 3 | import ( 4 | "fmt" 5 | "runtime" 6 | ) 7 | 8 | // Info contains versioning information. 9 | type Info struct { 10 | GitTag string `json:"gitTag"` 11 | GitCommit string `json:"gitCommit"` 12 | GitTreeState string `json:"gitTreeState"` 13 | BuildDate string `json:"buildDate"` 14 | GoVersion string `json:"goVersion"` 15 | Compiler string `json:"compiler"` 16 | Platform string `json:"platform"` 17 | } 18 | 19 | var ( 20 | gitTag string = "" 21 | gitCommit string = "$Format:%H$" // sha1 from git, output of $(git rev-parse HEAD) 22 | gitTreeState string = "not a git tree" // state of git tree, either "clean" or "dirty" 23 | buildDate string = "1970-01-01T00:00:00Z" // build date in ISO8601 format, output of $(date -u +'%Y-%m-%dT%H:%M:%SZ') 24 | ) 25 | 26 | // String returns info as a human-friendly version string. 27 | func (info Info) String() string { 28 | return info.GitTag 29 | } 30 | 31 | func Get() Info { 32 | return Info{ 33 | GitTag: gitTag, 34 | GitCommit: gitCommit, 35 | GitTreeState: gitTreeState, 36 | BuildDate: buildDate, 37 | GoVersion: runtime.Version(), 38 | Compiler: runtime.Compiler, 39 | Platform: fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH), // 运行的设备和设备结构 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /router/middleware/auth.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "RESTfulGo/handler" 5 | "RESTfulGo/pkg/response" 6 | "RESTfulGo/pkg/token" 7 | "github.com/gin-gonic/gin" 8 | ) 9 | 10 | func AuthMiddleware() gin.HandlerFunc { 11 | return func(g *gin.Context) { 12 | if _, err := token.ParseRequest(g); err != nil { 13 | // 没有token,拦截请求 14 | handler.SendResponse(g, response.ErrTokenInvalid, nil) 15 | g.Abort() 16 | return 17 | } 18 | g.Next() 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /router/middleware/header.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "net/http" 5 | "time" 6 | 7 | "github.com/gin-gonic/gin" 8 | ) 9 | 10 | // 这里设置请求头属性 11 | 12 | //// NoCache is a middleware function that appends headers 13 | //// to prevent the client from caching the HTTP response. 14 | func NoCache(c *gin.Context) { 15 | c.Header("Cache-Control", "no-cache, no-store, max-age=0, must-revalidate, value") 16 | c.Header("Expires", "Thu, 01 Jan 1970 00:00:00 GMT") 17 | c.Header("Last-Modified", time.Now().UTC().Format(http.TimeFormat)) 18 | c.Next() 19 | } 20 | 21 | // Options is a middleware function that appends headers 22 | // for options requests and aborts then exits the middleware 23 | // chain and ends the request. 24 | func Options(c *gin.Context) { 25 | if c.Request.Method != "OPTIONS" { 26 | c.Next() 27 | } else { 28 | c.Header("Access-Control-Allow-Origin", "*") 29 | c.Header("Access-Control-Allow-Methods", "GET,POST,PUT,PATCH,DELETE,OPTIONS") 30 | c.Header("Access-Control-Allow-Headers", "authorization, origin, content-type, accept") 31 | c.Header("Allow", "HEAD,GET,POST,PUT,PATCH,DELETE,OPTIONS") 32 | c.Header("Content-Type", "application/json") 33 | c.AbortWithStatus(200) 34 | } 35 | } 36 | 37 | // Secure is a middleware function that appends security 38 | // and resource access headers. 39 | func Secure(c *gin.Context) { 40 | c.Header("Access-Control-Allow-Origin", "*") 41 | c.Header("X-Frame-Options", "DENY") 42 | c.Header("X-Content-Type-Options", "nosniff") 43 | c.Header("X-XSS-Protection", "1; mode=block") 44 | if c.Request.TLS != nil { 45 | c.Header("Strict-Transport-Security", "max-age=31536000") 46 | } 47 | 48 | // Also consider adding Content-Security-Policy headers 49 | // c.Header("Content-Security-Policy", "script-src 'self' https://cdnjs.cloudflare.com") 50 | } 51 | -------------------------------------------------------------------------------- /router/middleware/logging.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "RESTfulGo/pkg/response" 5 | "bytes" 6 | "encoding/json" 7 | "io/ioutil" 8 | "regexp" 9 | "time" 10 | 11 | "RESTfulGo/handler" 12 | 13 | "github.com/gin-gonic/gin" 14 | "github.com/lexkong/log" 15 | "github.com/willf/pad" 16 | ) 17 | 18 | type bodyLogWriter struct { 19 | gin.ResponseWriter 20 | body *bytes.Buffer 21 | } 22 | 23 | func (w bodyLogWriter) Write(b []byte) (int, error) { 24 | w.body.Write(b) 25 | return w.ResponseWriter.Write(b) 26 | } 27 | 28 | // Logging is a middleware function that logs the each request. 29 | func Logging() gin.HandlerFunc { 30 | return func(c *gin.Context) { 31 | start := time.Now().UTC() 32 | path := c.Request.URL.Path 33 | 34 | reg := regexp.MustCompile("(/v1/user|/login)") 35 | if !reg.MatchString(path) { 36 | return 37 | } 38 | 39 | // Skip for the health check requests. 40 | if path == "/sd/health" || path == "/sd/ram" || path == "/sd/cpu" || path == "/sd/disk" { 41 | return 42 | } 43 | 44 | // Read the Body content 45 | var bodyBytes []byte 46 | if c.Request.Body != nil { 47 | bodyBytes, _ = ioutil.ReadAll(c.Request.Body) 48 | } 49 | 50 | // Restore the io.ReadCloser to its original state 51 | c.Request.Body = ioutil.NopCloser(bytes.NewBuffer(bodyBytes)) 52 | 53 | // The basic informations. 54 | method := c.Request.Method 55 | // 客户端IP 56 | ip := c.ClientIP() 57 | // 客户端设备 58 | phone := c.GetHeader("User-Agent") 59 | 60 | //log.Debugf("New request come in, path: %s, Method: %s, body `%s`", path, method, string(bodyBytes)) 61 | blw := &bodyLogWriter{ 62 | body: bytes.NewBufferString(""), 63 | ResponseWriter: c.Writer, 64 | } 65 | c.Writer = blw 66 | 67 | // Continue. 68 | c.Next() 69 | 70 | // Calculates the latency. 71 | end := time.Now().UTC() 72 | latency := end.Sub(start) 73 | 74 | code, message := -1, "" 75 | 76 | // get code and message 77 | var result handler.Result 78 | if err := json.Unmarshal(blw.body.Bytes(), &result); err != nil { 79 | log.Errorf(err, "response body can not unmarshal to model.Response struct, body: `%s`", blw.body.Bytes()) 80 | code = response.InternalServerError.Code 81 | message = err.Error() 82 | } else { 83 | code = result.Code 84 | message = result.Message 85 | } 86 | 87 | log.Infof("%-13s | %-12s | %s %s %s | {code: %d, message: %s}", latency, ip, phone, pad.Right(method, 5, ""), path, code, message) 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /router/middleware/requestid.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "github.com/satori/go.uuid" 6 | ) 7 | 8 | // 全局中间件 返回全局请求ID 9 | func RequestId() gin.HandlerFunc { 10 | return func(g *gin.Context) { 11 | requestId := g.Request.Header.Get("X-Request-Id") 12 | 13 | if requestId == "" { 14 | u4 := uuid.NewV4() 15 | requestId = u4.String() 16 | } 17 | 18 | g.Set("X-Request-Id", requestId) 19 | 20 | g.Writer.Header().Set("X-Request-Id", requestId) 21 | g.Next() 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /router/router.go: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | import ( 4 | _ "RESTfulGo/docs" // 注册swag目录 5 | "RESTfulGo/handler/block" 6 | "RESTfulGo/handler/sd" 7 | "RESTfulGo/handler/user" 8 | "RESTfulGo/router/middleware" 9 | "github.com/gin-contrib/pprof" 10 | "github.com/gin-gonic/gin" 11 | ginSwagger "github.com/swaggo/gin-swagger" 12 | "github.com/swaggo/gin-swagger/swaggerFiles" 13 | ) 14 | 15 | func Load(g *gin.Engine, mw ...gin.HandlerFunc) *gin.Engine { 16 | // 设置请求的Header 参数 17 | // 在处理某些请求时可能因为程序 bug 或者其他异常情况导致程序 panic, 18 | // 这时候为了不影响下一次请求的调用,需要通过 gin.Recovery()来恢复 API 服务器 19 | g.Use(gin.Recovery()) 20 | // 强制浏览器不使用缓存 21 | g.Use(middleware.NoCache) 22 | // 浏览器跨域 OPTIONS 请求设置 23 | g.Use(middleware.Options) 24 | // 一些安全设置 25 | g.Use(middleware.Secure) 26 | g.Use(mw...) 27 | 28 | pprof.Register(g) 29 | 30 | // 设置请求ID生成全局中间件 31 | g.Use(middleware.RequestId()) 32 | // 请求信息中间件 - 会消耗一些性能 33 | g.Use(middleware.Logging()) 34 | 35 | g.POST("/login", user.Login) 36 | 37 | g.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler)) 38 | 39 | mobile := g.Group("mobile") 40 | { 41 | // 用户 42 | u := mobile.Group("v1/user", middleware.AuthMiddleware()) 43 | { 44 | u.POST("", user.Create) //创建用户 45 | u.PUT("/:id", user.Update) // 更新用户 46 | u.GET("/:id", user.Get) //获取指定用户的数据 47 | } 48 | } 49 | 50 | admin := g.Group("admin") 51 | { 52 | u := admin.Group("user") 53 | { 54 | u.GET("", nil) 55 | u.DELETE("/:id", user.Delete) //删除用户 56 | u.PUT("/:id", user.Update) // 更新用户 57 | u.GET("", user.List) //用户列表 58 | u.GET("/:id", user.Get) //获取指定用户的数据 59 | } 60 | product := admin.Group("product") 61 | { 62 | product.GET("", nil) 63 | } 64 | 65 | // 服务器的一些配置 66 | svcd := admin.Group("/sd") 67 | { 68 | svcd.GET("/health", sd.HealthCheck) 69 | svcd.GET("/disk", sd.DiskCheck) 70 | svcd.GET("/cpu", sd.CPUCheck) 71 | svcd.GET("/ram", sd.RAMCheck) 72 | } 73 | chain := admin.Group("/test") 74 | { 75 | chain.POST("/contract", block.ContractBlock) 76 | chain.PATCH("/contract", block.PatchContractBlock) 77 | } 78 | } 79 | 80 | return g 81 | } 82 | -------------------------------------------------------------------------------- /util/cpu.profile: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChinaArJun/RESTfulGo/bf921f20e1994dbf2ff1d817fdc04d24c015cde1/util/cpu.profile -------------------------------------------------------------------------------- /util/util.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "github.com/teris-io/shortid" 6 | ) 7 | 8 | func GenShortId() (string, error) { 9 | return shortid.Generate() 10 | } 11 | 12 | func GetReqID(g *gin.Context) string { 13 | v, ok := g.Get("X-Request-Id") 14 | if !ok { 15 | return "" 16 | } 17 | if requestId, ok := v.(string); ok { 18 | return requestId 19 | } 20 | return "" 21 | } 22 | -------------------------------------------------------------------------------- /util/util.test: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChinaArJun/RESTfulGo/bf921f20e1994dbf2ff1d817fdc04d24c015cde1/util/util.test -------------------------------------------------------------------------------- /util/util_test.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import "testing" 4 | 5 | /* 6 | 测试单个模块 7 | 使用 go test 执行测试代码 8 | go test -v 显示执行的明细 9 | go test -v -n 、 go test -v -5 执行测试代码五次 10 | */ 11 | func TestGenShortId(t *testing.T) { 12 | shortId, err := GenShortId() 13 | if shortId == "" || err != nil { 14 | t.Error("genshortId failed!") 15 | } 16 | t.Log("genshortId test pass") 17 | } 18 | 19 | // 性能测试 20 | /* 21 | 说明: 22 | 性能测试函数名必须以 Benchmark 开头,如 BenchmarkXxx 或 Benchmark_xxx 23 | go test 默认不会执行压力测试函数,需要通过指定参数 -test.bench 来运行压力测试函数, 24 | -test.bench 后跟正则表达式,如 go test -test.bench=".*" 表示执行所有的压力测试函数 25 | 在压力测试中,需要在循环体中指定 testing.B.N 来循环执行压力测试代码 26 | 在 util 目录下执行命令 go test -test.bench=".*" 27 | 28 | 29 | ## 查看性能并生成函数调用图 30 | 执行命令: 31 | $ go test -bench=".*" -cpuprofile=cpu.profile ./util 32 | 上述命令会在当前目录下生成 cpu.profile 和 util.test 文件。 33 | 执行 go tool pprof util.test cpu.profile 查看性能(进入交互界面后执行 top 指令): top 34 | 35 | ## pprof 程序中最重要的命令就是 topN,此命令用于显示 profile 文件中的最靠前的 N 个样本(sample),它的输出格式各字段的含义依次是: 36 | 采样点落在该函数中的总时间 37 | 采样点落在该函数中的百分比 38 | 上一项的累积百分比 39 | 采样点落在该函数,以及被它调用的函数中的总时间 40 | 采样点落在该函数,以及被它调用的函数中的总次数百分比 41 | 函数名 42 | 43 | */ 44 | func BenchmarkGenShortId(b *testing.B) { 45 | for i := 0; i < b.N; i++ { 46 | GenShortId() 47 | } 48 | } 49 | 50 | func BenchmarkGenShortIdTimeConsuming(b *testing.B) { 51 | b.StopTimer() // 调用该函数停止压力测试的时间技术 52 | shortId, err := GenShortId() 53 | if shortId == "" || err != nil { 54 | b.Error() 55 | } 56 | b.StartTimer() // 重新开始时间 57 | 58 | for i := 0; i < b.N; i++ { 59 | GenShortId() 60 | } 61 | } 62 | --------------------------------------------------------------------------------