wangshan 5 kuukautta sitten
vanhempi
commit
4d8e856f77
9 muutettua tiedostoa jossa 510 lisäystä ja 6 poistoa
  1. 88 0
      captcha/cache/cache.go
  2. 166 0
      captcha/captcha.go
  3. 61 0
      captcha/check.go
  4. 39 0
      captcha/check_test.go
  5. 55 0
      captcha/helper/helper.go
  6. 48 0
      captcha/helper/load.go
  7. 19 0
      captcha/util.go
  8. 5 3
      go.mod
  9. 29 3
      go.sum

+ 88 - 0
captcha/cache/cache.go

@@ -0,0 +1,88 @@
+package cache
+
+import (
+	"sync"
+	"time"
+)
+
+// ====================================================================================
+//////////////////////////////////////////////////////////////////////////////////////
+// @TODO: In production environments, it is recommended to use redis as a cache
+//////////////////////////////////////////////////////////////////////////////////////
+// ====================================================================================
+
+// cachedata
+type cachedata = struct {
+	data     []byte
+	createAt time.Time
+}
+
+var mux sync.RWMutex
+
+var cachemaps = make(map[string]*cachedata)
+
+// WriteCache .
+func WriteCache(key string, data []byte) {
+	mux.Lock()
+	defer mux.Unlock()
+	cachemaps[key] = &cachedata{
+		createAt: time.Now(),
+		data:     data,
+	}
+}
+
+// ReadCache .
+func ReadCache(key string) []byte {
+	mux.RLock()
+	defer mux.RUnlock()
+	if cd, ok := cachemaps[key]; ok {
+		return cd.data
+	}
+
+	return []byte{}
+}
+
+// ClearCache .
+func ClearCache(key string) {
+	mux.Lock()
+	defer mux.Unlock()
+	delete(cachemaps, key)
+}
+
+// ClearCaches .
+func ClearCaches(keys []string) {
+	if len(keys) == 0 {
+		return
+	}
+
+	mux.Lock()
+	defer mux.Unlock()
+
+	for _, key := range keys {
+		delete(cachemaps, key)
+	}
+}
+
+// RunTimedTask .
+func RunTimedTask() {
+	ticker := time.NewTicker(time.Minute * 5)
+	go func() {
+		for range ticker.C {
+			checkCacheOvertimeFile()
+		}
+	}()
+}
+
+func checkCacheOvertimeFile() {
+	mux.RLock()
+	var keys = make([]string, 0)
+	for key, data := range cachemaps {
+		ex := time.Now().Unix() - data.createAt.Unix()
+		if ex > (60 * 30) {
+			keys = append(keys, key)
+		}
+	}
+	mux.RUnlock()
+
+	ClearCaches(keys)
+}

+ 166 - 0
captcha/captcha.go

@@ -0,0 +1,166 @@
+package captcha
+
+import (
+	"app.yhyue.com/moapp/jybase/captcha/cache"
+	"app.yhyue.com/moapp/jybase/captcha/helper"
+	"app.yhyue.com/moapp/jybase/go-xweb/httpsession"
+	"app.yhyue.com/moapp/jybase/redis"
+	"encoding/json"
+	"fmt"
+	"github.com/wenlng/go-captcha-assets/resources/images"
+	"github.com/wenlng/go-captcha-assets/resources/tiles"
+	"github.com/wenlng/go-captcha/v2/slide"
+	"image"
+	"log"
+	"net/http"
+	"strings"
+)
+
+type (
+	CaptStruct struct {
+		W     http.ResponseWriter
+		R     *http.Request
+		Sess  *httpsession.Session
+		Phone string
+	}
+	CaptData struct {
+		Code        int         `json:"code"`
+		CaptchaKey  interface{} `json:"captcha_key"`
+		ImageBase64 interface{} `json:"image_base64"`
+		TileBase64  interface{} `json:"tile_base64"`
+		TileWidth   interface{} `json:"tile_width"`
+		TileHeight  interface{} `json:"tile_height"`
+		TileX       interface{} `json:"tile_x"`
+		TileY       interface{} `json:"tile_y"`
+	}
+)
+
+var (
+	captFuncS   []slide.Captcha
+	rd                = RandomNumberGenerator()
+	redisCode         = "other"
+	redisTtl          = 10 * 60
+	redisKey          = "captcha_slide_%s_%s"
+	padding     int64 = 4
+	SendCodeKey       = "send_phone_check_%s_%s"
+	SendCodeTtl       = 20 * 60
+)
+
+func InitCaptcha() {
+	for i := range [1]int{} {
+		i++
+		if captcha := slideCaptcha(i); captcha != nil {
+			captFuncS = append(captFuncS, captcha)
+		}
+	}
+	if len(captFuncS) == 0 {
+		fmt.Println("InitCaptcha False")
+	}
+	cache.RunTimedTask()
+}
+
+// 滑动 Basic=1 || 拖拽 Region=2
+func slideCaptcha(n int) slide.Captcha {
+	var builder slide.Builder
+	switch n {
+	case 1:
+		builder = slide.NewBuilder(
+			slide.WithGenGraphNumber(2),
+		)
+	default:
+		builder = slide.NewBuilder(
+			slide.WithGenGraphNumber(2),
+			slide.WithEnableGraphVerticalRandom(true),
+		)
+	}
+	// background images
+	var (
+		imgS []image.Image
+		err  error
+	)
+	imgS, err = images.GetImages()
+	if err != nil {
+		log.Fatalln(err)
+		return nil
+	}
+	var graphs []*tiles.GraphImage
+	graphs, err = tiles.GetTiles()
+	if err != nil {
+		log.Fatalln(err)
+		return nil
+	}
+	var newGraphs = make([]*slide.GraphImage, 0, len(graphs))
+	for i := 0; i < len(graphs); i++ {
+		graph := graphs[i]
+		newGraphs = append(newGraphs, &slide.GraphImage{
+			OverlayImage: graph.OverlayImage,
+			MaskImage:    graph.MaskImage,
+			ShadowImage:  graph.ShadowImage,
+		})
+	}
+	// set resources
+	builder.SetResources(
+		slide.WithGraphImages(newGraphs),
+		slide.WithBackgrounds(imgS),
+	)
+	switch n {
+	case 1:
+		return builder.Make()
+	default:
+		return builder.MakeWithRegion()
+	}
+}
+
+// GetCaptchaData .
+func (c *CaptStruct) GetCaptchaData() (cd CaptData, err error) {
+	if strings.TrimSpace(c.Phone) == "" || !PhoneCheck(c.Phone) {
+		err = fmt.Errorf("phone param is empty or incorrect")
+		return
+	}
+	var (
+		randomIndex = rd.Intn(len(captFuncS))
+		captData    slide.CaptchaData
+	)
+	captData, err = captFuncS[randomIndex].Generate()
+	if err != nil {
+		log.Fatalln(err)
+		return
+	}
+	blockData := captData.GetData()
+	if blockData == nil {
+		err = fmt.Errorf("gen captcha data failed")
+		return
+	}
+	var masterImageBase64, tileImageBase64 string
+	masterImageBase64, err = captData.GetMasterImage().ToBase64()
+	if err != nil {
+		err = fmt.Errorf("masterImage base64 data failed")
+		return
+	}
+	tileImageBase64, err = captData.GetTileImage().ToBase64()
+	if err != nil {
+		err = fmt.Errorf("tileImage base64 data failed")
+		return
+	}
+	dotsByte, _ := json.Marshal(blockData)
+	//key := helper.GenUniqueId()
+	captchaKey := helper.StringToMD5(string(dotsByte))
+	cacheKey := fmt.Sprintf(redisKey, captchaKey, c.Phone)
+	//cache.WriteCache(fmt.Sprintf("%s#%s", captchaKey, c.Phone), dotsByte)
+	err = redis.PutBytes(redisCode, cacheKey, &dotsByte, redisTtl)
+	if err != nil {
+		err = fmt.Errorf("cache save value failed")
+		return
+	}
+	cd = CaptData{
+		Code:        randomIndex,
+		CaptchaKey:  captchaKey,
+		ImageBase64: masterImageBase64,
+		TileBase64:  tileImageBase64,
+		TileWidth:   blockData.Width,
+		TileHeight:  blockData.Height,
+		TileX:       blockData.TileX,
+		TileY:       blockData.TileY,
+	}
+	return
+}

+ 61 - 0
captcha/check.go

@@ -0,0 +1,61 @@
+package captcha
+
+import (
+	"app.yhyue.com/moapp/jybase/go-xweb/httpsession"
+	"app.yhyue.com/moapp/jybase/redis"
+	"encoding/json"
+	"fmt"
+	"github.com/wenlng/go-captcha/v2/slide"
+	"net/http"
+	"strconv"
+	"strings"
+)
+
+type CheckCaptcha struct {
+	W     http.ResponseWriter
+	R     *http.Request
+	Sess  *httpsession.Session
+	Phone string
+	Point string
+	Key   string
+}
+
+// CheckCaptchaData .
+func (c *CheckCaptcha) CheckCaptchaData() (err error) {
+	if strings.TrimSpace(c.Phone) == "" || !PhoneCheck(c.Phone) {
+		err = fmt.Errorf("phone param is empty or incorrect")
+		return
+	}
+	point := c.Point
+	key := c.Key
+	if point == "" || key == "" {
+		err = fmt.Errorf("point or key param is empty")
+		return
+	}
+	var cacheDataByte *[]byte
+	//cacheDataByte := cache.ReadCache(fmt.Sprintf("%s#%s", key, c.Phone))
+	cacheDataByte, err = redis.GetBytes(redisCode, fmt.Sprintf(redisKey, key, c.Phone))
+	if len(*cacheDataByte) == 0 {
+		err = fmt.Errorf("illegal key")
+		return
+	}
+	src := strings.Split(point, ",")
+	var dct *slide.Block
+	if err = json.Unmarshal(*cacheDataByte, &dct); err != nil {
+		return
+	}
+	chkRet := false
+	if 2 == len(src) {
+		sx, _ := strconv.ParseFloat(fmt.Sprintf("%v", src[0]), 64)
+		sy, _ := strconv.ParseFloat(fmt.Sprintf("%v", src[1]), 64)
+		chkRet = slide.CheckPoint(int64(sx), int64(sy), int64(dct.X), int64(dct.Y), padding)
+	}
+	if !chkRet {
+		err = fmt.Errorf("check result false")
+		return
+	}
+	//发送短信等验证缓存
+	//redis.Put(redisCode, fmt.Sprintf(SendCodeKey, key, c.Phone), c.Phone, SendCodeTtl)
+	c.Sess.Set(fmt.Sprintf(SendCodeKey, key, c.Phone), c.Phone)
+	return nil
+}

+ 39 - 0
captcha/check_test.go

@@ -0,0 +1,39 @@
+package captcha
+
+import (
+	"app.yhyue.com/moapp/jybase/go-xweb/httpsession"
+	"app.yhyue.com/moapp/jybase/redis"
+	"fmt"
+	"net/http"
+	"testing"
+	"time"
+)
+
+func TestCaptcha(t *testing.T) {
+	redis.InitRedisBySize("other=172.20.45.129:1712", 100, 30, 300)
+	InitCaptcha()
+	var (
+		W http.ResponseWriter
+		R *http.Request
+	)
+	cs := CaptStruct{
+		W:     W,
+		R:     R,
+		Sess:  &httpsession.Session{},
+		Phone: "15737190152",
+	}
+	cs.Sess.SetId("001")
+	b, err := cs.GetCaptchaData()
+	fmt.Println(b, err)
+	cd := CheckCaptcha{
+		W:     W,
+		R:     R,
+		Sess:  cs.Sess,
+		Phone: cs.Phone,
+		Point: fmt.Sprintf("%d,%d", b.TileX, b.TileY),
+		Key:   b.CaptchaKey.(string),
+	}
+	err = cd.CheckCaptchaData()
+	fmt.Println("err:", err)
+	time.Sleep(10 * time.Second)
+}

+ 55 - 0
captcha/helper/helper.go

@@ -0,0 +1,55 @@
+package helper
+
+import (
+	"crypto/md5"
+	"encoding/hex"
+	"fmt"
+	"os"
+	"sync/atomic"
+	"time"
+)
+
+// GetPWD .
+func GetPWD() string {
+	path, err := os.Getwd()
+	if err != nil {
+		return ""
+	}
+	return path
+}
+
+// StringToMD5 MD5
+func StringToMD5(src string) string {
+	m := md5.New()
+	m.Write([]byte(src))
+	res := hex.EncodeToString(m.Sum(nil))
+	return res
+}
+
+var num int64
+
+const (
+	Continuity = "20060102150405"
+)
+
+func GenUniqueId() string {
+	t := time.Now()
+	s := t.Format(Continuity)
+	m := t.UnixNano()/1e6 - t.UnixNano()/1e9*1e3
+	ms := sup(m, 3)
+	p := os.Getpid() % 1000
+	ps := sup(int64(p), 3)
+	i := atomic.AddInt64(&num, 1)
+	r := i % 10000
+	rs := sup(r, 4)
+	n := fmt.Sprintf("%s%s%s%s", s, ms, ps, rs)
+	return n
+}
+
+func sup(i int64, n int) string {
+	m := fmt.Sprintf("%d", i)
+	for len(m) < n {
+		m = fmt.Sprintf("0%s", m)
+	}
+	return m
+}

+ 48 - 0
captcha/helper/load.go

@@ -0,0 +1,48 @@
+package helper
+
+import (
+	"image"
+	"io"
+	"os"
+	"path"
+
+	"github.com/golang/freetype"
+	"github.com/golang/freetype/truetype"
+	"github.com/wenlng/go-captcha/v2/base/codec"
+)
+
+// LoadJpeg .
+func LoadJpeg(p string) (image.Image, error) {
+	p = path.Join(GetPWD(), p)
+	file, err := os.Open(p)
+	if err != nil {
+		return nil, err
+	}
+	defer file.Close()
+	dataByte, _ := io.ReadAll(file)
+	return codec.DecodeByteToJpeg(dataByte)
+}
+
+// LoadPNG .
+func LoadPNG(p string) (image.Image, error) {
+	p = path.Join(GetPWD(), p)
+	file, err := os.Open(p)
+	if err != nil {
+		return nil, err
+	}
+	defer file.Close()
+	dataByte, _ := io.ReadAll(file)
+	return codec.DecodeByteToPng(dataByte)
+}
+
+// LoadFont .
+func LoadFont(p string) (*truetype.Font, error) {
+	p = path.Join(GetPWD(), p)
+	file, err := os.Open(p)
+	if err != nil {
+		return nil, err
+	}
+	defer file.Close()
+	dataByte, _ := io.ReadAll(file)
+	return freetype.ParseFont(dataByte)
+}

+ 19 - 0
captcha/util.go

@@ -0,0 +1,19 @@
+package captcha
+
+import (
+	"math/rand"
+	"regexp"
+	"time"
+)
+
+var phoneReg = regexp.MustCompile("^[1][3-9][0-9]{9}$")
+
+func RandomNumberGenerator() *rand.Rand {
+	s1 := rand.NewSource(time.Now().UnixNano())
+	r1 := rand.New(s1)
+	return r1
+}
+
+func PhoneCheck(phone string) bool {
+	return phoneReg.MatchString(phone)
+}

+ 5 - 3
go.mod

@@ -15,6 +15,8 @@ require (
 	github.com/gomodule/redigo v1.8.9
 	github.com/howeyc/fsnotify v0.9.0
 	github.com/olivere/elastic/v7 v7.0.22
+	github.com/wenlng/go-captcha-assets v1.0.1
+	github.com/wenlng/go-captcha/v2 v2.0.3
 	github.com/yl2chen/cidranger v1.0.2
 	go.etcd.io/etcd/client/v3 v3.5.4
 	go.mongodb.org/mongo-driver v1.9.1
@@ -53,10 +55,10 @@ require (
 	go.uber.org/atomic v1.9.0 // indirect
 	go.uber.org/goleak v1.1.12 // indirect
 	go.uber.org/multierr v1.8.0 // indirect
-	golang.org/x/crypto v0.0.0-20201216223049-8b5274cf687f // indirect
-	golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 // indirect
+	golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 // indirect
+	golang.org/x/net v0.6.0 // indirect
 	golang.org/x/sync v0.11.0 // indirect
-	golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40 // indirect
+	golang.org/x/sys v0.5.0 // indirect
 	golang.org/x/text v0.22.0 // indirect
 	google.golang.org/genproto v0.0.0-20220602131408-e326c6e8e9c8 // indirect
 	google.golang.org/grpc v1.47.0 // indirect

+ 29 - 3
go.sum

@@ -54,6 +54,7 @@ github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4
 github.com/garyburd/redigo v1.6.2 h1:yE/pwKCrbLpLpQICzYTeZ7JsTA/C53wFTJHaEtRqniM=
 github.com/garyburd/redigo v1.6.2/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY=
 github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
+github.com/go-bindata/go-bindata v3.1.2+incompatible/go.mod h1:xK8Dsgwmeed+BBsSy2XTopBn/8uK2HWuGSnA11C3Joo=
 github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
 github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
 github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
@@ -197,6 +198,10 @@ github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PK
 github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
 github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4=
 github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
+github.com/wenlng/go-captcha-assets v1.0.1 h1:AdjRFMKmadPRWRTv0XEYfjDvcaayZ2yExITDvlK/7bk=
+github.com/wenlng/go-captcha-assets v1.0.1/go.mod h1:yQqc7rRbxgLCg+tWtVp+7Y317D1wIZDan/yIwt8wSac=
+github.com/wenlng/go-captcha/v2 v2.0.3 h1:QTZ39/gVDisPSgvL9O2X2HbTuj5P/z8QsdGB/aayg9c=
+github.com/wenlng/go-captcha/v2 v2.0.3/go.mod h1:5hac1em3uXoyC5ipZ0xFv9umNM/waQvYAQdr0cx/h34=
 github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
 github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
 github.com/xdg-go/scram v1.0.2 h1:akYIkZ28e6A96dkWNJQu3nmCzH3YfwMPQExUYDaRv7w=
@@ -210,6 +215,7 @@ github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7Jul
 github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
+github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
 go.etcd.io/etcd/api/v3 v3.5.4 h1:OHVyt3TopwtUQ2GKdd5wu3PmmipR4FTwCqoEjSyRdIc=
 go.etcd.io/etcd/api/v3 v3.5.4/go.mod h1:5GB2vv4A4AOn3yk7MftYGHkUfGtDHnEraIjym4dYz5A=
 go.etcd.io/etcd/client/pkg/v3 v3.5.4 h1:lrneYvz923dvC14R54XcA7FXoZ3mlGZAgmwhfm7HqOg=
@@ -236,9 +242,11 @@ golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnf
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/crypto v0.0.0-20201216223049-8b5274cf687f h1:aZp0e2vLN4MToVqnjNEYEtrEA8RH8U8FN1CU7JgqsPU=
 golang.org/x/crypto v0.0.0-20201216223049-8b5274cf687f/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
+golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg=
+golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/image v0.16.0/go.mod h1:ugSZItdV4nOxyqp56HmXwH0Ry0nBCpjnZdpDaIHdoPs=
 golang.org/x/image v0.24.0 h1:AN7zRgVsbvmTfNyqIbbOraYL8mSwcKncEj8ofjgzcMQ=
 golang.org/x/image v0.24.0/go.mod h1:4b/ITuLfqYq1hqZcjofwctIhi7sZh2WaCjvsBNjjya8=
 golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@@ -250,6 +258,8 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB
 golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
+golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
 golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -264,8 +274,11 @@ golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLL
 golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
 golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
 golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
-golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 h1:4nGaVu0QrbjT/AK2PRLuQfQuh6DJve+pELhqTdAj3x0=
+golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
 golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
+golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
+golang.org/x/net v0.6.0 h1:L4ZwwTvKW9gr0ZMS1yrHD9GZhIuVjOBBnaKH+SPQK0Q=
+golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -278,6 +291,8 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ
 golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
 golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
 golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -300,14 +315,23 @@ golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40 h1:JWgyZ1qgdTaF3N3oxC+MdTV7qvEEgHo3otj+HB5CM7Q=
 golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
+golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
+golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
+golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
+golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
 golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
 golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -322,6 +346,8 @@ golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roY
 golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
 golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
 golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
+golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
+golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=