├── .gitignore ├── README.md ├── application.yaml ├── docs ├── images │ ├── arch.png │ ├── cloc.png │ └── github-follow.png ├── 开发文档.md ├── 抖音视频流与视频投稿接口说明.md ├── 数据库.md └── 核心代码注释.md ├── go.mod ├── go.sum ├── sql └── douyin.sql └── src ├── cache ├── redis.go └── redis_test.go ├── common └── common.go ├── config ├── app_config.go ├── cos_config.go └── cos_test.go ├── controller ├── comment.go ├── favorite.go ├── feed.go ├── publish.go ├── relation.go └── user.go ├── db ├── favorite_dao.go ├── feed_dao.go ├── feed_dao_test.go ├── gorm_mysql.go ├── mysql.go ├── mysql_connection_test.go └── user_dao.go ├── main ├── main.go └── main_test.go ├── service ├── action_service.go ├── favorite_service.go ├── feed_service.go ├── feed_service_test.go ├── list_service.go ├── user_service.go └── user_service_test.go └── utils └── util.go /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea 2 | /upload 3 | /ffmpeg.exe -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Tiktok ![](https://img.shields.io/badge/-mini-brightgreen) ![](https://img.shields.io/badge/go--version-1.16-green) ![](https://img.shields.io/badge/iris-12.2.0-orange) ![](https://img.shields.io/badge/viper-1.11.0-yellow) ![](https://img.shields.io/badge/-redis-red) 2 | 字节跳动第三届青训营,极简抖音后端项目 3 | 4 | 秉承着Go语言简洁的风格,不到1500行的代码,有大约10%的注释,麻雀虽小,五脏俱全。 5 | 6 | ![image-20220612152040764](docs/images/cloc.png) 7 | 8 | #### 已完成接口 9 | 10 | | 接口 | 说明 | 11 | | ---------- | -------------------------------------------------------- | 12 | | 基础接口 | 用户注册、用户登录、用户信息、视频流、投稿接口、发布列表 | 13 | | 拓展接口一 | 赞操作、点赞列表、评论操作、评论列表 | 14 | | 拓展接口二 | 关注操作、关注列表,粉丝列表 | 15 | 16 | #### 整体结构 17 | 18 | ![image-20220612150929391](docs/images/arch.png) 19 | 20 | #### 视频流和点赞 21 | [演示地址](http://124.223.112.154/demo/%E8%A7%86%E9%A2%91%E6%B5%81%E7%82%B9%E8%B5%9E.mp4) 22 | 23 | -------------------------------------------------------------------------------- /application.yaml: -------------------------------------------------------------------------------- 1 | datasource: 2 | driverName: mysql 3 | dataSourceName: sandaiyidui:qingxun@tcp(124.223.112.154:3306)/douyin 4 | redis: 5 | host: 124.223.112.154 6 | port: 6379 7 | password: 8 | server: 9 | port: 8080 10 | 11 | # 视频的存放地址 12 | video: 13 | videoUrl: https://hzbucket-1312272501.cos.ap-guangzhou.myqcloud.com/videos/ # 这个是获取视频流服务器的接口地址 14 | imageUrl: https://hzbucket-1312272501.cos.ap-guangzhou.myqcloud.com/pictures/ #服务器上传文件并没有这封面好像 15 | filePath: upload/ # 如果存放本机的话 这个就是服务器中视频存放的目录 16 | 17 | cos: 18 | SecretId: 19 | SecretKey: 20 | url: https://hzbucket-1312272501.cos.ap-guangzhou.myqcloud.com 21 | -------------------------------------------------------------------------------- /docs/images/arch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarybethGasman/douyin/925e4edd4a0c6c60833c038ca81579a730be0451/docs/images/arch.png -------------------------------------------------------------------------------- /docs/images/cloc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarybethGasman/douyin/925e4edd4a0c6c60833c038ca81579a730be0451/docs/images/cloc.png -------------------------------------------------------------------------------- /docs/images/github-follow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarybethGasman/douyin/925e4edd4a0c6c60833c038ca81579a730be0451/docs/images/github-follow.png -------------------------------------------------------------------------------- /docs/开发文档.md: -------------------------------------------------------------------------------- 1 | #### 本地开发环境 2 | 3 | 需要mysql 和 redis 版本不限 4 | 5 | ```shell 6 | #拉取tm分支 7 | git clone https://github.com/MarybethGasman/douyin.git 8 | 9 | #修改配置文件application.yaml,配置mysql和redis 10 | #改为你自己的数据库 11 | [用户名]:[密码]@tcp(127.0.0.1:3306)/douyin 12 | 确保mysql,redis启动且用户名密码端口正确 13 | 14 | #查看数据库文档,按照里面的建表语句建表 15 | #清除未使用的依赖并下载需要的依赖 16 | go mod tidy -go='1.16' 17 | #启动main.go 18 | ``` 19 | 20 | #### 接口任务分配 21 | 22 | 登录注册 谭盟 23 | 24 | 视频流接口和投稿接口 简懿豪 25 | 26 | 用户信息和粉丝列表 张博思 27 | 28 | 发布列表 29 | 30 | 赞操作和点赞列表 31 | 32 | 评论操作和评论列表 徐政 33 | 34 | 关注操作和关注列表 陈鹤中 35 | 36 | 37 | 38 | 39 | 40 | --------------------------------------------------------------分界线,下面不用看-------------------------------------------------------------- 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | #### 项目创建 49 | 50 | ```shell 51 | #初始化 52 | go mod init douyin 53 | #下载mysql驱动 54 | go get -u github.com/go-sql-driver/mysql 55 | #下载iris-go,高性能web框架 56 | go get github.com/kataras/iris/v12@v12.2.0-beta1 57 | #下载gorm 58 | #go get -u gorm.io/gorm 59 | #下载redis-client,用于缓存 60 | go get github.com/gomodule/redigo/redis 61 | #下载viper,用于配置管理(记得spring的application.yaml么😀) 62 | go get github.com/spf13/viper 63 | #下载go-jwt,用于token 64 | go get -u github.com/dgrijalva/jwt-go@v3.2.0 65 | #下载cos的sdk,用于对象存储 66 | go get -u github.com/tencentyun/cos-go-sdk-v5 67 | 68 | #清除未使用的依赖并自动下载需要的依赖 69 | go mod tidy -go='1.16' 70 | ``` 71 | 72 | #### 项目测试 73 | 74 | Goland直接启动不知道为什么偶尔会报no test were run,无大语 75 | 76 | ```shell 77 | #启动测试文件 78 | go test -v [.go文件] 79 | #启动某个测试方法 80 | go test [.go文件] -v -run [方法名] 81 | #实际使用建议加--cover,要求覆盖度达到90%,否则考虑修改测试代码 82 | go test -v --cover 83 | ``` 84 | 85 | #### go.mod 86 | 87 | ```go 88 | module douyin 89 | 90 | go 1.16 91 | 92 | require ( 93 | github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect 94 | github.com/go-redis/redis/v8 v8.11.5 // indirect 95 | github.com/go-sql-driver/mysql v1.6.0 96 | github.com/gomodule/redigo v1.8.8 // indirect 97 | github.com/kataras/iris/v12 v12.2.0-beta1 98 | github.com/spf13/viper v1.11.0 // indirect 99 | ) 100 | ``` 101 | 102 | 103 | 104 | #### 远程拉取测试 105 | 106 | ```shell 107 | #拉取tm分支 108 | git clone https://github.com/MarybethGasman/douyin.git -b tm 109 | 110 | #清除未使用的依赖并下载需要的依赖 111 | go mod tidy -go='1.16' 112 | 113 | #修改配置文件application.yaml中的dataSourceName 114 | #改为你自己的数据库 115 | [用户名]:[密码]@tcp(127.0.0.1:3306)/douyin 116 | 117 | #进入本地mysql,新建数据库 118 | create database douyin 119 | #建表 120 | create table tb_user ( 121 | id bigint primary key auto_increment, 122 | username varchar(40) default '', 123 | password varchar(40) default '' 124 | ) 125 | #启动main.go 126 | ``` 127 | 128 | 接口文档地址https://www.apifox.cn/apidoc/shared-8cc50618-0da6-4d5e-a398-76f3b8f766c5/api-18899952 129 | 130 | 使用API测试 工具按照接口文档进行测试,注意登陆注册接口的参数类型都是param 131 | 132 | 也就是/douyin/user/register/?username=xxx&password=xxx 133 | 134 | #### 部署相关 135 | 136 | ```shell 137 | #安装go环境 138 | wget https://go.dev/dl/go1.16.3.linux-amd64.tar.gz 139 | tar -xvf go1.16.3.linux-amd64.tar.gz 140 | #配置环境变量 141 | export PATH=$PATH:/home/tan/qingxun/go/bin 142 | source ~/.profile 143 | #配置代理 144 | export GO111MODULE=on 145 | export GOPROXY=mirrors.aliyun.com/goproxy/ 146 | #查看配置是否生效 147 | go env 148 | #恢复默认env 149 | go env -u 150 | #清除未使用的依赖并自动下载需要的依赖.go1.16.3没有-go 151 | go mod tidy 152 | #编译 153 | go build 154 | ``` 155 | 156 | -------------------------------------------------------------------------------- /docs/抖音视频流与视频投稿接口说明.md: -------------------------------------------------------------------------------- 1 | ## 视频流接口 2 | 3 | 我是三带一队的小组成员简懿豪,我负责了视频流获取接口和视频投稿接口. 4 | 5 | 接下来我将从代码方面讲述一下这两个接口的实现方式,首先从dao层开始然后是service层,最后是controller层,自下而上的进行讲述. 6 | 7 | ### dao层 8 | 9 | * 首先 在dao层 我采用了gorm框架进行对象与表映射关系,以下是我定义的对象结构 10 | 11 | 对应了两张表 tb_user 与 表 tb_video 并且将 user_id字段作为TbVideo表结构的外键映射,为了接下来获取视频的发布作者提供了关联 12 | 13 | ``` go 14 | type TbUser struct { 15 | UserId int64 `gorm:"primaryKey"` 16 | Name string `gorm:"default:"` 17 | FollowCount int64 `gorm:"default:"` 18 | FollowerCount int64 `gorm:"default:"` 19 | IsFollow int8 `gorm:"default:"` 20 | Password string `gorm:"default:"` 21 | } 22 | 23 | type TbVideo struct { 24 | VideoId int64 `gorm:"primaryKey"` 25 | UserId int64 26 | PlayUrl string `gorm:"default:"` 27 | CoverUrl string `gorm:"default:"` 28 | FavoriteCount int64 `gorm:"default:"` 29 | CommentCount int64 `gorm:"default:"` 30 | Title string `gorm:"default:"` 31 | CreateDate time.Time `gorm:"default:"` 32 | UpdateDate time.Time `gorm:"default:"` 33 | TbUser TbUser `gorm:"foreignKey:UserId;references:UserId"` 34 | } 35 | ``` 36 | 37 | 为了获取到视频是否点赞与关注,提供了另外的两张表的对象模型,代码如下 38 | 39 | ```go 40 | type TbRelation struct { 41 | RelationId int64 `gorm:"primaryKey"` 42 | FollowerId string `gorm:"default:"` 43 | FollowingId int64 `gorm:"default:"` 44 | Isdeleted int8 `gorm:"default:"` 45 | } 46 | type TbFavorite struct { 47 | FavoriteId int64 `gorm:"primaryKey"` 48 | UserId int64 `gorm:"default:"` 49 | VideoId int64 `gorm:"default:"` 50 | IsDeleted int8 `gorm:"default:"` 51 | } 52 | ``` 53 | 54 | * 定义完了结构体之后,我提供一个根据时间戳来获取视频的sql语句实现如下 55 | 56 | ``` go 57 | func (v *FeedDao) SelectVideoByUpdate(unix string, number int) ([]TbVideo, error) { 58 | var tb []TbVideo 59 | if len(unix) > 0 { 60 | // 时间戳转换为时间 61 | parseInt, err := strconv.ParseInt(unix, 10, 64) 62 | if err != nil { 63 | return nil, err 64 | } 65 | date := time.UnixMilli(parseInt).Format("2006-01-02 15:04:05") 66 | db.Preload("TbUser").Limit(number).Where("update_date < ?", date).Order("update_date DESC").Find(&tb) 67 | return tb, nil 68 | } 69 | db.Preload("TbUser").Limit(number).Order("update_date DESC").Find(&tb) 70 | return tb, nil 71 | } 72 | ``` 73 | 74 | 其中函数的参数 75 | 76 | 1. unix: 代表了传递进来的时间戳 77 | 2. number:代表了一共搜索多少条视频 78 | 79 | 返回值 80 | 81 | 1. []TbVideo:代表搜索到的视频数据的数组 82 | 2. error:表示错误 83 | 84 | 该sql主要是通过Preload预加载TbUser,然后根据更新时间排序,时间戳比较,limit限制条数,最后得到我们需要的视频数据 85 | 86 | * 还提供了两个sql实现是否有关注或者点赞视频 87 | 88 | ```go 89 | // 根据用户名和视频Id查询是否有点赞 90 | func (v *FeedDao) IsFavorite(userID int64, videoID int64) bool { 91 | var count int64 92 | db.Model(&TbFavorite{}).Where("`user_id` = ? and `video_id` = ? and `is_deleted` = 0", userID, videoID).Count(&count) 93 | return count > 0 94 | } 95 | // 根据用户名id查询是否有关注 true 表示关注 96 | func (v *FeedDao) IsFollwer(originUserID int64, targetUserID int64) bool { 97 | var size int64 98 | db.Model(&TbRelation{}).Where("`follower_id` = ? and `following_id` = ? and `isdeleted` = 0", originUserID, targetUserID).Count(&size) 99 | return size > 0 100 | } 101 | ``` 102 | 103 | 其中 点赞是通过当前登录用户的id与视频id进行判断, 104 | 105 | 关注是通过当前登录的用户id 与 视频作者的id进行判断的 106 | 107 | ### service层 108 | 109 | * 首先定义了一系列的APP所需要的返回数据与dao对象、 以及从配置文件中读取到的视频存放地址与图片存放地址 110 | 111 | ``` go 112 | var ( 113 | videoUrl = config.AppConfig.GetString("video.videoUrl") 114 | imageUrl = config.AppConfig.GetString("video.imageUrl") 115 | dao = &db.FeedDao{} 116 | ) 117 | 118 | type FeedData struct { 119 | StatusCode int `json:"status_code"` 120 | StatusMsg string `json:"status_msg"` 121 | NextTime int64 `json:"next_time"` 122 | VideoList []VideoList `json:"video_list"` 123 | } 124 | 125 | type Author struct { 126 | ID int64 `json:"id"` 127 | Name string `json:"name"` 128 | FollowCount int64 `json:"follow_count"` 129 | FollowerCount int64 `json:"follower_count"` 130 | IsFollow bool `json:"is_follow"` 131 | } 132 | 133 | type VideoList struct { 134 | ID int64 `json:"id"` 135 | Author Author `json:"author"` 136 | PlayUrl string `json:"play_url"` 137 | CoverUrl string `json:"cover_url"` 138 | FavoriteCount int64 `json:"favorite_count"` 139 | CommentCount int64 `json:"comment_count"` 140 | IsFavorite bool `json:"is_favorite"` 141 | Title string `json:"title"` 142 | } 143 | ``` 144 | 145 | 然后提供一个获取视频流结果的函数,函数具体实现代码如下 146 | 147 | ```go 148 | func GetFeed(latestTime string, token string) *FeedData { 149 | data := &FeedData{} 150 | var userId int64 151 | userId = -1 152 | // 通过dao接口的获取视频函数 获取到符合时间戳的30条视频 153 | videos, err := dao.SelectVideoByUpdate(latestTime, 30) 154 | if err != nil { 155 | return &FeedData{ 156 | StatusCode: 1, 157 | StatusMsg: "查找视频失败,err: " + err.Error(), 158 | } 159 | } 160 | 161 | if len(token) > 0 { 162 | get := cache.RCGet(token) 163 | userId, err = get.Int64() 164 | } 165 | // 遍历获取视频数组 封装返回的数据 166 | for _, v := range videos { 167 | data.VideoList = append(data.VideoList, VideoList{ 168 | ID: v.VideoId, 169 | Author: Author{ 170 | ID: v.TbUser.UserId, 171 | Name: v.TbUser.Name, 172 | FollowCount: v.TbUser.FollowCount, 173 | FollowerCount: v.TbUser.FollowerCount, 174 | IsFollow: isFollow(userId, v.TbUser.UserId), 175 | }, 176 | // url 是通过配置文件中读取到的路径 与 文件名称拼接的 177 | PlayUrl: getVideoUrl(v.PlayUrl), 178 | CoverUrl: getImageUrl(v.CoverUrl), 179 | FavoriteCount: v.FavoriteCount, 180 | CommentCount: v.CommentCount, 181 | Title: v.Title, 182 | IsFavorite: isFavorite(userId, v.VideoId), 183 | }) 184 | } 185 | // 已经没有视频了 从头继续播放 186 | if len(videos) == 0 { 187 | data.NextTime = time.Now().UnixMilli() 188 | } else { 189 | data.NextTime = videos[len(videos)-1].UpdateDate.UnixMilli() 190 | } 191 | 192 | data.StatusCode = 0 193 | return data 194 | } 195 | ``` 196 | 197 | ### controller层 198 | 199 | * 控制层的实现很简单,只是通过调用service的函数即可实现. 200 | 201 | ```go 202 | // 使用了iris框架 使用了Get函数名 对应了Get的该路由请求 /douyin/feed 203 | func (fc *FeedController) Get(ctx iris.Context) { 204 | // 从请求中取出对应的参数 205 | ctx.JSON(service.GetFeed(ctx.URLParam("latest_time"), ctx.URLParam("token"))) 206 | } 207 | ``` 208 | 209 | ## 视频投稿接口 210 | 211 | 视频投稿接口的讲述,我也通过上面那种方式进行讲述,其中封面的获取,我通过参考了掘金社区同学的[文章](https://juejin.cn/post/7099827417170051103),通过ffmpeg进行视频截取图片,学到了很多 212 | 213 | ### dao层 214 | 215 | * 首先视频投稿还是对视频表的操作 tb_video,结构还是延用了上面视频流接口的视频表结构.这里就不再展示结构体代码了 216 | 217 | * 由于使用了gorm框架 实现插入数据真的简单 代码如下 218 | 219 | ``` go 220 | func (v *FeedDao) InsertVideo(table *TbVideo) bool { 221 | return db.Create(table) 222 | } 223 | ``` 224 | 225 | ### service层 226 | 227 | * 首先 我在配置文件中定义了存储的路径 下面是该函数所用到的路径变量 228 | 229 | ```go 230 | var ( 231 | FilePath = config.AppConfig.GetString("video.filePath") 232 | ) 233 | ``` 234 | 235 | * 在service中 提供了一个函数 实现了视频投稿的功能 236 | 237 | ``` go 238 | // Contribution 视频投稿 239 | func Contribution(ctx iris.Context) { 240 | r := ctx.Request() 241 | 242 | text := r.FormValue("token") 243 | 244 | // 鉴权 通过redis实现 245 | rcGet := cache.RCGet(text) 246 | if rcGet == nil { 247 | ctx.JSON(map[string]interface{}{ 248 | "status_code": 4, 249 | "status_msg": "该用户没用权限", 250 | }) 251 | return 252 | } 253 | // 获取到APP传过来的标题 254 | title := r.FormValue("title") 255 | // 获取到APP传过来的文件数据 256 | file, head, err := r.FormFile("data") 257 | if err != nil { 258 | ctx.JSON(map[string]interface{}{ 259 | "status_code": 1, 260 | "status_msg": "没有找到data参数,err: " + err.Error(), 261 | }) 262 | return 263 | } 264 | defer file.Close() 265 | // 先判断一下 该路径是否存在文件夹 如果没有就直接创建相关文件夹 266 | if b, _ := isHasDir(FilePath); !b { 267 | err = os.MkdirAll(FilePath, 0777) 268 | if err != nil { 269 | ctx.JSON(map[string]interface{}{ 270 | "status_code": 5, 271 | "status_msg": "create folder failed,err: " + err.Error(), 272 | }) 273 | return 274 | } 275 | } 276 | // 文件名通过获取到当前的纳秒级时间戳 和文件名进行拼接 保证了文件不重复 277 | fileName := strconv.FormatInt(time.Now().UnixNano(), 10) + head.Filename 278 | 279 | fw, err := os.Create(FilePath + fileName) 280 | 281 | if err != nil { 282 | ctx.JSON(map[string]interface{}{ 283 | "status_code": 2, 284 | "status_msg": "create file failed,err: " + err.Error(), 285 | }) 286 | return 287 | } 288 | defer fw.Close() 289 | // 进行视频的存储 290 | _, err = io.Copy(fw, file) 291 | if err != nil { 292 | ctx.JSON(map[string]interface{}{ 293 | "status_code": 3, 294 | "status_msg": "copy file failed,err: " + err.Error(), 295 | }) 296 | return 297 | } 298 | ctx.JSON(map[string]interface{}{ 299 | "status_code": 0, 300 | "status_msg": "save file success", 301 | }) 302 | 303 | // 保存封面 封面的文件名则与视频的名称相同 后缀 .mp4 更换为 .jpeg 304 | fileImage := GetSnapshot(FilePath+fileName, FilePath+strings.Trim(fileName, ".mp4"), 60) 305 | 306 | // 将信息插入到数据库 307 | dao := &db.FeedDao{} 308 | id, err := rcGet.Int64() 309 | if err != nil { 310 | panic("get id failed,err: " + err.Error()) 311 | } 312 | // 调用dao层接口存储信息 313 | dao.InsertVideo(&db.TbVideo{ 314 | UserId: id, 315 | PlayUrl: fileName, 316 | CoverUrl: fileImage, 317 | Title: title, 318 | }) 319 | } 320 | // 判断是否有该文件夹 321 | func isHasDir(path string) (bool, error) { 322 | stat, err := os.Stat(path) 323 | if err != nil { 324 | return false, err 325 | } 326 | return stat.IsDir(), nil 327 | } 328 | 329 | /** 330 | 视频的路径 videoPath 331 | 生成的缩略图保存的路径 snapshotPath 332 | 以及缩略图所属的帧数 frameNum 333 | 返回值则是缩略图的文件名称: snapshotName 334 | */ 335 | func GetSnapshot(videoPath, snapshotPath string, frameNum int) (snapshotName string) { 336 | buf := bytes.NewBuffer(nil) 337 | err := ffmpeg_go.Input(videoPath). 338 | Filter("select", ffmpeg_go.Args{fmt.Sprintf("gte(n,%d)", frameNum)}). 339 | Output("pipe:", ffmpeg_go.KwArgs{"vframes": 1, "format": "image2", "vcodec": "mjpeg"}). 340 | WithOutput(buf, os.Stdout). 341 | Run() 342 | if err != nil { 343 | log.Fatal("生成缩略图失败:", err) 344 | } 345 | 346 | img, err := imaging.Decode(buf) 347 | if err != nil { 348 | log.Fatal("生成缩略图失败:", err) 349 | } 350 | 351 | err = imaging.Save(img, snapshotPath+".jpeg") 352 | if err != nil { 353 | log.Fatal("生成缩略图失败:", err) 354 | } 355 | 356 | // 成功则返回生成的缩略图名 357 | names := strings.Split(snapshotPath, "/") 358 | snapshotName = names[len(names)-1] + ".jpeg" 359 | return 360 | } 361 | ``` 362 | 363 | ### controller层 364 | 365 | * controller 也是非常简单的调用一下service接口就行啦 366 | 367 | ```go 368 | // PostAction 对应了 /douyin/publish/action 的POST请求 369 | func (pc *PublishController) PostAction(ctx iris.Context) { 370 | service.Contribution(ctx) 371 | } 372 | ``` 373 | 374 | 自此,视频投稿接口完毕。 -------------------------------------------------------------------------------- /docs/数据库.md: -------------------------------------------------------------------------------- 1 | #### 数据类型选择 2 | 3 | 尽量使用满足需求的最小类型 4 | 5 | 如果mysql有对应的类型选择对应的类型,比如日期用date,timestamp 而不是用varchar 6 | 7 | 尽量避免null,字符串类型默认''" 8 | 9 | - 整数 有tiny small medium int 分别是8 16 24 32 64字节,选择满足需求的最小类型 10 | 11 | - 字符串 char varchar text blob,一般用varchar,可用于存储文章这种长度波动较大的内容,MD5和摘要这种东西可以用char固定长度存,而blob和text很少使用 12 | - 时间 datetime,timestamp,date三个类型,datetime占用8个字节,时区无关,可保存到毫秒,可保存时间范围大(到9999年),timestamp占用4个字节,到2038-01-19,精确到秒,整形存储,依赖数据库时区,可自动更新,date占用3个字节,保存的日期范围和datetime一致,timestamp和date使用较多,如果需要高精确度,使用datetime 13 | 14 | #### 数据表结构定义 15 | 16 | tb_user用户表 17 | 18 | ```sql 19 | DROP TABLE IF EXISTS `tb_user`; 20 | CREATE TABLE `tb_user` ( 21 | `user_id` bigint(20) NOT NULL AUTO_INCREMENT, 22 | `name` varchar(40) DEFAULT '', 23 | `follow_count` int(11) DEFAULT '0', 24 | `follower_count` int(11) DEFAULT '0', 25 | `password` char(40) DEFAULT '', 26 | PRIMARY KEY (`user_id`), 27 | UNIQUE KEY `username` (`name`(20)) 28 | ) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8 29 | //username建唯一索引或者前缀索引 30 | ``` 31 | 32 | tb_video视频表 33 | 34 | ```sql 35 | DROP TABLE IF EXISTS `tb_video`; 36 | CREATE TABLE `tb_video` ( 37 | `video_id` bigint(20) NOT NULL AUTO_INCREMENT, 38 | `user_id` bigint(20) DEFAULT NULL, 39 | `play_url` varchar(60) CHARACTER SET utf8 DEFAULT '', 40 | `cover_url` varchar(60) CHARACTER SET utf8 DEFAULT '', 41 | `favorite_count` int(11) DEFAULT '0', 42 | `comment_count` int(11) DEFAULT '0', 43 | `title` text CHARACTER SET utf8 COMMENT '视频标题', 44 | `create_date` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', 45 | `update_date` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', 46 | PRIMARY KEY (`video_id`) 47 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 48 | //考虑create_time建个索引 49 | ``` 50 | 51 | tb_favorite点赞表 52 | 53 | ```sql 54 | DROP TABLE IF EXISTS `tb_favorite`; 55 | CREATE TABLE `tb_favorite` ( 56 | `favorite_id` bigint(20) NOT NULL AUTO_INCREMENT, 57 | `user_id` bigint(20) DEFAULT '0', 58 | `video_id` bigint(20) DEFAULT '0', 59 | `is_deleted` tinyint(4) DEFAULT '0', 60 | PRIMARY KEY (`favorite_id`), 61 | KEY `favorite_user_id` (`user_id`) 62 | ) ENGINE=InnoDB AUTO_INCREMENT=25 DEFAULT CHARSET=utf8 63 | //user_id建个索引 64 | ``` 65 | 66 | tb_comment 评论表 67 | 68 | ```sql 69 | DROP TABLE IF EXISTS `tb_comment`; 70 | //user_id和video_id分别建两个索引 71 | CREATE TABLE `tb_comment` ( 72 | `comment_id` bigint(20) NOT NULL AUTO_INCREMENT, 73 | `user_id` bigint(20) DEFAULT '0', 74 | `video_id` bigint(20) DEFAULT '0', 75 | `content` varchar(40) DEFAULT '', 76 | `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', 77 | PRIMARY KEY (`comment_id`), 78 | KEY `create_time` (`create_time`), 79 | KEY `video_id` (`video_id`) 80 | ) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8 81 | ``` 82 | 83 | tb_relation 关系表 84 | 85 | ```sql 86 | DROP TABLE IF EXISTS `tb_relation`; 87 | //follower_id和following_id联合索引 88 | //following_id 单独建个索引 89 | CREATE TABLE `tb_relation` ( 90 | `relation_id` bigint(20) NOT NULL AUTO_INCREMENT, 91 | `follower_id` bigint(20) DEFAULT '0', 92 | `following_id` bigint(20) DEFAULT '0', 93 | `isdeleted` tinyint(4) DEFAULT '0', 94 | PRIMARY KEY (`relation_id`), 95 | KEY `follower` (`follower_id`,`following_id`), 96 | KEY `following` (`following_id`) 97 | ) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8 98 | ``` 99 | 100 | #### 范式与反范式 101 | 102 | 范式数据冗余少,更新速度快, 103 | 104 | 反范式减少联查,更新数据不用多处更新 105 | -------------------------------------------------------------------------------- /docs/核心代码注释.md: -------------------------------------------------------------------------------- 1 | #### main/main.go 2 | 3 | ```go 4 | func newApp() *iris.Application { 5 | //新建一个iris application 6 | app := iris.New() 7 | //将路径/douyin/user映射给UserController 8 | //这个路径的请求会进入UserController 9 | mvc.Configure(app.Party("/douyin/user"), func(app *mvc.Application) { 10 | app.Handle(new(UserController)) 11 | }) 12 | //返回app对象 13 | return app 14 | } 15 | func main() { 16 | //读取配置文件,获取项目监听端口号 17 | addr := strconv.Itoa(AppConfig.Get("server.port").(int)) 18 | app := newApp() 19 | //启动服务,监听对应端口 20 | app.Run(iris.Addr(":"+addr), iris.WithCharset("UTF-8")) 21 | } 22 | ``` 23 | 24 | #### db/mysql.go 25 | 26 | ```go 27 | //全局变量,首字母大写,用于暴露给外部调用其方法 28 | var DB *sql.DB 29 | //该方法会在第一次import导入时执行,先于main方法(如果有),一般用于初始化 30 | func init() { 31 | //读取配置文件 32 | driverName := AppConfig.Get("datasource.driverName").(string) 33 | dataSourceName := AppConfig.Get("datasource.dataSourceName").(string) 34 | //打印日志 35 | log.Printf("数据库为 %s, 数据库链接为%s", driverName, dataSourceName) 36 | //Open一下,此时并不会建立连接 37 | db, err := sql.Open(driverName, dataSourceName) 38 | if err != nil { 39 | panic(err) 40 | } 41 | //测试链接,如果失败则抛异常 42 | if db.Ping() != nil { 43 | panic("数据库连接错误") 44 | } 45 | //初始化全局变量供外部使用 46 | DB = db 47 | } 48 | ``` 49 | 50 | #### controller/user.go 51 | 52 | ```go 53 | //userController路径是在main方法里设置的 54 | type UserController struct { 55 | } 56 | //处理的请求为 57 | //POST [userController路径]/register 58 | func (uc *UserController) PostRegister(ctx iris.Context) mvc.Result { 59 | //mvc.Response里面有个Object变量,类型为interface{},需要返回的内容写入Object里就行 60 | //当成springmvc里面的restcontroller用就行 61 | //推荐返回map,不建议随意定义对象 62 | return mvc.Response{ 63 | Object: map[string]interface{}{ 64 | "token": "", 65 | "user_id": 1, 66 | "status_msg": "OK", 67 | "status_code": 0, 68 | }, 69 | } 70 | } 71 | //处理的请求为 72 | //POST [userController路径]/login 73 | func (uc *UserController) PostLogin(ctx iris.Context) mvc.Result { 74 | } 75 | ``` 76 | 77 | #### config/app_config.go 78 | 79 | ```go 80 | //暴露给外部使用的全局变量,该变量可用于读取application.yml内容 81 | var AppConfig = viper.New() 82 | func init() { 83 | AppConfig.AddConfigPath(".") //设置读取的文件路径 84 | AppConfig.SetConfigName("application") //设置读取的文件名 85 | AppConfig.SetConfigType("yaml") //设置文件的类型 86 | //尝试进行配置读取 87 | if err := AppConfig.ReadInConfig(); err != nil { 88 | panic(err) 89 | } 90 | } 91 | ``` 92 | 93 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module douyin 2 | 3 | go 1.16 4 | 5 | require ( 6 | github.com/aws/aws-sdk-go v1.44.31 // indirect 7 | github.com/disintegration/imaging v1.6.2 8 | github.com/go-redis/redis/v8 v8.11.5 9 | github.com/go-sql-driver/mysql v1.6.0 10 | github.com/kataras/iris/v12 v12.2.0-beta1 11 | github.com/spf13/viper v1.11.0 12 | github.com/tencentyun/cos-go-sdk-v5 v0.7.35 13 | github.com/u2takey/ffmpeg-go v0.4.1 14 | golang.org/x/image v0.0.0-20220601225756-64ec528b34cd // indirect 15 | gorm.io/driver/mysql v1.3.3 16 | gorm.io/gorm v1.23.5 17 | ) 18 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 3 | cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= 4 | cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= 5 | cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= 6 | cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= 7 | cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= 8 | cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= 9 | cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= 10 | cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= 11 | cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= 12 | cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= 13 | cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= 14 | cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= 15 | cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= 16 | cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= 17 | cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= 18 | cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= 19 | cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= 20 | cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= 21 | cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= 22 | cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= 23 | cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= 24 | cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM= 25 | cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY= 26 | cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= 27 | cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= 28 | cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= 29 | cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= 30 | cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA= 31 | cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A= 32 | cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= 33 | cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= 34 | cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= 35 | cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= 36 | cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= 37 | cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= 38 | cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow= 39 | cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM= 40 | cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M= 41 | cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= 42 | cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= 43 | cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= 44 | cloud.google.com/go/firestore v1.6.1/go.mod h1:asNXNOzBdyVQmEU+ggO8UPodTkEVFW5Qx+rwHnAz+EY= 45 | cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= 46 | cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= 47 | cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= 48 | cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= 49 | cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= 50 | cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= 51 | cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= 52 | cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= 53 | cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= 54 | cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= 55 | dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= 56 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 57 | github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I= 58 | github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= 59 | github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= 60 | github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 h1:sR+/8Yb4slttB4vD+b9btVEnWgL3Q00OBTzVT8B9C0c= 61 | github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno= 62 | github.com/CloudyKit/jet/v6 v6.1.0 h1:hvO96X345XagdH1fAoBjpBYG4a1ghhL/QzalkduPuXk= 63 | github.com/CloudyKit/jet/v6 v6.1.0/go.mod h1:d3ypHeIRNo2+XyqnGA8s+aphtcVpjP5hPwP/Lzo7Ro4= 64 | github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= 65 | github.com/Joker/hpp v1.0.0 h1:65+iuJYdRXv/XyN62C1uEmmOx3432rNG/rKlX6V7Kkc= 66 | github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY= 67 | github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= 68 | github.com/QcloudApi/qcloud_sign_golang v0.0.0-20141224014652-e4130a326409/go.mod h1:1pk82RBxDY/JZnPQrtqHlUFfCctgdorsd9M06fMynOM= 69 | github.com/Shopify/goreferrer v0.0.0-20210630161223-536fa16abd6f h1:XeOBnoBP7K19tMBEKeUo1NOxOO+h5FFi2HGzQvvkb44= 70 | github.com/Shopify/goreferrer v0.0.0-20210630161223-536fa16abd6f/go.mod h1:a1uqRtAwp2Xwc6WNPJEufxJ7fx3npB4UV/JOLmbu5I0= 71 | github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU= 72 | github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= 73 | github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= 74 | github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= 75 | github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= 76 | github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= 77 | github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY= 78 | github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= 79 | github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= 80 | github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= 81 | github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= 82 | github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= 83 | github.com/armon/go-metrics v0.3.10/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc= 84 | github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= 85 | github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= 86 | github.com/aws/aws-sdk-go v1.38.20/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= 87 | github.com/aws/aws-sdk-go v1.44.31 h1:nr3c7pbJbFslL+U7JrM7knBxzksnK52qIZoYX8uk2I4= 88 | github.com/aws/aws-sdk-go v1.44.31/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= 89 | github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= 90 | github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= 91 | github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= 92 | github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= 93 | github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= 94 | github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= 95 | github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= 96 | github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= 97 | github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= 98 | github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 99 | github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= 100 | github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= 101 | github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 102 | github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= 103 | github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 104 | github.com/cheekybits/is v0.0.0-20150225183255-68e9c0620927/go.mod h1:h/aW8ynjgkuj+NQRlZcDbAbM1ORAbXjXX77sX7T289U= 105 | github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= 106 | github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= 107 | github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= 108 | github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= 109 | github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= 110 | github.com/clbanning/mxj v1.8.4 h1:HuhwZtbyvyOw+3Z1AowPkU87JkJUSv751ELWaiTpj8I= 111 | github.com/clbanning/mxj v1.8.4/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng= 112 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 113 | github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= 114 | github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= 115 | github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= 116 | github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= 117 | github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= 118 | github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= 119 | github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= 120 | github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= 121 | github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= 122 | github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= 123 | github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= 124 | github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= 125 | github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= 126 | github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= 127 | github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= 128 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 129 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 130 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 131 | github.com/dgraph-io/badger/v2 v2.2007.4/go.mod h1:vSw/ax2qojzbN6eXHIx6KPKtCSHJN/Uz0X0VPruTIhk= 132 | github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= 133 | github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= 134 | github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= 135 | github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= 136 | github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c= 137 | github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4= 138 | github.com/djherbis/atime v1.1.0/go.mod h1:28OF6Y8s3NQWwacXc5eZTsEsiMzp7LF8MbXE+XJPdBE= 139 | github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= 140 | github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385 h1:clC1lXBpe2kTj2VHdaIu9ajZQe4kcEY9j0NsnDDBZ3o= 141 | github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM= 142 | github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 143 | github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 144 | github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= 145 | github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= 146 | github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= 147 | github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= 148 | github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= 149 | github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= 150 | github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= 151 | github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= 152 | github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= 153 | github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= 154 | github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= 155 | github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= 156 | github.com/flosch/pongo2/v4 v4.0.2 h1:gv+5Pe3vaSVmiJvh/BZa82b7/00YUGm0PIyVVLop0Hw= 157 | github.com/flosch/pongo2/v4 v4.0.2/go.mod h1:B5ObFANs/36VwxxlgKpdchIJHMvHB562PW+BWPhwZD8= 158 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 159 | github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= 160 | github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI= 161 | github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= 162 | github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= 163 | github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= 164 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 165 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 166 | github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= 167 | github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= 168 | github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= 169 | github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= 170 | github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= 171 | github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= 172 | github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI= 173 | github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= 174 | github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= 175 | github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= 176 | github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= 177 | github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= 178 | github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU= 179 | github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM= 180 | github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og= 181 | github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= 182 | github.com/gobwas/ws v1.1.0 h1:7RFti/xnNkMJnrK7D1yQ/iCIB5OrrY/54/H930kIbHA= 183 | github.com/gobwas/ws v1.1.0/go.mod h1:nzvNcVha5eUziGrbxFCo6qFIojQHjJV5cLYIbezhfL0= 184 | github.com/goccy/go-json v0.9.7-0.20220412154129-171d97575378 h1:eIZ4l5hJq4PBURyWol+fDlr2dFNFYIIvePwkAnk3jws= 185 | github.com/goccy/go-json v0.9.7-0.20220412154129-171d97575378/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= 186 | github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= 187 | github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= 188 | github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= 189 | github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= 190 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 191 | github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 192 | github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 193 | github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 194 | github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 195 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 196 | github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 197 | github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= 198 | github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 199 | github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 200 | github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 201 | github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= 202 | github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= 203 | github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= 204 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 205 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 206 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 207 | github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 208 | github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 209 | github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= 210 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= 211 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= 212 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= 213 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 214 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= 215 | github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= 216 | github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 217 | github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 218 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 219 | github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= 220 | github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= 221 | github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 222 | github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 223 | github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= 224 | github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 225 | github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 226 | github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 227 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 228 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 229 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 230 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 231 | github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 232 | github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 233 | github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 234 | github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 235 | github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 236 | github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 237 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 238 | github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 239 | github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o= 240 | github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= 241 | github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= 242 | github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= 243 | github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= 244 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 245 | github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= 246 | github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= 247 | github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= 248 | github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= 249 | github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 250 | github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 251 | github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 252 | github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 253 | github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 254 | github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 255 | github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 256 | github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 257 | github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 258 | github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 259 | github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 260 | github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 261 | github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 262 | github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 263 | github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 264 | github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 265 | github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= 266 | github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 267 | github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 268 | github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= 269 | github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 270 | github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= 271 | github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= 272 | github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= 273 | github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= 274 | github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM= 275 | github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99EXz9pXxye9YM= 276 | github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= 277 | github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= 278 | github.com/gopherjs/gopherjs v0.0.0-20220410123724-9e86199038b0 h1:fWY+zXdWhvWndXqnMj4SyC/vi8sK508OjhGCtMzsA9M= 279 | github.com/gopherjs/gopherjs v0.0.0-20220410123724-9e86199038b0/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k= 280 | github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY= 281 | github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c= 282 | github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= 283 | github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= 284 | github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= 285 | github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= 286 | github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= 287 | github.com/hashicorp/consul/api v1.12.0/go.mod h1:6pVBMo0ebnYdt2S3H87XhekM/HHrUoTD2XXb/VrZVy0= 288 | github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= 289 | github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms= 290 | github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= 291 | github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= 292 | github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= 293 | github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= 294 | github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= 295 | github.com/hashicorp/go-hclog v1.2.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= 296 | github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= 297 | github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= 298 | github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= 299 | github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= 300 | github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= 301 | github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= 302 | github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= 303 | github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= 304 | github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= 305 | github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= 306 | github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= 307 | github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= 308 | github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= 309 | github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 310 | github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 311 | github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= 312 | github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= 313 | github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= 314 | github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= 315 | github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= 316 | github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc= 317 | github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= 318 | github.com/hashicorp/memberlist v0.3.0/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= 319 | github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= 320 | github.com/hashicorp/serf v0.9.6/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4= 321 | github.com/hashicorp/serf v0.9.7/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4= 322 | github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= 323 | github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= 324 | github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= 325 | github.com/imkira/go-interpol v1.1.0 h1:KIiKr0VSG2CUW1hl1jpiyuzuJeKUUpC8iM1AIE7N1Vk= 326 | github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA= 327 | github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= 328 | github.com/iris-contrib/go.uuid v2.0.0+incompatible h1:XZubAYg61/JwnJNbZilGjf3b3pB80+OQg2qf6c8BfWE= 329 | github.com/iris-contrib/go.uuid v2.0.0+incompatible/go.mod h1:iz2lgM/1UnEf1kP0L/+fafWORmlnuysV2EMP8MW+qe0= 330 | github.com/iris-contrib/httpexpect/v2 v2.3.1 h1:A69ilxKGW1jDRKK5UAhjTL4uJYh3RjD4qzt9vNZ7fpY= 331 | github.com/iris-contrib/httpexpect/v2 v2.3.1/go.mod h1:ICTf89VBKSD3KB0fsyyHviKF8G8hyepP0dOXJPWz3T0= 332 | github.com/iris-contrib/jade v1.1.4 h1:WoYdfyJFfZIUgqNAeOyRfTNQZOksSlZ6+FnXR3AEpX0= 333 | github.com/iris-contrib/jade v1.1.4/go.mod h1:EDqR+ur9piDl6DUgs6qRrlfzmlx/D5UybogqrXvJTBE= 334 | github.com/iris-contrib/schema v0.0.6 h1:CPSBLyx2e91H2yJzPuhGuifVRnZBBJ3pCOMbOvPZaTw= 335 | github.com/iris-contrib/schema v0.0.6/go.mod h1:iYszG0IOsuIsfzjymw1kMzTL8YQcCWlm65f3wX8J5iA= 336 | github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= 337 | github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= 338 | github.com/jinzhu/now v1.1.4 h1:tHnRBy1i5F2Dh8BAFxqFzxKqqvezXrL2OW1TnX+Mlas= 339 | github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= 340 | github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= 341 | github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= 342 | github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= 343 | github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= 344 | github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= 345 | github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= 346 | github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= 347 | github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 348 | github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 349 | github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 350 | github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= 351 | github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= 352 | github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= 353 | github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= 354 | github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= 355 | github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= 356 | github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= 357 | github.com/kataras/blocks v0.0.5 h1:jFrsHEDfXZhHTbhkNWgMgpfEQNj1Bwr1IYEYZ9Xxoxg= 358 | github.com/kataras/blocks v0.0.5/go.mod h1:kcJIuvuA8QmGKFLHIZHdCAPCjcE85IhttzXd6W+ayfE= 359 | github.com/kataras/golog v0.1.7 h1:0TY5tHn5L5DlRIikepcaRR/6oInIr9AiWsxzt0vvlBE= 360 | github.com/kataras/golog v0.1.7/go.mod h1:jOSQ+C5fUqsNSwurB/oAHq1IFSb0KI3l6GMa7xB6dZA= 361 | github.com/kataras/iris/v12 v12.2.0-beta1 h1:lUPQXw7mS+AndRTPBrYQAON6z8XYRRiKUMaFOC/DBZ4= 362 | github.com/kataras/iris/v12 v12.2.0-beta1/go.mod h1:o5GWxxzNPQqPnry6EBshg+ySva6au/X3IUHvdAYea+U= 363 | github.com/kataras/jwt v0.1.8/go.mod h1:Q5j2IkcIHnfwy+oNY3TVWuEBJNw0ADgCcXK9CaZwV4o= 364 | github.com/kataras/neffos v0.0.19 h1:j3jp/hzvGFQjnkkLWGNjae5qMSdpMYr66Lxgf8CgcAw= 365 | github.com/kataras/neffos v0.0.19/go.mod h1:CAAuFqHYX5t0//LLMiVWooOSp5FPeBRD8cn/892P1JE= 366 | github.com/kataras/pio v0.0.10 h1:b0qtPUqOpM2O+bqa5wr2O6dN4cQNwSmFd6HQqgVae0g= 367 | github.com/kataras/pio v0.0.10/go.mod h1:gS3ui9xSD+lAUpbYnjOGiQyY7sUMJO+EHpiRzhtZ5no= 368 | github.com/kataras/sitemap v0.0.5 h1:4HCONX5RLgVy6G4RkYOV3vKNcma9p236LdGOipJsaFE= 369 | github.com/kataras/sitemap v0.0.5/go.mod h1:KY2eugMKiPwsJgx7+U103YZehfvNGOXURubcGyk0Bz8= 370 | github.com/kataras/tunnel v0.0.3 h1:+8eHXujPD3wLnqTbYtPGa/3/Jc+Eq+bsPwEGTeFBB00= 371 | github.com/kataras/tunnel v0.0.3/go.mod h1:VOlCoaUE5zN1buE+yAjWCkjfQ9hxGuhomKLsjei/5Zs= 372 | github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= 373 | github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= 374 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 375 | github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= 376 | github.com/klauspost/compress v1.14.4/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= 377 | github.com/klauspost/compress v1.15.1 h1:y9FcTHGyrebwfP0ZZqFiaxTaiDnUrGkJkI+f583BL1A= 378 | github.com/klauspost/compress v1.15.1/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= 379 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 380 | github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= 381 | github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= 382 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 383 | github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= 384 | github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= 385 | github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= 386 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 387 | github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= 388 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 389 | github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= 390 | github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= 391 | github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= 392 | github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo= 393 | github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= 394 | github.com/mailgun/raymond/v2 v2.0.46 h1:aOYHhvTpF5USySJ0o7cpPno/Uh2I5qg2115K25A+Ft4= 395 | github.com/mailgun/raymond/v2 v2.0.46/go.mod h1:lsgvL50kgt1ylcFJYZiULi5fjPBkkhNfj4KA0W54Z18= 396 | github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= 397 | github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= 398 | github.com/matryer/try v0.0.0-20161228173917-9ac251b645a2/go.mod h1:0KeJpeMD6o+O4hW7qJOT7vyQPKrWmj26uf5wMc/IiIs= 399 | github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= 400 | github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= 401 | github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= 402 | github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= 403 | github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= 404 | github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= 405 | github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= 406 | github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= 407 | github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= 408 | github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= 409 | github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= 410 | github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= 411 | github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= 412 | github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= 413 | github.com/mediocregopher/radix/v3 v3.8.0 h1:HI8EgkaM7WzsrFpYAkOXIgUKbjNonb2Ne7K6Le61Pmg= 414 | github.com/mediocregopher/radix/v3 v3.8.0/go.mod h1:8FL3F6UQRXHXIBSPUs5h0RybMF8i4n7wVopoX3x7Bv8= 415 | github.com/microcosm-cc/bluemonday v1.0.18 h1:6HcxvXDAi3ARt3slx6nTesbvorIc3QeTzBNRvWktHBo= 416 | github.com/microcosm-cc/bluemonday v1.0.18/go.mod h1:Z0r70sCuXHig8YpBzCc5eGHAap2K7e/u082ZUpDRRqM= 417 | github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= 418 | github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= 419 | github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= 420 | github.com/minio/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA/g= 421 | github.com/minio/highwayhash v1.0.2/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY= 422 | github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= 423 | github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= 424 | github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 425 | github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 426 | github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= 427 | github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= 428 | github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= 429 | github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 430 | github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 431 | github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= 432 | github.com/mitchellh/mapstructure v1.4.3 h1:OVowDSCllw/YjdLkam3/sm7wEtOy59d8ndGgCcyj8cs= 433 | github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= 434 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 435 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= 436 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 437 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 438 | github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 439 | github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= 440 | github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= 441 | github.com/mozillazg/go-httpheader v0.2.1 h1:geV7TrjbL8KXSyvghnFm+NyTux/hxwueTSrwhe88TQQ= 442 | github.com/mozillazg/go-httpheader v0.2.1/go.mod h1:jJ8xECTlalr6ValeXYdOF8fFUISeBAdw6E61aqQma60= 443 | github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= 444 | github.com/nats-io/jwt/v2 v2.2.0/go.mod h1:0tqz9Hlu6bCBFLWAASKhE5vUA4c24L9KPUUgvwumE/k= 445 | github.com/nats-io/jwt/v2 v2.2.1-0.20220113022732-58e87895b296 h1:vU9tpM3apjYlLLeY23zRWJ9Zktr5jp+mloR942LEOpY= 446 | github.com/nats-io/jwt/v2 v2.2.1-0.20220113022732-58e87895b296/go.mod h1:0tqz9Hlu6bCBFLWAASKhE5vUA4c24L9KPUUgvwumE/k= 447 | github.com/nats-io/nats-server/v2 v2.7.3 h1:P0NgsnbTxrPMMPZ1/rLXWjS5bbPpRMCcPwlMd4nBDK4= 448 | github.com/nats-io/nats-server/v2 v2.7.3/go.mod h1:eJUrA5gm0ch6sJTEv85xmXIgQWsB0OyjkTsKXvlHbYc= 449 | github.com/nats-io/nats.go v1.13.1-0.20220121202836-972a071d373d h1:GRSmEJutHkdoxKsRypP575IIdoXe7Bm6yHQF6GcDBnA= 450 | github.com/nats-io/nats.go v1.13.1-0.20220121202836-972a071d373d/go.mod h1:BPko4oXsySz4aSWeFgOHLZs3G4Jq4ZAyE6/zMCxRT6w= 451 | github.com/nats-io/nkeys v0.3.0 h1:cgM5tL53EvYRU+2YLXIK0G2mJtK12Ft9oeooSZMA2G8= 452 | github.com/nats-io/nkeys v0.3.0/go.mod h1:gvUNGjVcM2IPr5rCsRsC6Wb3Hr2CQAm08dsxtV6A5y4= 453 | github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= 454 | github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= 455 | github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= 456 | github.com/neelance/sourcemap v0.0.0-20200213170602-2833bce08e4c/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= 457 | github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= 458 | github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= 459 | github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= 460 | github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= 461 | github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 462 | github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= 463 | github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= 464 | github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= 465 | github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= 466 | github.com/onsi/ginkgo/v2 v2.0.0/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= 467 | github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= 468 | github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= 469 | github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= 470 | github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE= 471 | github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs= 472 | github.com/panjf2000/ants/v2 v2.4.2/go.mod h1:f6F0NZVFsGCp5A7QW/Zj/m92atWwOkY0OIhFxRNFr4A= 473 | github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= 474 | github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= 475 | github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= 476 | github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= 477 | github.com/pelletier/go-toml v1.9.4 h1:tjENF6MfZAg8e4ZmZTeWaWiT2vXtsoO6+iuOjFhECwM= 478 | github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= 479 | github.com/pelletier/go-toml/v2 v2.0.0-beta.8 h1:dy81yyLYJDwMTifq24Oi/IslOslRrDSb3jwDggjz3Z0= 480 | github.com/pelletier/go-toml/v2 v2.0.0-beta.8/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo= 481 | github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 482 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 483 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 484 | github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= 485 | github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= 486 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 487 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 488 | github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= 489 | github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= 490 | github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= 491 | github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= 492 | github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= 493 | github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= 494 | github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= 495 | github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 496 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 497 | github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 498 | github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= 499 | github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= 500 | github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= 501 | github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= 502 | github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= 503 | github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= 504 | github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 505 | github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo= 506 | github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= 507 | github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 508 | github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= 509 | github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 510 | github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= 511 | github.com/sagikazarmark/crypt v0.5.0/go.mod h1:l+nzl7KWh51rpzp2h7t4MZWyiEWdhNpOAnclKvg+mdA= 512 | github.com/schollz/closestmatch v2.1.0+incompatible h1:Uel2GXEpJqOWBrlyI+oY9LTiyyjYS17cCYRqP13/SHk= 513 | github.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g= 514 | github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= 515 | github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= 516 | github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= 517 | github.com/shirou/gopsutil/v3 v3.22.3/go.mod h1:D01hZJ4pVHPpCTZ3m3T2+wDF2YAGfd+H4ifUguaQzHM= 518 | github.com/shurcooL/go v0.0.0-20200502201357-93f07166e636/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= 519 | github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= 520 | github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= 521 | github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw= 522 | github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= 523 | github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= 524 | github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= 525 | github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= 526 | github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= 527 | github.com/smartystreets/assertions v1.2.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo= 528 | github.com/smartystreets/assertions v1.2.1 h1:bKNHfEv7tSIjZ8JbKaFjzFINljxG4lzZvmHUnElzOIg= 529 | github.com/smartystreets/assertions v1.2.1/go.mod h1:wDmR7qL282YbGsPy6H/yAsesrxfxaaSlJazyFLYVFx8= 530 | github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= 531 | github.com/smartystreets/goconvey v1.7.2 h1:9RBaZCeXEQ3UselpuwUQHltGVXvdwm6cv1hgR6gDIPg= 532 | github.com/smartystreets/goconvey v1.7.2/go.mod h1:Vw0tHAZW6lzCRk3xgdin6fKYcG+G3Pg9vgXWeJpQFMM= 533 | github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= 534 | github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= 535 | github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= 536 | github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= 537 | github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= 538 | github.com/spf13/afero v1.8.2 h1:xehSyVa0YnHWsJ49JFljMpg1HX19V6NDZ1fkm1Xznbo= 539 | github.com/spf13/afero v1.8.2/go.mod h1:CtAatgMJh6bJEIs48Ay/FOnkljP3WeGUG0MC1RfAqwo= 540 | github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= 541 | github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= 542 | github.com/spf13/cast v1.4.1 h1:s0hze+J0196ZfEMTs80N7UlFt0BDuQ7Q+JDnHiMWKdA= 543 | github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= 544 | github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= 545 | github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= 546 | github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= 547 | github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= 548 | github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= 549 | github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= 550 | github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= 551 | github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 552 | github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= 553 | github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= 554 | github.com/spf13/viper v1.11.0 h1:7OX/1FS6n7jHD1zGrZTM7WtY13ZELRyosK4k93oPr44= 555 | github.com/spf13/viper v1.11.0/go.mod h1:djo0X/bA5+tYVoCn+C7cAYJGcVn/qYLFTG8gdUsX7Zk= 556 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 557 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 558 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 559 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 560 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 561 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 562 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 563 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 564 | github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= 565 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 566 | github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= 567 | github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= 568 | github.com/tdewolff/minify/v2 v2.11.1 h1:x2IAGnHs3qBjulArA7g4dYGCpcMrM8H2sywfwr436RA= 569 | github.com/tdewolff/minify/v2 v2.11.1/go.mod h1:UkCTT2Sa8N7XNU0Z9Q+De6NvaxPlC7DGfSWDRowwXqY= 570 | github.com/tdewolff/parse/v2 v2.5.28 h1:QziFVLe+bfFIwnCWAJzMrzwltQXPT21Evl9Z4x25D+U= 571 | github.com/tdewolff/parse/v2 v2.5.28/go.mod h1:WzaJpRSbwq++EIQHYIRTpbYKNA3gn9it1Ik++q4zyho= 572 | github.com/tdewolff/test v1.0.6 h1:76mzYJQ83Op284kMT+63iCNCI7NEERsIN8dLM+RiKr4= 573 | github.com/tdewolff/test v1.0.6/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE= 574 | github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.194/go.mod h1:7sCQWVkxcsR38nffDW057DRGk8mUjK1Ing/EFOK8s8Y= 575 | github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/kms v1.0.194/go.mod h1:yrBKWhChnDqNz1xuXdSbWXG56XawEq0G5j1lg4VwBD4= 576 | github.com/tencentyun/cos-go-sdk-v5 v0.7.35 h1:XVk5GQ4eH1q+DBUJfpaMMdU9TJZWMjwNNwv0PG5nbLQ= 577 | github.com/tencentyun/cos-go-sdk-v5 v0.7.35/go.mod h1:4dCEtLHGh8QPxHEkgq+nFaky7yZxQuYwgSJM87icDaw= 578 | github.com/tklauser/go-sysconf v0.3.10/go.mod h1:C8XykCvCb+Gn0oNCWPIlcb0RuglQTYaQ2hGm7jmxEFk= 579 | github.com/tklauser/numcpus v0.4.0/go.mod h1:1+UI3pD8NW14VMwdgJNJ1ESk2UnwhAnz5hMwiKKqXCQ= 580 | github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= 581 | github.com/u2takey/ffmpeg-go v0.4.1 h1:l5ClIwL3N2LaH1zF3xivb3kP2HW95eyG5xhHE1JdZ9Y= 582 | github.com/u2takey/ffmpeg-go v0.4.1/go.mod h1:ruZWkvC1FEiUNjmROowOAps3ZcWxEiOpFoHCvk97kGc= 583 | github.com/u2takey/go-utils v0.3.1 h1:TaQTgmEZZeDHQFYfd+AdUT1cT4QJgJn/XVPELhHw4ys= 584 | github.com/u2takey/go-utils v0.3.1/go.mod h1:6e+v5vEZ/6gu12w/DC2ixZdZtCrNokVxD0JUklcqdCs= 585 | github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= 586 | github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= 587 | github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= 588 | github.com/vmihailenco/msgpack/v5 v5.3.5 h1:5gO0H1iULLWGhs2H5tbAHIZTV8/cYafcFOr9znI5mJU= 589 | github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc= 590 | github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= 591 | github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= 592 | github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c= 593 | github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= 594 | github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= 595 | github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= 596 | github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= 597 | github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= 598 | github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= 599 | github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0 h1:6fRhSjgLCkTD3JnJxvaJ4Sj+TYblw757bqYgZaOq5ZY= 600 | github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI= 601 | github.com/yosssi/ace v0.0.5 h1:tUkIP/BLdKqrlrPwcmH0shwEEhTRHoGnc1wFIWmaBUA= 602 | github.com/yosssi/ace v0.0.5/go.mod h1:ALfIzm2vT7t5ZE7uoIZqF3TQ7SAOyupFZnkrF5id+K0= 603 | github.com/yudai/gojsondiff v1.0.0 h1:27cbfqXLVEJ1o8I6v3y9lg8Ydm53EKqHXAOMxEGlCOA= 604 | github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg= 605 | github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3IfnEUduWvb9is428/nNb5L3U01M= 606 | github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM= 607 | github.com/yudai/pp v2.0.1+incompatible h1:Q4//iY4pNF6yPLZIigmvcl7k/bPgrcTPIFIcmawg5bI= 608 | github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc= 609 | github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 610 | github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 611 | github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 612 | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 613 | github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= 614 | github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= 615 | go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= 616 | go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= 617 | go.etcd.io/etcd/api/v3 v3.5.2/go.mod h1:5GB2vv4A4AOn3yk7MftYGHkUfGtDHnEraIjym4dYz5A= 618 | go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= 619 | go.etcd.io/etcd/client/pkg/v3 v3.5.2/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= 620 | go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= 621 | go.etcd.io/etcd/client/v2 v2.305.2/go.mod h1:2D7ZejHVMIfog1221iLSYlQRzrtECw3kz4I4VAQm3qI= 622 | go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= 623 | go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= 624 | go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 625 | go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 626 | go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 627 | go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= 628 | go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= 629 | go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= 630 | go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= 631 | go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= 632 | go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= 633 | gocv.io/x/gocv v0.25.0/go.mod h1:Rar2PS6DV+T4FL+PM535EImD/h13hGVaHhnCu1xarBs= 634 | golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 635 | golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 636 | golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 637 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 638 | golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 639 | golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 640 | golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 641 | golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= 642 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 643 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 644 | golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= 645 | golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= 646 | golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 647 | golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 648 | golang.org/x/crypto v0.0.0-20220112180741-5e0467b6c7ce/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= 649 | golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 h1:kUhD7nTDoI3fVd9G4ORWrbV5NY0liEs/Jg2pv5f+bBA= 650 | golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= 651 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 652 | golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 653 | golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= 654 | golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= 655 | golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= 656 | golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 657 | golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 658 | golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 659 | golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= 660 | golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= 661 | golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= 662 | golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= 663 | golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= 664 | golang.org/x/image v0.0.0-20220601225756-64ec528b34cd h1:9NbNcTg//wfC5JskFW4Z3sqwVnjmJKHxLAol1bW2qgw= 665 | golang.org/x/image v0.0.0-20220601225756-64ec528b34cd/go.mod h1:doUCurBvlfPMKfmIpRIywoHmhN3VyhnoFDbvIEWF4hY= 666 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 667 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 668 | golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 669 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 670 | golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 671 | golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 672 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 673 | golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= 674 | golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 675 | golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 676 | golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 677 | golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 678 | golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= 679 | golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= 680 | golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= 681 | golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= 682 | golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 683 | golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 684 | golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 685 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 686 | golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 687 | golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 688 | golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 689 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 690 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 691 | golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 692 | golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 693 | golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 694 | golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 695 | golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 696 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 697 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 698 | golang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 699 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 700 | golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 701 | golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 702 | golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 703 | golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 704 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 705 | golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 706 | golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 707 | golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 708 | golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 709 | golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 710 | golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 711 | golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 712 | golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 713 | golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 714 | golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 715 | golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 716 | golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 717 | golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 718 | golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 719 | golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 720 | golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 721 | golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 722 | golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 723 | golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 724 | golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 725 | golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 726 | golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 727 | golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 728 | golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 729 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 730 | golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= 731 | golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= 732 | golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= 733 | golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= 734 | golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 735 | golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 736 | golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 737 | golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= 738 | golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= 739 | golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= 740 | golang.org/x/net v0.0.0-20220412020605-290c469a71a5 h1:bRb386wvrE+oBNdF1d/Xh9mQrfQ4ecYhW5qJ5GvTGT4= 741 | golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= 742 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 743 | golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 744 | golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 745 | golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 746 | golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 747 | golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 748 | golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 749 | golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 750 | golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 751 | golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 752 | golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 753 | golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 754 | golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 755 | golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 756 | golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 757 | golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 758 | golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 759 | golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 760 | golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= 761 | golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= 762 | golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= 763 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 764 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 765 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 766 | golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 767 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 768 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 769 | golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 770 | golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 771 | golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 772 | golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 773 | golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= 774 | golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 775 | golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 776 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 777 | golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 778 | golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 779 | golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 780 | golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 781 | golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 782 | golang.org/x/sys v0.0.0-20190130150945-aca44879d564/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 783 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 784 | golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 785 | golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 786 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 787 | golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 788 | golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 789 | golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 790 | golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 791 | golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 792 | golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 793 | golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 794 | golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 795 | golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 796 | golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 797 | golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 798 | golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 799 | golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 800 | golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 801 | golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 802 | golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 803 | golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 804 | golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 805 | golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 806 | golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 807 | golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 808 | golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 809 | golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 810 | golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 811 | golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 812 | golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 813 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 814 | golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 815 | golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 816 | golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 817 | golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 818 | golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 819 | golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 820 | golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 821 | golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 822 | golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 823 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 824 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 825 | golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 826 | golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 827 | golang.org/x/sys v0.0.0-20201207223542-d4d67f95c62d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 828 | golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 829 | golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 830 | golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 831 | golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 832 | golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 833 | golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 834 | golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 835 | golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 836 | golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 837 | golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 838 | golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 839 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 840 | golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 841 | golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 842 | golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 843 | golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 844 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 845 | golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 846 | golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 847 | golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 848 | golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 849 | golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 850 | golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 851 | golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 852 | golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 853 | golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 854 | golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 855 | golang.org/x/sys v0.0.0-20220111092808-5a964db01320/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 856 | golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 857 | golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 858 | golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 859 | golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 860 | golang.org/x/sys v0.0.0-20220412211240-33da011f77ad h1:ntjMns5wyP/fN65tdBD4g8J5w8n015+iIIs9rtjXkY0= 861 | golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 862 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 863 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 864 | golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 865 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 866 | golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 867 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 868 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 869 | golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 870 | golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 871 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 872 | golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= 873 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 874 | golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 875 | golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 876 | golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 877 | golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 878 | golang.org/x/time v0.0.0-20220411224347-583f2d630306 h1:+gHMid33q6pen7kv9xvT+JRinntgeXO2AeZVd0AWD3w= 879 | golang.org/x/time v0.0.0-20220411224347-583f2d630306/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 880 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 881 | golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 882 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 883 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 884 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 885 | golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 886 | golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 887 | golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 888 | golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 889 | golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 890 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 891 | golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 892 | golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 893 | golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 894 | golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 895 | golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 896 | golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 897 | golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 898 | golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 899 | golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 900 | golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 901 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 902 | golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 903 | golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 904 | golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 905 | golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 906 | golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 907 | golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 908 | golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 909 | golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 910 | golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 911 | golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 912 | golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 913 | golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 914 | golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= 915 | golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= 916 | golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= 917 | golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 918 | golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 919 | golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 920 | golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 921 | golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 922 | golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 923 | golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 924 | golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 925 | golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= 926 | golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 927 | golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 928 | golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 929 | golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 930 | golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 931 | golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 932 | golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 933 | golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= 934 | golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= 935 | golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= 936 | golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= 937 | golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= 938 | golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= 939 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 940 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 941 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 942 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 943 | golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f h1:GGU+dLjvlC3qDwqYgL6UgRmHXhOOgns0bZu2Ty5mm6U= 944 | golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 945 | google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= 946 | google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= 947 | google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 948 | google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 949 | google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 950 | google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 951 | google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 952 | google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 953 | google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 954 | google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 955 | google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 956 | google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 957 | google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= 958 | google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= 959 | google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= 960 | google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= 961 | google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= 962 | google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= 963 | google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= 964 | google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= 965 | google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= 966 | google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8= 967 | google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= 968 | google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= 969 | google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= 970 | google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= 971 | google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= 972 | google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= 973 | google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= 974 | google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI= 975 | google.golang.org/api v0.59.0/go.mod h1:sT2boj7M9YJxZzgeZqXogmhfmRWDtPzT31xkieUbuZU= 976 | google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I= 977 | google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo= 978 | google.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g= 979 | google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA= 980 | google.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc4j8= 981 | google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs= 982 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 983 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 984 | google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 985 | google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= 986 | google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 987 | google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 988 | google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 989 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 990 | google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 991 | google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 992 | google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 993 | google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 994 | google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 995 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 996 | google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= 997 | google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 998 | google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 999 | google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 1000 | google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 1001 | google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 1002 | google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 1003 | google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= 1004 | google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 1005 | google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 1006 | google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 1007 | google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 1008 | google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 1009 | google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 1010 | google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 1011 | google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 1012 | google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 1013 | google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= 1014 | google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= 1015 | google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= 1016 | google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 1017 | google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 1018 | google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 1019 | google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 1020 | google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 1021 | google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 1022 | google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 1023 | google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 1024 | google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 1025 | google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 1026 | google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 1027 | google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 1028 | google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 1029 | google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 1030 | google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= 1031 | google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= 1032 | google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= 1033 | google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= 1034 | google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= 1035 | google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= 1036 | google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= 1037 | google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= 1038 | google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= 1039 | google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= 1040 | google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= 1041 | google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= 1042 | google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= 1043 | google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= 1044 | google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= 1045 | google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= 1046 | google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= 1047 | google.golang.org/genproto v0.0.0-20211008145708-270636b82663/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= 1048 | google.golang.org/genproto v0.0.0-20211028162531-8db9c33dc351/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= 1049 | google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= 1050 | google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= 1051 | google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= 1052 | google.golang.org/genproto v0.0.0-20211221195035-429b39de9b1c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= 1053 | google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= 1054 | google.golang.org/genproto v0.0.0-20220207164111-0872dc986b00/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= 1055 | google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= 1056 | google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= 1057 | google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= 1058 | google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= 1059 | google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= 1060 | google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= 1061 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 1062 | google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= 1063 | google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= 1064 | google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 1065 | google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= 1066 | google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 1067 | google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 1068 | google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 1069 | google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= 1070 | google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= 1071 | google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= 1072 | google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= 1073 | google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= 1074 | google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= 1075 | google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= 1076 | google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= 1077 | google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= 1078 | google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= 1079 | google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= 1080 | google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= 1081 | google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= 1082 | google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= 1083 | google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= 1084 | google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= 1085 | google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= 1086 | google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= 1087 | google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= 1088 | google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= 1089 | google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= 1090 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 1091 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 1092 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= 1093 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= 1094 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= 1095 | google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 1096 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 1097 | google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 1098 | google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= 1099 | google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= 1100 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 1101 | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 1102 | google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 1103 | google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= 1104 | google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= 1105 | gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 1106 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 1107 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 1108 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 1109 | gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 1110 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= 1111 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= 1112 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 1113 | gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= 1114 | gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= 1115 | gopkg.in/ini.v1 v1.66.4 h1:SsAcf+mM7mRZo2nJNGt8mZCjG8ZRaNGMURJw7BsIST4= 1116 | gopkg.in/ini.v1 v1.66.4/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= 1117 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= 1118 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= 1119 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 1120 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 1121 | gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 1122 | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 1123 | gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 1124 | gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 1125 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 1126 | gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 1127 | gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= 1128 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 1129 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 1130 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= 1131 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 1132 | gorm.io/driver/mysql v1.3.3 h1:jXG9ANrwBc4+bMvBcSl8zCfPBaVoPyBEBshA8dA93X8= 1133 | gorm.io/driver/mysql v1.3.3/go.mod h1:ChK6AHbHgDCFZyJp0F+BmVGb06PSIoh9uVYKAlRbb2U= 1134 | gorm.io/gorm v1.23.1/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk= 1135 | gorm.io/gorm v1.23.5 h1:TnlF26wScKSvknUC/Rn8t0NLLM22fypYBlvj1+aH6dM= 1136 | gorm.io/gorm v1.23.5/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk= 1137 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 1138 | honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 1139 | honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 1140 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 1141 | honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= 1142 | honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= 1143 | honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= 1144 | moul.io/http2curl v1.0.0 h1:6XwpyZOYsgZJrU8exnG87ncVkU1FVCcTRpwzOkTDUi8= 1145 | moul.io/http2curl v1.0.0/go.mod h1:f6cULg+e4Md/oW1cYmwW4IWQOVl2lGbmCNGOHvzX2kE= 1146 | rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= 1147 | rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= 1148 | rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= 1149 | sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= 1150 | -------------------------------------------------------------------------------- /sql/douyin.sql: -------------------------------------------------------------------------------- 1 | 2 | DROP TABLE IF EXISTS `tb_comment`; 3 | CREATE TABLE `tb_comment` ( 4 | `comment_id` bigint NOT NULL AUTO_INCREMENT, 5 | `user_id` bigint DEFAULT 0, 6 | `video_id` bigint DEFAULT 0, 7 | `content` varchar(40) DEFAULT '', 8 | `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', 9 | PRIMARY KEY (`comment_id`) 10 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 11 | 12 | DROP TABLE IF EXISTS `tb_favorite`; 13 | CREATE TABLE `tb_favorite` ( 14 | `favorite_id` bigint NOT NULL AUTO_INCREMENT, 15 | `username` varchar(40) DEFAULT '', 16 | `user_id` bigint DEFAULT 0, 17 | `video_id` bigint DEFAULT 0, 18 | `is_deleted` tinyint DEFAULT 0, 19 | PRIMARY KEY (`favorite_id`) 20 | )ENGINE=InnoDB DEFAULT CHARSET=utf8; 21 | 22 | DROP TABLE IF EXISTS `tb_relation`; 23 | CREATE TABLE `tb_relation` ( 24 | `relation_id` bigint NOT NULL AUTO_INCREMENT, 25 | `follower_id` bigint DEFAULT '0', 26 | `following_id` bigint DEFAULT '0', 27 | `isdeleted` tinyint DEFAULT '0', 28 | PRIMARY KEY (`relation_id`) 29 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 30 | 31 | DROP TABLE IF EXISTS `tb_user`; 32 | CREATE TABLE `tb_user` ( 33 | `user_id` bigint NOT NULL AUTO_INCREMENT, 34 | `name` varchar(40) DEFAULT '', 35 | `follow_count` int DEFAULT '0', 36 | `follower_count` int DEFAULT '0', 37 | `is_follow` tinyint DEFAULT '0', 38 | `password` char(40) DEFAULT '', 39 | PRIMARY KEY (`user_id`) 40 | )ENGINE=InnoDB DEFAULT CHARSET=utf8; 41 | 42 | DROP TABLE IF EXISTS `tb_video`; 43 | CREATE TABLE `tb_video` ( 44 | `video_id` bigint(20) NOT NULL AUTO_INCREMENT, 45 | `user_id` bigint(20) DEFAULT NULL, 46 | `play_url` varchar(60) CHARACTER SET utf8 DEFAULT '', 47 | `cover_url` varchar(60) CHARACTER SET utf8 DEFAULT '', 48 | `favorite_count` int(11) DEFAULT '0', 49 | `comment_count` int(11) DEFAULT '0', 50 | `title` text CHARACTER SET utf8 COMMENT '视频标题', 51 | `create_date` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', 52 | `update_date` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', 53 | PRIMARY KEY (`video_id`) 54 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -------------------------------------------------------------------------------- /src/cache/redis.go: -------------------------------------------------------------------------------- 1 | package cache 2 | 3 | import ( 4 | "context" 5 | . "douyin/config" 6 | "fmt" 7 | "github.com/go-redis/redis/v8" 8 | "time" 9 | ) 10 | 11 | var ctx = context.Background() 12 | var rc *redis.Client 13 | 14 | func init() { 15 | host := AppConfig.GetString("redis.host") 16 | port := AppConfig.GetString("redis.port") 17 | password := AppConfig.GetString("redis.password") 18 | rc = redis.NewClient(&redis.Options{ 19 | Addr: host + ":" + port, 20 | Password: password, // no password set 21 | DB: 0, // use default DB 22 | }) 23 | } 24 | func RCGet(key string) *redis.StringCmd { 25 | return rc.Get(ctx, key) 26 | } 27 | func RCExists(key string) bool { 28 | return rc.Exists(ctx, key).Val() != 0 29 | } 30 | func RCSet(key string, value interface{}, expiration time.Duration) { 31 | if RCExists(key) { 32 | rc.Expire(ctx, key, expiration) 33 | return 34 | } 35 | rc.Set(ctx, key, value, expiration) 36 | } 37 | func RCIncrement(key string) { 38 | rc.Incr(ctx, key) 39 | } 40 | func ExampleClient() { 41 | rdb := redis.NewClient(&redis.Options{ 42 | Addr: "localhost:6379", 43 | Password: "", // no password set 44 | DB: 0, // use default DB 45 | }) 46 | 47 | err := rdb.Set(ctx, "key", "value", 0).Err() 48 | if err != nil { 49 | panic(err) 50 | } 51 | 52 | val, err := rdb.Get(ctx, "key").Result() 53 | if err != nil { 54 | panic(err) 55 | } 56 | fmt.Println("key", val) 57 | 58 | val2, err := rdb.Get(ctx, "key2").Result() 59 | if err == redis.Nil { 60 | fmt.Println("key2 does not exist") 61 | } else if err != nil { 62 | panic(err) 63 | } else { 64 | fmt.Println("key2", val2) 65 | } 66 | } 67 | 68 | func RCSAdd(key string, members interface{}) { 69 | rc.SAdd(ctx, key, members) 70 | } 71 | 72 | func RCSRem(key string, members interface{}) { 73 | rc.SRem(ctx, key, members) 74 | } 75 | 76 | func RCSmembers(key string) *redis.StringSliceCmd { 77 | return rc.SMembers(ctx, key) 78 | } 79 | -------------------------------------------------------------------------------- /src/cache/redis_test.go: -------------------------------------------------------------------------------- 1 | package cache 2 | 3 | import ( 4 | "crypto/md5" 5 | "encoding/hex" 6 | "hash/crc32" 7 | "testing" 8 | ) 9 | 10 | func TestRedisClient(t *testing.T) { 11 | 12 | t.Log(RCExists("ebba034e09d79a50692371d0070e61e")) 13 | 14 | } 15 | 16 | func CRC32(input string) uint32 { 17 | bytes := []byte(input) 18 | return crc32.ChecksumIEEE(bytes) 19 | } 20 | func MD5(s string) string { 21 | sum := md5.Sum([]byte(s)) 22 | return hex.EncodeToString(sum[:]) 23 | } 24 | -------------------------------------------------------------------------------- /src/common/common.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | type Response struct { 4 | StatusCode int32 `json:"status_code"` 5 | StatusMsg string `json:"status_msg,omitempty"` 6 | } 7 | 8 | type VideoList1 struct { 9 | Id int64 `gorm:"column:video_id;primaryKey;autoIncrement:true" json:"id"` 10 | UserId int64 `gorm:"column:user_id"` 11 | PlayURL string `gorm:"column:play_url" json:"play_url"` 12 | CoverURL string `gorm:"column:cover_url" json:"cover_url"` 13 | FavoriteCount int64 `gorm:"column:favorite_count" json:"favorite_count"` 14 | CommentCount int64 `gorm:"column:comment_count" json:"comment_count"` 15 | Title string `gorm:"column:title"` 16 | } 17 | 18 | func (*VideoList1) TableName() string { 19 | return "tb_video" 20 | } 21 | 22 | type User2 struct { 23 | Id int64 `gorm:"column:user_id;primaryKey;autoIncrement:true" json:"id"` 24 | Name string `gorm:"column:name" json:"name"` 25 | FollowCount int64 `gorm:"column:follow_count" json:"follow_count"` 26 | FollowerCount int64 `gorm:"column:follower_count" json:"follower_count"` 27 | IsFollow bool `gorm:"column:is_follow" json:"is_follow"` 28 | } 29 | 30 | type VideoList2 struct { 31 | Id int64 `gorm:"column:video_id;primaryKey;autoIncrement:true" json:"id"` 32 | Author User2 `json:"author"` 33 | PlayURL string `gorm:"column:play_url" json:"play_url"` 34 | CoverURL string `gorm:"column:cover_url" json:"cover_url"` 35 | FavoriteCount int64 `gorm:"column:favorite_count" json:"favorite_count"` 36 | CommentCount int64 `gorm:"column:comment_count" json:"comment_count"` 37 | IsFavorite bool `json:"is_favorite"` 38 | Title string `json:"title"` 39 | } 40 | type VideoListResponse struct { 41 | StatusCode int32 `json:"status_code"` 42 | StatusMsg string `json:"status_msg,omitempty"` 43 | VideoLists []VideoList2 `json:"video_list"` 44 | } 45 | 46 | func (*VideoList2) TableName() string { 47 | return "tb_video" 48 | } 49 | 50 | type User struct { 51 | Id int64 `gorm:"column:user_id;primaryKey;autoIncrement:true" json:"id,omitempty"` 52 | Name string `gorm:"column:name" json:"name,omitempty"` 53 | FollowCount int64 `gorm:"column:follow_count" json:"follow_count,omitempty"` 54 | FollowerCount int64 `gorm:"column:follower_count" json:"follower_count,omitempty"` 55 | IsFollow bool `gorm:"column:is_follow" json:"is_follow,omitempty"` 56 | Password string `gorm:"column:password"` 57 | } 58 | 59 | func (*User) TableName() string { 60 | return "tb_user" 61 | } 62 | 63 | func (user *User) IsCorrect(password string) bool { 64 | return user.Password == password 65 | } 66 | func (user *User) Exists() bool { 67 | return user.Id > 0 68 | } 69 | -------------------------------------------------------------------------------- /src/config/app_config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import "github.com/spf13/viper" 4 | 5 | var AppConfig = viper.New() 6 | 7 | func init() { 8 | AppConfig.AddConfigPath("./") 9 | AppConfig.SetConfigName("application") //设置读取的文件名 10 | AppConfig.SetConfigType("yaml") //设置文件的类型 11 | //尝试进行配置读取 12 | if err := AppConfig.ReadInConfig(); err != nil { 13 | panic(err) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/config/cos_config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "github.com/tencentyun/cos-go-sdk-v5" 5 | "log" 6 | ) 7 | 8 | import ( 9 | "net/http" 10 | "net/url" 11 | ) 12 | 13 | var Cos *cos.Client 14 | 15 | func init() { 16 | log.Println("初始化cos配置...") 17 | u, _ := url.Parse(AppConfig.GetString("cos.url")) 18 | bucket := &cos.BaseURL{BucketURL: u} 19 | Cos = cos.NewClient(bucket, &http.Client{Transport: &cos.AuthorizationTransport{ 20 | SecretID: AppConfig.GetString("cos.SecretId"), 21 | SecretKey: AppConfig.GetString("cos.SecretKey"), 22 | }}) 23 | log.Println("初始化cos配置完成") 24 | } 25 | -------------------------------------------------------------------------------- /src/config/cos_test.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "context" 5 | "github.com/tencentyun/cos-go-sdk-v5" 6 | "net/http" 7 | "net/url" 8 | "os" 9 | "strings" 10 | "testing" 11 | ) 12 | 13 | func TestCos(t *testing.T) { 14 | // 将 examplebucket-1250000000 和 COS_REGION 修改为真实的信息 15 | // 存储桶名称,由bucketname-appid 组成,appid必须填入,可以在COS控制台查看存储桶名称。https://console.cloud.tencent.com/cos5/bucket 16 | // COS_REGION 可以在控制台查看,https://console.cloud.tencent.com/cos5/bucket, 关于地域的详情见 https://cloud.tencent.com/document/product/436/6224 17 | u, _ := url.Parse("https://hzbucket-1312272501.cos.ap-guangzhou.myqcloud.com") 18 | b := &cos.BaseURL{BucketURL: u} 19 | c := cos.NewClient(b, &http.Client{ 20 | Transport: &cos.AuthorizationTransport{ 21 | SecretID: "SecretId", // 替换为用户的 SecretId,请登录访问管理控制台进行查看和管理,https://console.cloud.tencent.com/cam/capi 22 | SecretKey: "SecretKey", // 替换为用户的 SecretKey,请登录访问管理控制台进行查看和管理,https://console.cloud.tencent.com/cam/capi 23 | }, 24 | }) 25 | // 对象键(Key)是对象在存储桶中的唯一标识。 26 | // 例如,在对象的访问域名 `examplebucket-1250000000.cos.COS_REGION.myqcloud.com/test/objectPut.go` 中,对象键为 test/objectPut.go 27 | name := "2022/6" 28 | //1.通过字符串上传对象 29 | f := strings.NewReader("测试用例") 30 | 31 | _, err := c.Object.Put(context.Background(), name, f, nil) 32 | 33 | if err != nil { 34 | panic(err) 35 | } 36 | // 2.通过本地文件上传对象 37 | _, err = c.Object.PutFromFile(context.Background(), name, "C:\\Users\\Chen\\Desktop\\抖音\\测试1.mp4", nil) 38 | if err != nil { 39 | panic(err) 40 | } 41 | // 3.通过文件流上传对象 42 | fd, err := os.Open("./test") 43 | if err != nil { 44 | panic(err) 45 | } 46 | defer fd.Close() 47 | _, err = c.Object.Put(context.Background(), name, fd, nil) 48 | if err != nil { 49 | panic(err) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/controller/comment.go: -------------------------------------------------------------------------------- 1 | package controller 2 | 3 | import ( 4 | "douyin/cache" 5 | . "douyin/common" 6 | db2 "douyin/db" 7 | "fmt" 8 | "github.com/kataras/iris/v12" 9 | "github.com/kataras/iris/v12/mvc" 10 | "log" 11 | "time" 12 | ) 13 | 14 | type Comment struct { 15 | Id int64 `json:"id,omitempty"` 16 | User User `json:"user"` 17 | Content string `json:"content,omitempty"` 18 | CreateDate string `json:"create_date,omitempty"` 19 | } 20 | 21 | type CommentListRequest struct { 22 | UserId int64 `json:"user_id"` 23 | Token string `json:"token"` 24 | VideoId int64 `json:"video_id"` 25 | } 26 | 27 | type CommentListResponse struct { 28 | //Response Response 29 | StatusCode int32 `json:"status_code"` //0成功,其他值失败 30 | StatusMsg string `json:"status_msg"` 31 | CommentList []Comment `json:"comment_list"` 32 | } 33 | 34 | type CommentActionRequest struct { 35 | UserId int64 `json:"user_id"` 36 | Token string `json:"token"` 37 | VideoId int64 `json:"video_id"` 38 | ActionType int64 `json:"action_type"` //1-发布评论,2-删除评论 39 | CommentText string `json:"comment_text"` 40 | CommentId int64 `json:"comment_id"` //需要删除评论的id 41 | } 42 | type CommentActionResponse struct { 43 | StatusCode int32 `json:"status_code"` //0表示成功,其他值表示失败 44 | StatusMsg string `json:"status_msg"` 45 | } 46 | 47 | var sqlSession = db2.DB 48 | 49 | type CommentController struct { 50 | } 51 | 52 | /** 53 | 54 | "/douyin/comment/list/?user_id=&token=&video_id=1" 55 | 获取所有评论 56 | 57 | */ 58 | 59 | func (cc *CommentController) GetList(ctx iris.Context) mvc.Result { 60 | video_id := ctx.URLParam("video_id") 61 | sql := "select comment_id,tb_comment.user_id,content,create_time,name from tb_comment inner join tb_user on tb_comment.user_id=tb_user.user_id and video_id=? order by create_time desc;" 62 | rows, err := sqlSession.Query(sql, video_id) 63 | if err != nil { 64 | panic(err) 65 | } 66 | //延时资源关闭 67 | defer rows.Close() 68 | 69 | var commentList []Comment = make([]Comment, 0) 70 | var comment Comment 71 | for rows.Next() { 72 | //这里是查询video_id视频的所有评论,然后查询出来的全部封装到comment类中,再封装到CommentListResponse中返回到前端 73 | rows.Scan(&comment.Id, &comment.User.Id, &comment.Content, &comment.CreateDate, &comment.User.Name) 74 | commentList = append(commentList, comment) 75 | } 76 | if rows.Err() != nil { 77 | log.Fatal(rows.Err()) 78 | } 79 | 80 | return mvc.Response{ 81 | Object: CommentListResponse{StatusCode: 0, StatusMsg: "查询成功!", CommentList: commentList}, 82 | } 83 | } 84 | 85 | /** 86 | "/douyin/comment/action 87 | 发表评论或者删除评论,根据 88 | */ 89 | 90 | func (cc *CommentController) PostAction(ctx iris.Context) mvc.Result { 91 | var actionRequest CommentActionRequest 92 | 93 | fmt.Println(ctx.URLParams()) 94 | 95 | //actionRequest.UserId, _ = ctx.URLParamInt64("user_id") 96 | actionRequest.Token = ctx.URLParam("token") 97 | actionRequest.VideoId, _ = ctx.URLParamInt64("video_id") 98 | actionRequest.ActionType, _ = ctx.URLParamInt64("action_type") 99 | actionRequest.CommentText = ctx.URLParam("comment_text") 100 | actionRequest.CommentId, _ = ctx.URLParamInt64("comment_id") 101 | actionRequest.UserId, _ = cache.RCGet(actionRequest.Token).Int64() 102 | 103 | if actionRequest.ActionType == 1 { 104 | //发布评论 105 | sql := "insert into tb_comment values(?,?,?,?,?)" 106 | _, err := sqlSession.Exec(sql, nil, actionRequest.UserId, actionRequest.VideoId, actionRequest.CommentText, time.Now().Format("2006-01-02 15:04:05")) 107 | if err != nil { 108 | return mvc.Response{Object: Response{StatusCode: 1, StatusMsg: "发表失败!!!"}} 109 | } 110 | 111 | rows, _ := sqlSession.Query("select count(*) from tb_comment where video_id=?", actionRequest.VideoId) 112 | var count int 113 | if rows.Next() { 114 | rows.Scan(&count) 115 | } 116 | sql1 := "update tb_video set comment_count=? where video_id=?" 117 | sqlSession.Exec(sql1, count, actionRequest.VideoId) 118 | } else { 119 | //删除评论,得保证是本人删除,也就是删除的是当前用户的评论 120 | sql := "delete from tb_comment where comment_id=? and user_id=?" 121 | _, err := sqlSession.Exec(sql, actionRequest.CommentId, actionRequest.UserId) 122 | if err != nil { 123 | return mvc.Response{ 124 | Object: Response{StatusCode: 1, StatusMsg: "删除失败,请重试!!!"}, 125 | } 126 | } 127 | 128 | rows, _ := sqlSession.Query("select count(*) from tb_comment where video_id=?", actionRequest.VideoId) 129 | var count int64 130 | if rows.Next() { 131 | rows.Scan(&count) 132 | } 133 | sqlSession.Exec("update tb_video set comment_count=? where video_id=?", count) 134 | } 135 | return mvc.Response{ 136 | Object: Response{StatusCode: 0, StatusMsg: "操作成功!!!"}, 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /src/controller/favorite.go: -------------------------------------------------------------------------------- 1 | package controller 2 | 3 | // 点赞和点赞列表实现 4 | // 直接对数据库进行操作 5 | // 优点: 6 | // 1.实现简单 7 | // 2.数据准确,且实时性好 8 | // 3.适用于业务规模较小,大概在十万级左右 9 | // 缺点: 10 | // 1.点赞属于高频操作,若请求过多将导致数据库压力过大 11 | // 方案二:使用redis缓存实现 12 | // 使用Set,key 为 favorite_{用户id},value 为 该用户点赞的视频id集合 13 | // 点赞操作只需向redis中加入一条数据,redis内数据定期刷回数据库 14 | // 查询某用户点赞的所有视频:从数据库和redis取出该用户点赞的所有视频id集合,然后用此集合查询结果返回 15 | // 判断某用户是否点赞某视频:先查redis,存在直接返回,不存在去数据库找 16 | 17 | import ( 18 | "douyin/cache" 19 | . "douyin/common" 20 | "douyin/config" 21 | . "douyin/db" 22 | "github.com/kataras/iris/v12" 23 | "github.com/kataras/iris/v12/mvc" 24 | ) 25 | 26 | import ( 27 | "log" 28 | "time" 29 | ) 30 | 31 | type favoriteResponse struct { 32 | Response 33 | } 34 | 35 | type favoriteListResponse struct { 36 | StatusCode int32 `json:"status_code"` 37 | StatusMsg string `json:"status_msg,omitempty"` 38 | VideoList []VideoList2 `json:"video_list,omitempty"` 39 | } 40 | 41 | type FavoriteController struct { 42 | } 43 | 44 | func (fc *FavoriteController) PostAction(ctx iris.Context) mvc.Result { 45 | var token = ctx.URLParamDefault("token", "") 46 | var videoId = ctx.URLParamInt64Default("video_id", -1) 47 | // 动作类型 1-点赞 2-取消点赞 48 | var actionType = ctx.URLParamIntDefault("action_type", -1) 49 | 50 | if token == "" || videoId == -1 || actionType < 1 || actionType > 2 { 51 | return mvc.Response{ 52 | Object: Response{ 53 | StatusCode: -1, 54 | StatusMsg: "缺少参数或参数错误", 55 | }, 56 | } 57 | } 58 | if !cache.RCExists(token) { 59 | return mvc.Response{ 60 | Object: Response{ 61 | StatusCode: -1, 62 | StatusMsg: "鉴权失败,检查登录状态", 63 | }, 64 | } 65 | } 66 | var response mvc.Response 67 | userId, _ := cache.RCGet(token).Int64() 68 | cache.RCSet(token, userId, time.Minute*30) 69 | user := SelectUserById(userId) 70 | tx, err := DB.Begin() 71 | if RecordExists(userId, videoId) { 72 | //如果数据库中有记录,那么根据action_type改变is_deleted即可 73 | _, err = tx.Exec( 74 | "update tb_favorite set is_deleted = ? where user_id = ? and video_id = ?", 75 | actionType-1, userId, videoId) 76 | //更新tb_video的冗余字段,若为点赞操作则视频点赞数+1,若为取消点赞操作则-1 77 | number := 1 78 | if actionType == 2 { 79 | number = -1 80 | } 81 | _, err = tx.Exec("update tb_video set favorite_count = favorite_count + ? where video_id = ?", 82 | number, videoId) 83 | if err != nil { 84 | tx.Rollback() 85 | panic(err.Error()) 86 | } 87 | } else { 88 | //若不存在记录,则只处理点赞操作 89 | if actionType == 1 { 90 | _, err = tx.Exec( 91 | "insert into tb_favorite(user_id,video_id,is_deleted) values (?,?,?)", 92 | user.Id, videoId, 0) 93 | _, err = tx.Exec("update tb_video set favorite_count = favorite_count + 1 where video_id = ?", videoId) 94 | if err != nil { 95 | tx.Rollback() 96 | panic(err.Error()) 97 | } 98 | } 99 | } 100 | tx.Commit() 101 | response = mvc.Response{ 102 | Object: Response{ 103 | StatusCode: 0, 104 | StatusMsg: "操作成功", 105 | }, 106 | } 107 | return response 108 | } 109 | 110 | func (fc *FavoriteController) GetList(ctx iris.Context) mvc.Result { 111 | var token = ctx.URLParamDefault("token", "") 112 | var authorId = ctx.URLParamInt64Default("user_id", -1) 113 | if token == "" || authorId == -1 { 114 | return mvc.Response{ 115 | Object: Response{ 116 | StatusCode: -1, 117 | StatusMsg: "缺少参数或参数错误", 118 | }, 119 | } 120 | } 121 | 122 | var response mvc.Response 123 | userId, _ := cache.RCGet(token).Int64() 124 | cache.RCSet(token, userId, time.Minute*30) 125 | rows, err := DB.Query("select video_id, play_url, cover_url, favorite_count, comment_count, title,tu.user_id,tu.name,follow_count, follower_count from tb_video tv inner join tb_user tu on tv.user_id = tu.user_id where video_id in (select video_id from tb_favorite where user_id = ? and is_deleted = 0)", authorId) 126 | if err != nil { 127 | log.Fatalln(err) 128 | } 129 | defer rows.Close() 130 | baseUrl := config.AppConfig.GetString("video.imageUrl") 131 | var videoListResponse VideoListResponse 132 | for rows.Next() { 133 | var favoriteVideo VideoList2 134 | err = rows.Scan(&favoriteVideo.Id, &favoriteVideo.PlayURL, &favoriteVideo.CoverURL, &favoriteVideo.FavoriteCount, &favoriteVideo.CommentCount, &favoriteVideo.Title, &favoriteVideo.Author.Id, &favoriteVideo.Author.Name, &favoriteVideo.Author.FollowCount, &favoriteVideo.Author.FollowerCount) 135 | favoriteVideo.PlayURL = baseUrl + favoriteVideo.PlayURL 136 | favoriteVideo.CoverURL = baseUrl + favoriteVideo.CoverURL 137 | favoriteVideo.Author.IsFollow = isFollow(userId, favoriteVideo.Author.Id) 138 | videoListResponse.VideoLists = append(videoListResponse.VideoLists, favoriteVideo) 139 | if err != nil { 140 | log.Fatalln(err) 141 | } 142 | } 143 | response = mvc.Response{ 144 | Object: videoListResponse, 145 | } 146 | return response 147 | } 148 | 149 | func isFollow(userId int64, authorId int64) bool { 150 | if userId == -1 { 151 | return false 152 | } 153 | row := DB.QueryRow("select relation_id from tb_relation where follower_id = ? and following_id = ? and isdeleted = 0", userId, authorId) 154 | relationId := -1 155 | row.Scan(&relationId) 156 | if relationId > 0 { 157 | return true 158 | } 159 | return false 160 | } 161 | -------------------------------------------------------------------------------- /src/controller/feed.go: -------------------------------------------------------------------------------- 1 | package controller 2 | 3 | import ( 4 | "bytes" 5 | service2 "douyin/service" 6 | "github.com/kataras/iris/v12" 7 | "github.com/kataras/iris/v12/mvc" 8 | "os" 9 | "os/exec" 10 | "strings" 11 | ) 12 | 13 | type FeedController struct { 14 | } 15 | 16 | func (fc *FeedController) BeforeActivation(b mvc.BeforeActivation) { 17 | b.Handle("GET", "/video/{videoName}", "GetFeedVideo") 18 | b.Handle("GET", "/", "Get") 19 | } 20 | 21 | func (fc *FeedController) Get(ctx iris.Context) { 22 | ctx.JSON(service2.GetFeed(ctx.URLParam("latest_time"), ctx.URLParam("token"))) 23 | } 24 | 25 | func (fc *FeedController) GetFeedVideo(b iris.Context) { 26 | videoName := b.Params().Get("videoName") 27 | 28 | stdout, _ := exec.Command("go", "env", "GOMOD").Output() 29 | path := string(bytes.TrimSpace(stdout)) 30 | if path == "" { 31 | os.Exit(1) 32 | } 33 | ss := strings.Split(path, "\\") 34 | ss = ss[:len(ss)-1] 35 | path = strings.Join(ss, "\\") + "\\" 36 | 37 | b.SendFile(path+service2.FilePath+videoName, videoName) 38 | } 39 | -------------------------------------------------------------------------------- /src/controller/publish.go: -------------------------------------------------------------------------------- 1 | package controller 2 | 3 | import ( 4 | . "douyin/common" 5 | service2 "douyin/service" 6 | "github.com/kataras/iris/v12" 7 | "log" 8 | "strconv" 9 | ) 10 | 11 | type PublishController struct { 12 | } 13 | 14 | func (pc *PublishController) Get() { 15 | 16 | } 17 | 18 | func (pc *PublishController) GetList(ctx iris.Context) { 19 | 20 | request := ctx.Request() 21 | //获取参数 22 | //token := request.FormValue("token") 23 | //userId := cache.RCGet(token).Val() 24 | uid := request.FormValue("user_id") 25 | 26 | //if userId == "" { 27 | // _, err := ctx.JSON(VideoListResponse{ 28 | // StatusCode: 301, 29 | // StatusMsg: "鉴权失败,请检测是否登录", 30 | // VideoLists: []VideoList2{}, 31 | // }) 32 | // if err != nil { 33 | // log.Println(err.Error()) 34 | // } 35 | // return 36 | //} 37 | //获取视频列表 38 | useridINT, err := strconv.Atoi(uid) 39 | if err != nil { 40 | log.Println(err) 41 | } 42 | videoLists := service2.GetVideoListsById(useridINT) 43 | 44 | //不知道为什么videoLists不能为空,等我解决吧 45 | //已解决:videoLists应为是数组,所以是空是[],而不是nil 46 | _, err = ctx.JSON(VideoListResponse{ 47 | StatusCode: 0, 48 | StatusMsg: "成功", 49 | VideoLists: videoLists, 50 | }) 51 | if err != nil { 52 | log.Println(err.Error()) 53 | } 54 | } 55 | 56 | func (pc *PublishController) PostAction(ctx iris.Context) { 57 | service2.Contribution(ctx) 58 | } 59 | -------------------------------------------------------------------------------- /src/controller/relation.go: -------------------------------------------------------------------------------- 1 | package controller 2 | 3 | import ( 4 | "database/sql" 5 | . "douyin/cache" 6 | . "douyin/common" 7 | "douyin/db" 8 | "github.com/go-redis/redis/v8" 9 | "github.com/kataras/iris/v12" 10 | "github.com/kataras/iris/v12/mvc" 11 | "log" 12 | "strconv" 13 | ) 14 | 15 | type actionResponse struct { 16 | StatusCode int `json:"status_code"` 17 | StatusMsg string `json:"status_msg"` 18 | } 19 | 20 | type listResponse struct { 21 | StatusCode string `json:"status_code"` 22 | StatusMsg string `json:"status_msg"` 23 | UserList []User2 `json:"user_list,omitempty"` 24 | } 25 | 26 | type Relation struct { 27 | RelationId int64 28 | FollowerId int64 29 | FollowingId int64 30 | IsDeleted bool 31 | } 32 | type RelationController struct { 33 | } 34 | 35 | func (rc *RelationController) PostAction(context iris.Context) mvc.Result { 36 | token := RCGet(context.URLParam("token")) 37 | if token.Err() == redis.Nil { 38 | return mvc.Response{ 39 | Object: actionResponse{ 40 | StatusCode: 400, 41 | StatusMsg: "登录超时", 42 | }, 43 | } 44 | } 45 | userid := token.Val() 46 | touserid := context.URLParam("to_user_id") 47 | if userid == touserid { 48 | return mvc.Response{ 49 | Object: actionResponse{StatusCode: 100, StatusMsg: "无法关注自己"}, 50 | } 51 | } 52 | actiontype, _ := strconv.Atoi(context.URLParam("action_type")) 53 | 54 | row, err := db.DB.Query("select `isdeleted` from `tb_relation` where `follower_id`=? and `following_id`=?", userid, touserid) 55 | if err != nil { 56 | log.Println("查询关注列表错误") 57 | return mvc.Response{ 58 | Object: actionResponse{ 59 | StatusCode: 500, 60 | StatusMsg: "查询列表错误", 61 | }, 62 | } 63 | } 64 | defer row.Close() //在执行完后关闭游标 65 | 66 | //开启事务 67 | tx, _ := db.DB.Begin() 68 | var result sql.Result 69 | //判断relation表中是否有对于关注关系 70 | exist := row.Next() //exist用于判断记录是否存在 71 | 72 | //TODO 防止关注/取消关注重复操作 73 | if exist { 74 | var curtype bool 75 | row.Scan(&curtype) //如果对应记录存在则匹配对应类型值 76 | chtype := (actiontype == 2) 77 | if curtype == chtype { //修改前后状态无变化 78 | return mvc.Response{ 79 | Object: actionResponse{ 80 | StatusCode: 400, 81 | StatusMsg: "你已经关注/取关该用户,请勿重复操作", 82 | }, 83 | } 84 | } 85 | } 86 | 87 | //根据是否存在记录是否存在决定修改/添加记录 88 | if !exist { 89 | result, _ = db.DB.Exec("insert into `tb_relation`(`follower_id`,`following_id`,`isdeleted`) "+ 90 | "values(?,?,?)", userid, touserid, (actiontype+1)%2) // 2对应true执行关注操作(删除) 1对应true执行取消关注操作(不删除) 91 | } else { 92 | result, _ = db.DB.Exec("update `tb_relation` set isdeleted=? where `follower_id`=? "+ 93 | "and `following_id`=?", (actiontype+1)%2, userid, touserid) 94 | } 95 | 96 | if actiontype == 1 { 97 | db.DB.Exec("update `tb_user` set `follow_count`=`follow_count`+1 where `user_id`=?", userid) //关注数+1 98 | db.DB.Exec("update `tb_user` set `follower_count`=`follower_count`+1 where `user_id`=?", touserid) //粉丝数+1 99 | } else { 100 | db.DB.Exec("update `tb_user` set `follow_count`=`follow_count`-1 where `user_id`=?", userid) //关注数-1 101 | db.DB.Exec("update `tb_user` set `follower_count`=`follower_count`-1 where `user_id`=?", touserid) //粉丝数-1 102 | } 103 | tx.Commit() //提交事务 104 | ar, _ := result.RowsAffected() 105 | var stm string 106 | switch actiontype { 107 | case 1: 108 | { 109 | stm = "关注" 110 | } 111 | case 2: 112 | { 113 | stm = "取消关注" 114 | } 115 | } 116 | log.Println("用户:" + userid + "执行了" + stm + "操作") 117 | if ar != 0 { 118 | return mvc.Response{ 119 | Object: actionResponse{ 120 | StatusCode: 0, 121 | StatusMsg: stm + "成功", 122 | }, 123 | } 124 | } 125 | return mvc.Response{ 126 | Object: actionResponse{ 127 | StatusCode: 501, 128 | StatusMsg: "发生错误,请稍后重试", 129 | }, 130 | } 131 | } 132 | 133 | func (rc *RelationController) GetFollowList(context iris.Context) mvc.Result { 134 | token := RCGet(context.URLParam("token")) 135 | if token.Err() == redis.Nil { 136 | return mvc.Response{ 137 | Object: listResponse{ 138 | StatusCode: "100", 139 | StatusMsg: "登录超时", 140 | }, 141 | } 142 | } 143 | 144 | userid := token.Val() //根据token获取用户id 145 | log.Println("用户(ID):" + userid + " token:" + context.URLParam("token")) 146 | rows, err := db.DB.Query("select `following_id` from `tb_relation` where `follower_id`=? and `isdeleted`=false", userid) //获取关注对象的id 147 | if err != nil { 148 | log.Printf("查询关注列表错误:%v\n", err) 149 | return mvc.Response{ 150 | Object: listResponse{ 151 | StatusCode: "500", 152 | StatusMsg: "查询出错", 153 | }, 154 | } 155 | } 156 | user_list := make([]User2, 0, 50) 157 | for rows.Next() { 158 | var user User2 159 | var id int 160 | err := rows.Scan(&id) 161 | if err != nil { 162 | log.Printf("%v", err) 163 | } 164 | //获取关注用户信息 165 | row := db.DB.QueryRow("select `user_id`,`name`,`follow_count`,`follower_count` from `tb_user` where `user_id`=?", id) 166 | row.Scan(&user.Id, &user.Name, &user.FollowCount, &user.FollowerCount) 167 | user.IsFollow = true //从关注列表中只获取关注对象 168 | user_list = append(user_list, user) 169 | } 170 | defer rows.Close() 171 | return mvc.Response{ 172 | Object: listResponse{ 173 | StatusCode: "0", 174 | StatusMsg: "请求完成", 175 | UserList: user_list, 176 | }, 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /src/controller/user.go: -------------------------------------------------------------------------------- 1 | package controller 2 | 3 | import ( 4 | "douyin/common" 5 | userService "douyin/service" 6 | "douyin/utils" 7 | "github.com/kataras/iris/v12" 8 | "github.com/kataras/iris/v12/mvc" 9 | ) 10 | 11 | type UserController struct { 12 | } 13 | type UserResponse struct { 14 | common.Response 15 | User common.User2 `json:"user"` 16 | } 17 | 18 | func (uc *UserController) PostRegister(ctx iris.Context) mvc.Result { 19 | //参数接受校验 20 | var username = ctx.URLParam("username") 21 | var password = ctx.URLParam("password") 22 | if username == "" || password == "" { 23 | return mvc.Response{ 24 | Object: userService.UserLoginAndRegisterResponse{ 25 | Response: common.Response{StatusCode: 1, StatusMsg: "参数不能为空"}, 26 | }, 27 | } 28 | } 29 | password = utils.MD5WithSalt(password) 30 | var response = userService.Register(username, password) 31 | 32 | return mvc.Response{ 33 | Object: response, 34 | } 35 | } 36 | 37 | func (uc *UserController) PostLogin(ctx iris.Context) mvc.Response { 38 | //参数获取与校验 39 | var username = ctx.URLParam("username") 40 | var password = ctx.URLParam("password") 41 | if username == "" || password == "" { 42 | return mvc.Response{ 43 | Object: userService.UserLoginAndRegisterResponse{ 44 | Response: common.Response{StatusCode: 1, StatusMsg: "参数不能为空"}, 45 | }, 46 | } 47 | } 48 | response := userService.Login(username, utils.MD5WithSalt(password)) 49 | 50 | return mvc.Response{ 51 | Object: response, 52 | } 53 | } 54 | 55 | func (uc *UserController) Get(ctx iris.Context) mvc.Response { 56 | var userId int64 = ctx.URLParamInt64Default("user_id", -1) 57 | if userId == -1 { 58 | return mvc.Response{ 59 | Object: userService.UserLoginAndRegisterResponse{ 60 | Response: common.Response{StatusCode: 1, StatusMsg: "参数不能为空"}, 61 | }, 62 | } 63 | } 64 | user := userService.Info(userId) 65 | return mvc.Response{ 66 | Object: UserResponse{ 67 | Response: common.Response{StatusCode: 0, StatusMsg: ""}, 68 | User: user, 69 | }, 70 | } 71 | } 72 | 73 | //func (uc *UserController) BeforeActivation(a mvc.BeforeActivation) { 74 | // a.Handle("POST", "/login/", "PostLogin") 75 | // a.Handle("POST", "/register/", "PostRegister") 76 | //} 77 | -------------------------------------------------------------------------------- /src/db/favorite_dao.go: -------------------------------------------------------------------------------- 1 | package db 2 | 3 | import "database/sql" 4 | 5 | // RecordExists 查询tb_favorite表判断是否存在一条记录 6 | func RecordExists(userId, videoId int64) bool { 7 | row := DB.QueryRow("select favorite_id from tb_favorite where user_id = ? and video_id = ?", userId, videoId) 8 | favoriteId := -1 9 | row.Scan(&favoriteId) 10 | if favoriteId > 0 { 11 | return true 12 | } 13 | return false 14 | } 15 | 16 | //更新tb_favorite某条记录的is_deleted字段 17 | func updateIsDeletedBy(tx sql.Tx, userId, videoId int64, isDeleted bool) (sql.Tx, error) { 18 | _, err := tx.Exec( 19 | "update tb_favorite set is_deleted = ? where user_id = ? and video_id = ?", 20 | isDeleted, userId, videoId) 21 | if err != nil { 22 | return tx, err 23 | } 24 | return tx, err 25 | } 26 | -------------------------------------------------------------------------------- /src/db/feed_dao.go: -------------------------------------------------------------------------------- 1 | package db 2 | 3 | import ( 4 | "douyin/config" 5 | "os" 6 | "strconv" 7 | "time" 8 | 9 | "gorm.io/driver/mysql" 10 | "gorm.io/gorm" 11 | "gorm.io/gorm/schema" 12 | ) 13 | 14 | var ( 15 | db = NewDB() 16 | dns = config.AppConfig.GetString("datasource.dataSourceName") + "?parseTime=true" //gorm框架 连接mysql 17 | ) 18 | 19 | type FeedDao struct { 20 | } 21 | 22 | type TbUser struct { 23 | UserId int64 `gorm:"primaryKey"` 24 | Name string `gorm:"default:"` 25 | FollowCount int64 `gorm:"default:"` 26 | FollowerCount int64 `gorm:"default:"` 27 | IsFollow int8 `gorm:"default:"` 28 | Password string `gorm:"default:"` 29 | } 30 | 31 | type TbRelation struct { 32 | RelationId int64 `gorm:"primaryKey"` 33 | FollowerId string `gorm:"default:"` 34 | FollowingId int64 `gorm:"default:"` 35 | Isdeleted int8 `gorm:"default:"` 36 | } 37 | 38 | type TbFavorite struct { 39 | FavoriteId int64 `gorm:"primaryKey"` 40 | UserId int64 `gorm:"default:"` 41 | VideoId int64 `gorm:"default:"` 42 | IsDeleted int8 `gorm:"default:"` 43 | } 44 | 45 | type TbVideo struct { 46 | VideoId int64 `gorm:"primaryKey"` 47 | UserId int64 48 | PlayUrl string `gorm:"default:"` 49 | CoverUrl string `gorm:"default:"` 50 | FavoriteCount int64 `gorm:"default:"` 51 | CommentCount int64 `gorm:"default:"` 52 | Title string `gorm:"default:"` 53 | CreateDate time.Time `gorm:"default:"` 54 | UpdateDate time.Time `gorm:"default:"` 55 | TbUser TbUser `gorm:"foreignKey:UserId;references:UserId"` 56 | } 57 | 58 | func (TbVideo) TableName() string { 59 | return "tb_video" 60 | } 61 | 62 | func (TbUser) TableName() string { 63 | return "tb_user" 64 | } 65 | func NewDB() *gorm.DB { 66 | open, err := gorm.Open(mysql.New(mysql.Config{ 67 | DSN: dns, 68 | }), &gorm.Config{ 69 | NamingStrategy: schema.NamingStrategy{ 70 | SingularTable: true, 71 | }, 72 | }) 73 | if err != nil { 74 | println("create gorm filed,err: " + err.Error()) 75 | os.Exit(1) 76 | } 77 | return open 78 | } 79 | 80 | // SelectVideoByUpdate 根据时间戳获取最多number条视频记录 81 | func (v *FeedDao) SelectVideoByUpdate(unix string, number int) ([]TbVideo, error) { 82 | var tb []TbVideo 83 | if len(unix) > 0 { 84 | // 时间戳转换为时间 85 | parseInt, err := strconv.ParseInt(unix, 10, 64) 86 | if err != nil { 87 | return nil, err 88 | } 89 | date := time.UnixMilli(parseInt).Format("2006-01-02 15:04:05") 90 | db.Preload("TbUser").Limit(number).Where("update_date < ?", date).Order("update_date DESC").Find(&tb) 91 | return tb, nil 92 | } 93 | db.Preload("TbUser").Limit(number).Order("update_date DESC").Find(&tb) 94 | return tb, nil 95 | } 96 | 97 | func (v *FeedDao) InsertVideo(table *TbVideo) bool { 98 | db.Create(table) 99 | return true 100 | } 101 | 102 | func (v *FeedDao) SelectUserById(id int64) *TbUser { 103 | user := &TbUser{} 104 | db.Where("user_id = ?", id).First(user) 105 | return user 106 | } 107 | 108 | // 根据用户名和视频Id查询是否有点赞 109 | func (v *FeedDao) IsFavorite(userID int64, videoID int64) bool { 110 | var count int64 111 | db.Model(&TbFavorite{}).Where("`user_id` = ? and `video_id` = ? and `is_deleted` = 0", userID, videoID).Count(&count) 112 | return count > 0 113 | } 114 | 115 | // 根据用户名id查询是否有关注 true 表示关注 116 | func (v *FeedDao) IsFollwer(originUserID int64, targetUserID int64) bool { 117 | var size int64 118 | db.Model(&TbRelation{}).Where("`follower_id` = ? and `following_id` = ? and `isdeleted` = 0", originUserID, targetUserID).Count(&size) 119 | return size > 0 120 | } 121 | -------------------------------------------------------------------------------- /src/db/feed_dao_test.go: -------------------------------------------------------------------------------- 1 | package db 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | "testing" 7 | "time" 8 | ) 9 | 10 | func TestFeedDao_SelectUserById(t *testing.T) { 11 | dao := &FeedDao{} 12 | id := dao.SelectUserById(3) 13 | fmt.Println(id) 14 | } 15 | 16 | func TestFeedDao_SelectVideoByUpdate(t *testing.T) { 17 | dao := &FeedDao{} 18 | location, err := time.LoadLocation("Local") 19 | inLocation, err := time.ParseInLocation("2006-01-02 15:04:05", "2022-05-22 18:53:42", location) 20 | formatInt := strconv.FormatInt(inLocation.UnixMilli(), 10) 21 | fmt.Println(formatInt) 22 | update, err := dao.SelectVideoByUpdate("", 2) 23 | if err != nil { 24 | return 25 | } 26 | for i, d := range update { 27 | fmt.Println(i, "--->", d) 28 | } 29 | } 30 | 31 | func TestFeedDao_InsertVideo(t *testing.T) { 32 | //db := &FeedDao{} 33 | //db.InsertVideo(&TbVideo{ 34 | // UserId: 30, 35 | // PlayUrl: "head.Filename", 36 | //}) 37 | } 38 | 39 | func TestFeedDao_PreLoad(t *testing.T) { 40 | var testTable []TbVideo 41 | db.Debug().Preload("TbUser").Find(&testTable) 42 | fmt.Println(testTable) 43 | } 44 | -------------------------------------------------------------------------------- /src/db/gorm_mysql.go: -------------------------------------------------------------------------------- 1 | package db 2 | 3 | import ( 4 | . "douyin/config" 5 | "gorm.io/driver/mysql" 6 | "gorm.io/gorm" 7 | "log" 8 | ) 9 | 10 | var gormDb *gorm.DB 11 | var cnt = 0 12 | 13 | func GetDBConnect() (conn *gorm.DB) { 14 | if gormDb != nil { 15 | return gormDb 16 | } 17 | 18 | cnt++ 19 | dataSourceName := AppConfig.Get("datasource.dataSourceName").(string) 20 | Db, err := gorm.Open(mysql.Open(dataSourceName)) 21 | log.Printf("数据库为mysql , 数据库链接为%s", dataSourceName) 22 | if err != nil { 23 | panic(err) 24 | } 25 | gormDb = Db 26 | return gormDb 27 | } 28 | -------------------------------------------------------------------------------- /src/db/mysql.go: -------------------------------------------------------------------------------- 1 | package db 2 | 3 | import ( 4 | "database/sql" 5 | . "douyin/config" 6 | _ "github.com/go-sql-driver/mysql" 7 | "log" 8 | ) 9 | 10 | var DB *sql.DB 11 | 12 | func init() { 13 | 14 | driverName := AppConfig.Get("datasource.driverName").(string) 15 | dataSourceName := AppConfig.Get("datasource.dataSourceName").(string) 16 | //打印文件读取出来的内容: 17 | log.Printf("数据库为 %s, 数据库链接为%s", driverName, dataSourceName) 18 | db, err := sql.Open(driverName, dataSourceName+"?charset=utf8&parseTime=True") 19 | if err != nil { 20 | panic(err) 21 | } 22 | if db.Ping() != nil { 23 | panic("数据库连接错误") 24 | } 25 | DB = db 26 | } 27 | -------------------------------------------------------------------------------- /src/db/mysql_connection_test.go: -------------------------------------------------------------------------------- 1 | package db 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "log" 7 | "testing" 8 | ) 9 | 10 | type User struct { 11 | Id int64 `json:"id,omitempty"` 12 | Name string `json:"name,omitempty"` 13 | FollowCount int64 `json:"follow_count,omitempty"` 14 | FollowerCount int64 `json:"follower_count,omitempty"` 15 | IsFollow bool `json:"is_follow,omitempty"` 16 | } 17 | 18 | var user User 19 | 20 | func Test_init(t *testing.T) { 21 | 22 | } 23 | 24 | func TestMysqlConnection(t *testing.T) { 25 | 26 | log.Println("连接数据库") 27 | row := DB.QueryRow("select * from tb_user") 28 | rows, err := DB.Query("select * from tb_user") 29 | defer rows.Close() 30 | if err != nil { 31 | fmt.Printf("insert data error: %v\n", err) 32 | } 33 | 34 | var ll string 35 | for rows.Next() { 36 | e := rows.Scan(&user.Id, &user.Name, &user.FollowCount, &user.FollowerCount, &user.IsFollow, &ll) 37 | if e == nil { 38 | buf, err := json.Marshal(user) 39 | if err != nil { 40 | panic(err) 41 | } 42 | log.Println(string(buf)) 43 | log.Println(user.Id, user.Name, user.FollowerCount, user.IsFollow, user.IsFollow) 44 | } 45 | } 46 | log.Println("连接完成") 47 | var hh string 48 | row.Scan(&hh, &hh, &hh, &hh, &hh, &hh) 49 | log.Println("开始") 50 | log.Println(hh) 51 | log.Println("结束") 52 | 53 | } 54 | -------------------------------------------------------------------------------- /src/db/user_dao.go: -------------------------------------------------------------------------------- 1 | package db 2 | 3 | import ( 4 | "douyin/common" 5 | ) 6 | 7 | func SelectUserById(userId int64) (user common.User) { 8 | row := DB.QueryRow("select user_id, name, follow_count, follower_count, is_follow, password from tb_user where user_id = ?", userId) 9 | row.Scan(&user.Id, &user.Name, &user.FollowCount, &user.FollowerCount, &user.IsFollow, &user.Password) 10 | return user 11 | } 12 | 13 | func SelectUserIdBy(username string) (userId int64, err error) { 14 | row := DB.QueryRow("select user_id from tb_user where name = ?", username) 15 | err = row.Scan(&userId) 16 | if err != nil { 17 | return -1, err 18 | } 19 | return userId, err 20 | } 21 | 22 | func InsertUser(user common.User) (insertId int64, err error) { 23 | result, err := DB.Exec( 24 | "insert into tb_user(name,password) values(?,?)", 25 | user.Name, user.Password) 26 | if err != nil { 27 | return -1, err 28 | } 29 | user.Id, err = result.LastInsertId() //新增数据的ID 30 | if err != nil { 31 | return -1, err 32 | } 33 | return user.Id, err 34 | } 35 | 36 | func SelectUserIdNamePasswordBy(username string) (user common.User) { 37 | rows := DB.QueryRow( 38 | "select user_id,name,password from tb_user where name = ?", 39 | username) 40 | rows.Scan(&user.Id, &user.Name, &user.Password) 41 | return user 42 | } 43 | 44 | func SelectUserInfoBy(userId int64) (user common.User) { 45 | row := DB.QueryRow( 46 | "select user_id,name,follow_count,follower_count,is_follow from tb_user where user_id = ?", userId) 47 | row.Scan(&user.Id, &user.Name, &user.FollowCount, &user.FollowerCount, &user.IsFollow) 48 | return user 49 | } 50 | -------------------------------------------------------------------------------- /src/main/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // 抖音项目 4 | // 三带一队: 5 | // @陈鹤中 @张博思 @简懿豪 @谭盟 @徐政 @杨彬烜 @解城文 @梁明栩 6 | // 使用iris框架作为web框架,部分接口使用gorm操作数据库,部分接口使用原生接口操作数据库 7 | // 使用viper作为项目application.yaml配置管理,数据存储使用mysql和redis 8 | // 登录,注册接口:谭盟 9 | // 视频流接口和投稿接口 简懿豪 10 | // 用户信息和粉丝列表 张博思 11 | // 发布列表 梁明栩 12 | // 赞操作和点赞列表 杨彬烜 13 | // 评论操作和评论列表 徐政 14 | // 关注操作和关注列表 陈鹤中 15 | import ( 16 | . "douyin/src/config" 17 | . "douyin/src/controller" 18 | "github.com/kataras/iris/v12" 19 | "github.com/kataras/iris/v12/mvc" 20 | "strconv" 21 | ) 22 | 23 | func newApp() *iris.Application { 24 | app := iris.New() 25 | //配置404返回内容 26 | app.OnErrorCode(iris.StatusNotFound, notFound) 27 | //配置路由 28 | mvc.Configure(app.Party("/douyin/user"), func(app *mvc.Application) { 29 | app.Handle(new(UserController)) 30 | }) 31 | mvc.Configure(app.Party("/douyin/favorite"), func(app *mvc.Application) { 32 | app.Handle(new(FavoriteController)) 33 | }) 34 | mvc.Configure(app.Party("/douyin/comment"), func(app *mvc.Application) { 35 | app.Handle(new(CommentController)) 36 | }) 37 | mvc.Configure(app.Party("/douyin/publish"), func(app *mvc.Application) { 38 | app.Handle(new(PublishController)) 39 | }) 40 | mvc.Configure(app.Party("/douyin/relation"), func(app *mvc.Application) { 41 | app.Handle(new(RelationController)) 42 | }) 43 | mvc.Configure(app.Party("/douyin/feed"), func(app *mvc.Application) { 44 | app.Handle(new(FeedController)) 45 | }) 46 | return app 47 | } 48 | 49 | func main() { 50 | addr := strconv.Itoa(AppConfig.GetInt("server.port")) 51 | app := newApp() 52 | //每一次请求都打印请求路径,iris使用了责任链模式,很类似servlet里面的filter 53 | app.UseGlobal(before) 54 | //监听我们的服务端口,iris.WithoutPathCorrectionRedirection这个选项用于路径匹配,例如 55 | // /douyin/user/register 和 /douyin/user/register/ 这两个路径完全不同但却需要同一个请求处理器 56 | // 咱们的客户端发送的请求末尾都会带上一个分号,若未开启这个选项,请求会fail,都是教训啊( ̄、 ̄) 57 | app.Run(iris.Addr(":"+addr), iris.WithCharset("UTF-8"), iris.WithoutPathCorrectionRedirection) 58 | } 59 | 60 | func before(ctx iris.Context) { 61 | iris.New().Logger().Info(ctx.Path()) 62 | ctx.Next() 63 | } 64 | 65 | func notFound(ctx iris.Context) { 66 | code := ctx.GetStatusCode() 67 | msg := "404 Not Found" 68 | ctx.JSON(iris.Map{ 69 | "Message": msg, 70 | "Code": code, 71 | }) 72 | } 73 | -------------------------------------------------------------------------------- /src/main/main_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/kataras/iris/v12/httptest" 5 | "testing" 6 | ) 7 | 8 | func TestNewApp(t *testing.T) { 9 | app := newApp() 10 | 11 | e := httptest.New(t, app) 12 | 13 | // redirects to /admin without basic auth 14 | e.GET("/").Expect().Status(httptest.StatusNotFound) 15 | 16 | // without basic auth 17 | e.GET("/admin").Expect().Status(httptest.StatusUnauthorized) 18 | 19 | // with valid basic auth 20 | e.GET("/admin").WithBasicAuth("myusername", "mypassword").Expect(). 21 | Status(httptest.StatusOK).Body().Equal("/admin myusername:mypassword") 22 | e.GET("/admin/profile").WithBasicAuth("myusername", "mypassword").Expect(). 23 | Status(httptest.StatusOK).Body().Equal("/admin/profile myusername:mypassword") 24 | e.GET("/admin/settings").WithBasicAuth("myusername", "mypassword").Expect(). 25 | Status(httptest.StatusOK).Body().Equal("/admin/settings myusername:mypassword") 26 | 27 | // with invalid basic auth 28 | e.GET("/admin/settings").WithBasicAuth("invalidusername", "invalidpassword"). 29 | Expect().Status(httptest.StatusUnauthorized) 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/service/action_service.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "bytes" 5 | "context" 6 | "douyin/cache" 7 | config2 "douyin/config" 8 | "douyin/db" 9 | "fmt" 10 | "github.com/disintegration/imaging" 11 | "github.com/kataras/iris/v12" 12 | ffmpeg_go "github.com/u2takey/ffmpeg-go" 13 | "io" 14 | "log" 15 | "mime/multipart" 16 | "net/http" 17 | "os" 18 | "path/filepath" 19 | "strconv" 20 | "strings" 21 | "time" 22 | ) 23 | 24 | var ( 25 | FilePath = config2.AppConfig.GetString("video.filePath") 26 | ) 27 | 28 | // Contribution 视频投稿 29 | func Contribution(ctx iris.Context) { 30 | r := ctx.Request() 31 | 32 | text := r.FormValue("token") 33 | 34 | // 鉴权 35 | rcGet := cache.RCGet(text) 36 | if rcGet == nil { 37 | ctx.JSON(map[string]interface{}{ 38 | "status_code": 4, 39 | "status_msg": "该用户没用权限", 40 | }) 41 | return 42 | } 43 | 44 | title := r.FormValue("title") 45 | 46 | file, head, err := r.FormFile("data") 47 | if err != nil { 48 | ctx.JSON(map[string]interface{}{ 49 | "status_code": 1, 50 | "status_msg": "没有找到data参数,err: " + err.Error(), 51 | }) 52 | return 53 | } 54 | defer file.Close() 55 | 56 | if b, _ := isHasDir(FilePath); !b { 57 | err = os.MkdirAll(FilePath, 0777) 58 | if err != nil { 59 | ctx.JSON(map[string]interface{}{ 60 | "status_code": 5, 61 | "status_msg": "create folder failed,err: " + err.Error(), 62 | }) 63 | return 64 | } 65 | } 66 | 67 | fileName := strconv.FormatInt(time.Now().UnixNano(), 10) + head.Filename 68 | 69 | fw, err := os.Create(FilePath + fileName) 70 | 71 | if err != nil { 72 | ctx.JSON(map[string]interface{}{ 73 | "status_code": 2, 74 | "status_msg": "create file failed,err: " + err.Error(), 75 | }) 76 | return 77 | } 78 | defer fw.Close() 79 | _, err = io.Copy(fw, file) 80 | if err != nil { 81 | ctx.JSON(map[string]interface{}{ 82 | "status_code": 3, 83 | "status_msg": "copy file failed,err: " + err.Error(), 84 | }) 85 | return 86 | } 87 | 88 | //将文件存储到桶当中 89 | _, err = config2.Cos.Object.PutFromFile(context.Background(), "/videos/"+fileName, FilePath+fileName, nil) 90 | if err != nil { 91 | panic(err) 92 | } 93 | ctx.JSON(map[string]interface{}{ 94 | "status_code": 0, 95 | "status_msg": "save file success", 96 | }) 97 | 98 | // 保存封面 99 | fileImage := GetSnapshot(FilePath+fileName, FilePath+strings.Trim(fileName, ".mp4"), 60) 100 | 101 | // 将信息插入到数据库 102 | dao := &db.FeedDao{} 103 | id, err := rcGet.Int64() 104 | if err != nil { 105 | panic("get id failed,err: " + err.Error()) 106 | } 107 | 108 | dao.InsertVideo(&db.TbVideo{ 109 | UserId: id, 110 | PlayUrl: fileName, 111 | CoverUrl: fileImage, 112 | Title: title, 113 | }) 114 | } 115 | 116 | func isHasDir(path string) (bool, error) { 117 | stat, err := os.Stat(path) 118 | if err != nil { 119 | return false, err 120 | } 121 | return stat.IsDir(), nil 122 | } 123 | 124 | func GetSnapshot(videoPath, snapshotPath string, frameNum int) (snapshotName string) { 125 | buf := bytes.NewBuffer(nil) 126 | err := ffmpeg_go.Input(videoPath). 127 | Filter("select", ffmpeg_go.Args{fmt.Sprintf("gte(n,%d)", frameNum)}). 128 | Output("pipe:", ffmpeg_go.KwArgs{"vframes": 1, "format": "image2", "vcodec": "mjpeg"}). 129 | WithOutput(buf, os.Stdout). 130 | Run() 131 | if err != nil { 132 | log.Fatal("生成缩略图失败:", err) 133 | } 134 | 135 | img, err := imaging.Decode(buf) 136 | if err != nil { 137 | log.Fatal("生成缩略图失败:", err) 138 | } 139 | 140 | err = imaging.Save(img, snapshotPath+".jpeg") 141 | if err != nil { 142 | log.Fatal("生成缩略图失败:", err) 143 | } 144 | 145 | // 成功则返回生成的缩略图名 146 | names := strings.Split(snapshotPath, "/") 147 | snapshotName = names[len(names)-1] + ".jpeg" 148 | 149 | //将文件存储到桶当中 150 | _, err = config2.Cos.Object.PutFromFile(context.Background(), "/pictures/"+snapshotName, snapshotPath+".jpeg", nil) 151 | if err != nil { 152 | panic(err) 153 | } 154 | return 155 | } 156 | func NewUploadRequest1(url string, params map[string]string, path string) (*http.Request, error) { 157 | 158 | file, err := os.Open(path) 159 | if err != nil { 160 | return nil, err 161 | } 162 | defer file.Close() 163 | 164 | // 实例化multipart 165 | body := &bytes.Buffer{} 166 | writer := multipart.NewWriter(body) 167 | 168 | // 创建multipart 文件字段 169 | part, err := writer.CreateFormFile("data", filepath.Base(path)) 170 | if err != nil { 171 | return nil, err 172 | } 173 | // 写入文件数据到multipart 174 | _, err = io.Copy(part, file) 175 | //将额外参数也写入到multipart 176 | for key, val := range params { 177 | _ = writer.WriteField(key, val) 178 | } 179 | err = writer.Close() 180 | if err != nil { 181 | return nil, err 182 | } 183 | 184 | //创建请求 185 | req, err := http.NewRequest("POST", url, body) 186 | if err != nil { 187 | return nil, err 188 | } 189 | //不要忘记加上writer.FormDataContentType(), 190 | //该值等于content-type :multipart/form-data; boundary=xxxxx 191 | req.Header.Add("Content-Type", writer.FormDataContentType()) 192 | return req, nil 193 | } 194 | -------------------------------------------------------------------------------- /src/service/favorite_service.go: -------------------------------------------------------------------------------- 1 | package service 2 | -------------------------------------------------------------------------------- /src/service/feed_service.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "douyin/cache" 5 | "douyin/config" 6 | "douyin/db" 7 | "time" 8 | ) 9 | 10 | var ( 11 | videoUrl = config.AppConfig.GetString("video.videoUrl") 12 | imageUrl = config.AppConfig.GetString("video.imageUrl") 13 | dao = &db.FeedDao{} 14 | ) 15 | 16 | type FeedData struct { 17 | StatusCode int `json:"status_code"` 18 | StatusMsg string `json:"status_msg"` 19 | NextTime int64 `json:"next_time"` 20 | VideoList []VideoList `json:"video_list"` 21 | } 22 | 23 | type Author struct { 24 | ID int64 `json:"id"` 25 | Name string `json:"name"` 26 | FollowCount int64 `json:"follow_count"` 27 | FollowerCount int64 `json:"follower_count"` 28 | IsFollow bool `json:"is_follow"` 29 | } 30 | 31 | type VideoList struct { 32 | ID int64 `json:"id"` 33 | Author Author `json:"author"` 34 | PlayUrl string `json:"play_url"` 35 | CoverUrl string `json:"cover_url"` 36 | FavoriteCount int64 `json:"favorite_count"` 37 | CommentCount int64 `json:"comment_count"` 38 | IsFavorite bool `json:"is_favorite"` 39 | Title string `json:"title"` 40 | } 41 | 42 | func GetFeed(latestTime string, token string) *FeedData { 43 | data := &FeedData{} 44 | var userId int64 45 | userId = -1 46 | 47 | videos, err := dao.SelectVideoByUpdate(latestTime, 30) 48 | if err != nil { 49 | return &FeedData{ 50 | StatusCode: 1, 51 | StatusMsg: "查找视频失败,err: " + err.Error(), 52 | } 53 | } 54 | 55 | if len(token) > 0 { 56 | get := cache.RCGet(token) 57 | userId, err = get.Int64() 58 | } 59 | 60 | for _, v := range videos { 61 | data.VideoList = append(data.VideoList, VideoList{ 62 | ID: v.VideoId, 63 | Author: Author{ 64 | ID: v.TbUser.UserId, 65 | Name: v.TbUser.Name, 66 | FollowCount: v.TbUser.FollowCount, 67 | FollowerCount: v.TbUser.FollowerCount, 68 | IsFollow: isFollow(userId, v.TbUser.UserId), 69 | }, 70 | PlayUrl: getVideoUrl(v.PlayUrl), 71 | CoverUrl: getImageUrl(v.CoverUrl), 72 | FavoriteCount: v.FavoriteCount, 73 | CommentCount: v.CommentCount, 74 | Title: v.Title, 75 | IsFavorite: isFavorite(userId, v.VideoId), 76 | }) 77 | } 78 | // 已经没有视频了 从头继续播放 79 | if len(videos) == 0 { 80 | data.NextTime = time.Now().UnixMilli() 81 | } else { 82 | data.NextTime = videos[len(videos)-1].UpdateDate.UnixMilli() 83 | } 84 | 85 | data.StatusCode = 0 86 | return data 87 | } 88 | 89 | func getVideoUrl(videoName string) string { 90 | return videoUrl + videoName 91 | } 92 | 93 | func getImageUrl(imageName string) string { 94 | return imageUrl + imageName 95 | } 96 | 97 | func isFollow(uId int64, vId int64) bool { 98 | if uId == -1 { 99 | return false 100 | } 101 | // 查看该uId用户是否关注了该视频作者 102 | return dao.IsFollwer(uId, vId) 103 | } 104 | 105 | func isFavorite(uId int64, vId int64) bool { 106 | if uId == -1 { 107 | return false 108 | } 109 | // TODO 查看该uId用户是否点赞了vId该视频 110 | return dao.IsFavorite(uId, vId) 111 | } 112 | -------------------------------------------------------------------------------- /src/service/feed_service_test.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestGetFeed(t *testing.T) { 8 | 9 | } 10 | -------------------------------------------------------------------------------- /src/service/list_service.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | . "douyin/common" 5 | db2 "douyin/db" 6 | ) 7 | 8 | const ( 9 | TableNameFavorite = "tb_favorite" 10 | ) 11 | 12 | // Favorite mapped from table 13 | type Favorite struct { 14 | FavoriteID int64 `gorm:"column:favorite_id;primaryKey;autoIncrement:true"` 15 | Username string `gorm:"column:username"` 16 | VideoID int64 `gorm:"column:video_id"` 17 | Isdeleted int32 `gorm:"column:isdeleted"` 18 | } 19 | 20 | // TableName Favorite's table name 21 | func (*Favorite) TableName() string { 22 | return TableNameFavorite 23 | } 24 | 25 | /* 26 | 获取视频列表 27 | */ 28 | func GetVideoListsById(id int) []VideoList2 { 29 | 30 | var videos1 []VideoList1 31 | var user User2 32 | 33 | db := db2.GetDBConnect() 34 | result := db.Select("video_id", "user_id", "play_url", "cover_url", "favorite_count", "comment_count").Where("user_id = ?", id).Find(&videos1) 35 | db.Model(User{}).Where("user_id = ?", id).First(&user) 36 | n := result.RowsAffected 37 | videoS := make([]VideoList2, n) 38 | 39 | var i int64 40 | for i = 0; i < n; i++ { 41 | videoS[i].Id = videos1[i].Id 42 | //拼接得到视频地址 43 | //使用了feedService.go中的接口 44 | videoS[i].PlayURL = getVideoUrl(videos1[i].PlayURL) 45 | videoS[i].CommentCount = videos1[i].CommentCount 46 | videoS[i].FavoriteCount = videos1[i].FavoriteCount 47 | //我也不知道封面放在哪? 48 | videoS[i].CoverURL = getImageUrl(videos1[i].CoverURL) 49 | videoS[i].Author = user 50 | 51 | var favorite Favorite 52 | cnt := db.Where("user_id = ?", user.Id).Where("video_id = ?", videoS[i].Id).First(&favorite) 53 | if cnt.RowsAffected > 0 && favorite.Isdeleted != 1 { 54 | videoS[i].IsFavorite = true 55 | } else { 56 | videoS[i].IsFavorite = false 57 | } 58 | videoS[i].Title = videos1[i].Title 59 | } 60 | return videoS 61 | } 62 | -------------------------------------------------------------------------------- /src/service/user_service.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | // 用户注册、登录、获取用户信息逻辑 4 | // 登录状态由redis保持,当用户注册或登录后,将用户名和盐值MD5后作为key存入redis,value为用户id 5 | // 该key的有效期为30分钟,用户每进行一次请求都会续期 6 | // 常用的用户鉴权方法还有jwt,但简单的redis实现也可满足需求 7 | // 登录注册传入的password都是经过加密的,以保证安全性 8 | // author: 谭盟,张博思 9 | import ( 10 | . "douyin/cache" 11 | . "douyin/common" 12 | . "douyin/db" 13 | "douyin/utils" 14 | "time" 15 | ) 16 | 17 | type UserLoginAndRegisterResponse struct { 18 | Response 19 | UserId int64 `json:"user_id,omitempty"` 20 | Token string `json:"token"` 21 | } 22 | 23 | func Register(username string, password string) UserLoginAndRegisterResponse { 24 | var userInDB User 25 | userInDB.Id = 0 26 | userInDB.Name = username 27 | //step1:判断用户名是否存在 28 | row := DB.QueryRow("select user_id from tb_user where name = ?", username) 29 | row.Scan(&userInDB.Id) 30 | userInDB.Password = password 31 | if userInDB.Exists() { 32 | return UserLoginAndRegisterResponse{ 33 | Response: Response{StatusCode: 1, StatusMsg: "User already exist"}, 34 | } 35 | } else { 36 | //step2:插入新记录 37 | result, err := DB.Exec( 38 | "insert into tb_user(name,password) values(?,?)", 39 | userInDB.Name, userInDB.Password) 40 | if err != nil { 41 | panic("新增数据错误") 42 | } 43 | userInDB.Id, err = result.LastInsertId() //新增数据的ID 44 | if err != nil { 45 | panic("获取新增数据ID错误") 46 | } 47 | //step3:保存登录状态信息 48 | token := utils.MD5WithSalt(userInDB.Name) 49 | RCSet(token, userInDB.Id, 30*time.Minute) 50 | 51 | return UserLoginAndRegisterResponse{ 52 | Response: Response{StatusCode: 0}, 53 | UserId: userInDB.Id, 54 | Token: token, 55 | } 56 | } 57 | } 58 | 59 | func Login(username string, password string) UserLoginAndRegisterResponse { 60 | //查询数据库 61 | rows := DB.QueryRow( 62 | "select user_id,name,password from tb_user where name = ?", 63 | username) 64 | var userFormDB = User{} 65 | rows.Scan(&userFormDB.Id, &userFormDB.Name, &userFormDB.Password) 66 | 67 | if userFormDB.IsCorrect(password) { 68 | token := utils.MD5WithSalt(username) 69 | RCSet(token, userFormDB.Id, 30*time.Minute) 70 | return UserLoginAndRegisterResponse{ 71 | Response: Response{StatusCode: 0}, 72 | UserId: userFormDB.Id, 73 | Token: token, 74 | } 75 | } else { 76 | return UserLoginAndRegisterResponse{ 77 | Response: Response{StatusCode: 1, StatusMsg: "用户名或密码错误"}, 78 | } 79 | } 80 | } 81 | 82 | func Info(userId int64) User2 { 83 | var user User2 84 | row := DB.QueryRow( 85 | "select user_id,name,follow_count,follower_count,is_follow from tb_user where user_id = ?", userId) 86 | row.Scan(&user.Id, &user.Name, &user.FollowCount, &user.FollowerCount, &user.IsFollow) 87 | return user 88 | } 89 | -------------------------------------------------------------------------------- /src/service/user_service_test.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import "testing" 4 | 5 | func TestUser(t *testing.T) { 6 | var name = "'12345';INSERT INTO tb_user(`name`) VALUES('sql注入')" 7 | Login(name, "") 8 | } 9 | -------------------------------------------------------------------------------- /src/utils/util.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "crypto/md5" 5 | "encoding/hex" 6 | ) 7 | 8 | func MD5(s string) string { 9 | sum := md5.Sum([]byte(s)) 10 | return hex.EncodeToString(sum[:]) 11 | } 12 | 13 | func MD5WithSalt(s string) string { 14 | sum := md5.Sum([]byte(s + "sandaiyidui")) 15 | return hex.EncodeToString(sum[:]) 16 | } 17 | --------------------------------------------------------------------------------