├── README ├── browser.png ├── command.png ├── Flowchart.png └── logo.svg ├── resource ├── logo │ └── favicon.ico ├── embed.go └── template │ ├── list.tpl │ └── login.tpl ├── .gitee ├── ISSUE_TEMPLATE.zh-CN.md ├── ISSUE_TEMPLATE.en.md ├── PULL_REQUEST_TEMPLATE.zh-CN.md └── PULL_REQUEST_TEMPLATE.en.md ├── .idea ├── vcs.xml ├── modules.xml ├── OneTiny.iml └── watcherTasks.xml ├── internal ├── handle │ ├── handle.go │ ├── core │ │ ├── upload.go │ │ └── download.go │ └── secure │ │ └── login.go ├── server │ ├── routes │ │ ├── login.go │ │ ├── setup.go │ │ ├── core.go │ │ └── common.go │ ├── middleware │ │ ├── setup.go │ │ ├── check-login.go │ │ ├── logger.go │ │ └── check-level.go │ └── server.go ├── kit │ └── verify │ │ ├── verify_path.go │ │ ├── verify_port.go │ │ └── vreify_ups.go ├── constant │ └── constant.go └── conf │ └── conf.go ├── cmd ├── road_map.go ├── common_flag.go ├── config.go ├── root.go ├── secure.go └── update.go ├── .vscode └── launch.json ├── .github └── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md ├── pkg ├── pkg.go └── container │ └── handle_chain.go ├── main.go ├── LICENSE ├── Makefile ├── go.mod ├── .gitignore ├── README.md └── go.sum /README/browser.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TCP404/OneTiny/HEAD/README/browser.png -------------------------------------------------------------------------------- /README/command.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TCP404/OneTiny/HEAD/README/command.png -------------------------------------------------------------------------------- /README/Flowchart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TCP404/OneTiny/HEAD/README/Flowchart.png -------------------------------------------------------------------------------- /resource/logo/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TCP404/OneTiny/HEAD/resource/logo/favicon.ico -------------------------------------------------------------------------------- /resource/embed.go: -------------------------------------------------------------------------------- 1 | package resource 2 | 3 | import "embed" 4 | 5 | //go:embed template/* logo/* 6 | var FS embed.FS 7 | -------------------------------------------------------------------------------- /.gitee/ISSUE_TEMPLATE.zh-CN.md: -------------------------------------------------------------------------------- 1 | ### 该问题是怎么引起的? 2 | 3 | 4 | 5 | ### 重现步骤 6 | 7 | 8 | 9 | ### 报错信息 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /.gitee/ISSUE_TEMPLATE.en.md: -------------------------------------------------------------------------------- 1 | ### How did the problem arise? 2 | 3 | 4 | 5 | ### Repeat steps 6 | 7 | 8 | 9 | ### Error message 10 | -------------------------------------------------------------------------------- /.gitee/PULL_REQUEST_TEMPLATE.zh-CN.md: -------------------------------------------------------------------------------- 1 | ### 相关的Issue 2 | 3 | 4 | ### 原因(目的、解决的问题等) 5 | 6 | 7 | ### 描述(做了什么,变更了什么) 8 | 9 | 10 | ### 测试用例(新增、改动、可能影响的功能) 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /internal/handle/handle.go: -------------------------------------------------------------------------------- 1 | package handle 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/gin-gonic/gin" 7 | ) 8 | 9 | func ErrorHandle(c *gin.Context, msg string) { 10 | c.String(http.StatusInternalServerError, msg) 11 | } 12 | -------------------------------------------------------------------------------- /.gitee/PULL_REQUEST_TEMPLATE.en.md: -------------------------------------------------------------------------------- 1 | ### Relevant Issue 2 | 3 | 4 | ### Cause (purpose, problem solved, etc) 5 | 6 | 7 | ### Description (What was done, What was change) 8 | 9 | 10 | ### Test cases (new, changed, potentially affected features) 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /internal/server/routes/login.go: -------------------------------------------------------------------------------- 1 | package routes 2 | 3 | import ( 4 | "github.com/TCP404/OneTiny-cli/internal/handle/secure" 5 | 6 | "github.com/gin-gonic/gin" 7 | ) 8 | 9 | func loadLoginRoute(r *gin.Engine) { 10 | r.GET("/login", secure.LoginGet) 11 | r.POST("/login", secure.LoginPost) 12 | } 13 | -------------------------------------------------------------------------------- /.idea/OneTiny.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /cmd/road_map.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | // onetiny 4 | // ├── -r 5 | // ├── -p 6 | // ├── -a 7 | // ├── -x 8 | // ├── -s 9 | // ├── config 10 | // │ ├── -r 11 | // │ ├── -p 12 | // │ ├── -a 13 | // │ ├── -x 14 | // │ └── -s 15 | // ├── sec 16 | // │ ├── -u 17 | // │ ├── -p 18 | // │ └── -s 19 | // └── update 20 | // ├── --use 21 | // └── -l 22 | -------------------------------------------------------------------------------- /internal/server/middleware/setup.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "github.com/gin-contrib/sessions" 5 | "github.com/gin-contrib/sessions/cookie" 6 | "github.com/gin-gonic/gin" 7 | ) 8 | 9 | func Setup(r *gin.Engine) *gin.Engine { 10 | r.Use(Logger(), gin.Recovery()) 11 | r.Use(enableCookieSession()) 12 | return r 13 | } 14 | 15 | func enableCookieSession() gin.HandlerFunc { 16 | s := cookie.NewStore([]byte("secret")) 17 | return sessions.Sessions("SESSIONID", s) 18 | } 19 | -------------------------------------------------------------------------------- /internal/server/routes/setup.go: -------------------------------------------------------------------------------- 1 | package routes 2 | 3 | import ( 4 | "html/template" 5 | "log" 6 | 7 | "github.com/TCP404/OneTiny-cli/resource" 8 | "github.com/gin-gonic/gin" 9 | ) 10 | 11 | func Setup(r *gin.Engine) *gin.Engine { 12 | 13 | t, err := template.ParseFS(resource.FS, "template/*.tpl") 14 | if err != nil { 15 | log.Fatal(err.Error()) 16 | } 17 | r.SetHTMLTemplate(t) 18 | 19 | loadIndexRoute(r) 20 | loadCoreRoute(r) 21 | loadLoginRoute(r) 22 | load404Route(r) 23 | loadICORoute(r) 24 | return r 25 | } 26 | -------------------------------------------------------------------------------- /internal/server/routes/core.go: -------------------------------------------------------------------------------- 1 | package routes 2 | 3 | import ( 4 | "github.com/TCP404/OneTiny-cli/internal/constant" 5 | "github.com/TCP404/OneTiny-cli/internal/handle/core" 6 | "github.com/TCP404/OneTiny-cli/internal/server/middleware" 7 | 8 | "github.com/gin-gonic/gin" 9 | ) 10 | 11 | func loadCoreRoute(r *gin.Engine) *gin.RouterGroup { 12 | fileG := r.Group(constant.FileGroupPrefix, middleware.CheckLogin, middleware.CheckLevel) 13 | { 14 | fileG.GET("/*filename", core.Downloader) 15 | fileG.POST("/upload", core.Uploader) 16 | } 17 | return fileG 18 | } 19 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "Launch Package", 9 | "type": "go", 10 | "request": "launch", 11 | "mode": "auto", 12 | "program": "${workspaceFolder}/main.go", 13 | "args": [ 14 | 15 | ] 16 | } 17 | ] 18 | } -------------------------------------------------------------------------------- /internal/server/middleware/check-login.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/gin-contrib/sessions" 7 | "github.com/gin-gonic/gin" 8 | 9 | "github.com/TCP404/OneTiny-cli/internal/conf" 10 | ) 11 | 12 | func CheckLogin(c *gin.Context) { 13 | // 检查 session, 14 | // 有则放行 15 | // 无则跳转登录页面 16 | if !conf.Config.IsSecure { 17 | return 18 | } 19 | 20 | if session := sessions.Default(c); session.Get("login") == conf.Config.SessionVal { 21 | c.Next() 22 | return 23 | } else { 24 | c.Redirect(http.StatusTemporaryRedirect, "/login") 25 | return 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /internal/kit/verify/verify_path.go: -------------------------------------------------------------------------------- 1 | package verify 2 | 3 | import ( 4 | "errors" 5 | "os" 6 | 7 | "github.com/TCP404/OneTiny-cli/pkg/container" 8 | "github.com/fatih/color" 9 | ) 10 | 11 | type pathVerifier struct { 12 | rootPath string 13 | } 14 | 15 | var _ container.Handler = (*pathVerifier)(nil) 16 | 17 | func NewPathVerifier(path string) container.Handler { return &pathVerifier{rootPath: path} } 18 | 19 | // pathHandler.Handle 负责校验用户指定的共享目录的绝对路径 20 | func (p *pathVerifier) Handle() error { 21 | if _, err := os.Stat(p.rootPath); err != nil { 22 | if !os.IsExist(err) { 23 | return errors.New(color.RedString("无法设置您指定的共享路径, 请检查给出的路径是否有问题:%s", p.rootPath)) 24 | } 25 | } 26 | return nil 27 | } 28 | -------------------------------------------------------------------------------- /internal/server/routes/common.go: -------------------------------------------------------------------------------- 1 | package routes 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/TCP404/OneTiny-cli/resource" 7 | "github.com/gin-gonic/gin" 8 | ) 9 | 10 | func load404Route(app *gin.Engine) { 11 | app.NoRoute(func(c *gin.Context) { 12 | c.String(http.StatusNotFound, "404 Page Not Found", nil) 13 | }) 14 | } 15 | 16 | func loadIndexRoute(app *gin.Engine) { 17 | app.GET("/", func(c *gin.Context) { 18 | c.Redirect(http.StatusPermanentRedirect, "/file/") 19 | }) 20 | } 21 | 22 | func loadICORoute(app *gin.Engine) { 23 | app.GET("favicon.ico", func(c *gin.Context) { 24 | file, _ := resource.FS.ReadFile("logo/favicon.ico") 25 | c.Data(http.StatusOK, "image/x-icon", file) 26 | }) 27 | } 28 | -------------------------------------------------------------------------------- /internal/handle/core/upload.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "net/http" 5 | "path" 6 | "path/filepath" 7 | 8 | "github.com/TCP404/OneTiny-cli/internal/conf" 9 | "github.com/TCP404/OneTiny-cli/internal/constant" 10 | "github.com/TCP404/OneTiny-cli/internal/handle" 11 | "github.com/gin-gonic/gin" 12 | ) 13 | 14 | func Uploader(c *gin.Context) { 15 | f, err := c.FormFile("upload_file") 16 | if err != nil { 17 | handle.ErrorHandle(c, "文件上传失败!") 18 | return 19 | } 20 | 21 | currPath := c.PostForm("path") 22 | err = c.SaveUploadedFile(f, filepath.Join(conf.Config.RootPath, currPath, f.Filename)) 23 | if err != nil { 24 | handle.ErrorHandle(c, "文件保存失败!") 25 | return 26 | } 27 | c.Redirect(http.StatusMovedPermanently, path.Join(constant.FileGroupPrefix, currPath)) 28 | } 29 | -------------------------------------------------------------------------------- /pkg/pkg.go: -------------------------------------------------------------------------------- 1 | package pkg 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "time" 7 | 8 | pb "github.com/schollz/progressbar/v3" 9 | ) 10 | 11 | func GetBar(filename string, contentLen int64, output io.Writer) *pb.ProgressBar { 12 | // 使用下载进度条,当访问者点击下载时,共享者会有进度条提示 13 | ops := []pb.Option{ 14 | pb.OptionSetDescription("[green]Downloading[reset] [blue]" + filename + "[reset]"), 15 | pb.OptionSetWidth(10), 16 | pb.OptionThrottle(65 * time.Millisecond), 17 | pb.OptionShowCount(), 18 | pb.OptionOnCompletion(func() { fmt.Fprint(output, "\n") }), 19 | pb.OptionSpinnerType(14), 20 | pb.OptionFullWidth(), 21 | pb.OptionSetWriter(output), 22 | pb.OptionEnableColorCodes(true), 23 | pb.OptionShowBytes(true), 24 | pb.OptionSetWidth(50), 25 | } 26 | return pb.NewOptions64(contentLen, ops...) 27 | } 28 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "os" 6 | 7 | "github.com/TCP404/OneTiny-cli/cmd" 8 | "github.com/TCP404/OneTiny-cli/internal/conf" 9 | "github.com/TCP404/OneTiny-cli/internal/kit/verify" 10 | "github.com/TCP404/OneTiny-cli/internal/server" 11 | "github.com/TCP404/OneTiny-cli/pkg/container" 12 | 13 | "github.com/fatih/color" 14 | ) 15 | 16 | func main() { 17 | var err error 18 | defer func() { 19 | if err != nil { 20 | log.Println(color.RedString("%v", err)) 21 | } 22 | }() 23 | 24 | if err = conf.LoadConfig(); err != nil { 25 | return 26 | } 27 | 28 | if err = cmd.CLI().Run(os.Args); err != nil { 29 | return 30 | } 31 | 32 | if err = container.NewHandleChain(). 33 | AddToHead(verify.NewPortVerifier(conf.Config.Port)). 34 | AddToHead(verify.NewPathVerifier(conf.Config.RootPath)). 35 | Iterator(); err != nil { 36 | return 37 | } 38 | 39 | server.RunCore() 40 | } 41 | -------------------------------------------------------------------------------- /internal/handle/secure/login.go: -------------------------------------------------------------------------------- 1 | package secure 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/TCP404/OneTiny-cli/internal/conf" 7 | "github.com/TCP404/eutil" 8 | "github.com/gin-contrib/sessions" 9 | "github.com/gin-gonic/gin" 10 | ) 11 | 12 | // LoginGet 接收 GET /login 请求,返回登录页面 13 | func LoginGet(c *gin.Context) { 14 | c.HTML(http.StatusOK, "login.tpl", nil) 15 | } 16 | 17 | // LoginPost 接收 POST /login 请求,验证帐号密码,验证通过则生成 session 18 | func LoginPost(c *gin.Context) { 19 | // 检查帐号密码 20 | // 通过则生成session,跳转首页 21 | // 不通过则返回登录页 22 | if eutil.MD5(c.PostForm("username")) == conf.Config.Username && 23 | eutil.MD5(c.PostForm("password")) == conf.Config.Password { 24 | session := sessions.Default(c) 25 | session.Set("login", conf.Config.SessionVal) 26 | session.Save() 27 | c.JSON(http.StatusOK, gin.H{"code": 1, "message": "登录成功"}) 28 | return 29 | } else { 30 | c.JSON(http.StatusOK, gin.H{"code": 0, "message": "登录失败"}) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /internal/kit/verify/verify_port.go: -------------------------------------------------------------------------------- 1 | package verify 2 | 3 | import ( 4 | "errors" 5 | "runtime" 6 | 7 | "github.com/TCP404/OneTiny-cli/pkg/container" 8 | "github.com/fatih/color" 9 | ) 10 | 11 | type portVerifier struct { 12 | port int 13 | } 14 | 15 | var _ container.Handler = (*portVerifier)(nil) 16 | 17 | func NewPortVerifier(port int) container.Handler { return &portVerifier{port: port} } 18 | 19 | // portHandler.Handle 负责校验用户指定的端口号是否在合法范围内。 20 | // 对于 unix 系列的操作系统,端口允许范围在 [1024,65535]; 21 | // 对于微软操作系统,端口允许范围在 [5001, 65535]; 22 | // 对于其他操作系统暂时不做验证; 23 | func (p *portVerifier) Handle() error { 24 | switch runtime.GOOS { 25 | case "linux", "darwin": 26 | if !(p.port >= 1024 && p.port <= 65535) { 27 | return errors.New(color.RedString("不可以设置系统预留端口 %d, 您可以设置的范围在 [ 1024 ~ 65535 ] 之间。", p.port)) 28 | } 29 | case "windows": 30 | if !(p.port >= 5001 && p.port <= 65535) { 31 | return errors.New(color.RedString("不可以设置系统预留端口 %d, 您可以设置的范围在 [ 5001 ~ 65535 ] 之间。", p.port)) 32 | } 33 | } 34 | return nil 35 | } 36 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /internal/server/middleware/logger.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | 7 | "github.com/gin-gonic/gin" 8 | 9 | "github.com/TCP404/OneTiny-cli/internal/conf" 10 | ) 11 | 12 | func Logger() gin.HandlerFunc { 13 | return gin.LoggerWithConfig(gin.LoggerConfig{ 14 | Output: conf.Config.Output, 15 | Formatter: func(param gin.LogFormatterParams) string { 16 | var statusColor, methodColor, resetColor string 17 | if param.IsOutputColor() { 18 | statusColor = param.StatusCodeColor() 19 | methodColor = param.MethodColor() 20 | resetColor = param.ResetColor() 21 | } 22 | if param.Latency > time.Minute { 23 | // Truncate in a golang < 1.8 safe way 24 | param.Latency = param.Latency - param.Latency%time.Second 25 | } 26 | return fmt.Sprintf("%v %s %3d %s %13v | %15s |%s %-7s %s %#v\n%s", 27 | param.TimeStamp.Format("2006/01/02 15:04:05"), 28 | statusColor, param.StatusCode, resetColor, 29 | param.Latency, 30 | param.ClientIP, 31 | methodColor, param.Method, resetColor, 32 | param.Path, 33 | param.ErrorMessage, 34 | ) 35 | }}) 36 | } 37 | -------------------------------------------------------------------------------- /internal/constant/constant.go: -------------------------------------------------------------------------------- 1 | package constant 2 | 3 | import "github.com/TCP404/eutil" 4 | 5 | var VERSION string 6 | 7 | const ( 8 | ROOT string = "/" 9 | FileGroupPrefix string = "/file" 10 | ) 11 | 12 | const ( 13 | MaxLevel uint8 = 0 // 允许访问的最大层级 14 | Port int = 8192 // 指定的服务端口 15 | IsAllowUpload bool = false // 是否允许上传 16 | IsSecure bool = false // 是否开启访问登录 17 | RootPath string = "/" // 共享目录的根路径,默认值:当前目录 18 | Username string = "admin" // 访问登录的帐号 19 | Password string = "admin" // 访问登录的密码 20 | IP string = "127.0.0.1" // 本机局域网IP 21 | ) 22 | 23 | const ( 24 | VersionListURL = "https://api.github.com/repos/TCP404/OneTiny/tags" 25 | VersionLatestURL = "https://api.github.com/repos/TCP404/OneTiny/releases/latest" 26 | VersionByTagURL = "https://api.github.com/repos/TCP404/OneTiny/releases/tags/" 27 | ) 28 | 29 | var ( 30 | ReleaseName = map[string]string{ 31 | "linux": "OneTiny", 32 | "windows": "OneTiny.exe", 33 | "darwin": "OneTiny_mac", 34 | } 35 | ) 36 | 37 | var BufferLimit = 512 * eutil.KB 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Boii 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | VERSION=v0.6.0 2 | GO_BUILD=go build -ldflags "-s -w -X github.com/TCP404/OneTiny-cli/internal/constant.VERSION=$(VERSION)" -o 3 | 4 | BINARY_NAME=OneTiny 5 | MAC_AMD_EXE :=./exe/$(BINARY_NAME)_amd64.dmg 6 | MAC_ARM_EXE :=./exe/$(BINARY_NAME)_arm64.dmg 7 | LINUX_EXE :=./exe/$(BINARY_NAME) 8 | WINDOWS_EXE :=./exe/$(BINARY_NAME).exe 9 | 10 | BUILD_MAC_AMD64 := CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 $(GO_BUILD) $(MAC_AMD_EXE) main.go 11 | BUILD_MAC_ARM64 := CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 $(GO_BUILD) $(MAC_ARM_EXE) main.go 12 | BUILD_LINUX := CGO_ENABLED=0 GOOS=linux GOARCH=amd64 $(GO_BUILD) $(LINUX_EXE) main.go 13 | BUILD_WINDOWS := CGO_ENABLED=0 GOOS=windows GOARCH=amd64 $(GO_BUILD) $(WINDOWS_EXE) main.go 14 | 15 | COMPRESS_UPX=upx --best 16 | COMPRESS_MAC_AMD := $(COMPRESS_UPX) $(MAC_AMD_EXE) 17 | COMPRESS_LINUX := $(COMPRESS_UPX) $(LINUX_EXE) 18 | COMPRESS_WINDOWS := $(COMPRESS_UPX) $(WINDOWS_EXE) 19 | 20 | start: clean build compress 21 | 22 | build: 23 | mkdir -p ./exe 24 | $(BUILD_MAC_ARM64) 25 | $(BUILD_MAC_AMD64) 26 | $(BUILD_LINUX) 27 | $(BUILD_WINDOWS) 28 | 29 | compress: 30 | $(COMPRESS_MAC_AMD) 31 | $(COMPRESS_LINUX) 32 | $(COMPRESS_WINDOWS) 33 | 34 | .PHONY: clean 35 | clean: 36 | rm -rf ./exe/* 37 | 38 | -------------------------------------------------------------------------------- /pkg/container/handle_chain.go: -------------------------------------------------------------------------------- 1 | package container 2 | 3 | type Handler interface { 4 | Handle() error 5 | } 6 | 7 | type handleNode struct { 8 | Handler 9 | Next *handleNode 10 | } 11 | 12 | type Chain interface { 13 | AddToHead(Handler) Chain 14 | AddToTail(Handler) Chain 15 | Iterator() error 16 | } 17 | 18 | var _ Chain = (*handleChain)(nil) 19 | 20 | func NewHandleChain() Chain { 21 | return &handleChain{} 22 | } 23 | 24 | type handleChain struct { 25 | head *handleNode 26 | tail *handleNode 27 | } 28 | 29 | func (c *handleChain) AddToHead(h Handler) Chain { 30 | node := &handleNode{ 31 | Handler: h, 32 | Next: c.head, 33 | } 34 | if c.head == nil { 35 | c.head, c.tail = node, node 36 | return c 37 | } 38 | c.head = node 39 | return c 40 | } 41 | 42 | func (c *handleChain) AddToTail(h Handler) Chain { 43 | node := &handleNode{ 44 | Handler: h, 45 | Next: nil, 46 | } 47 | if c.head == nil || c.tail == nil { 48 | c.head, c.tail = node, node 49 | return c 50 | } 51 | c.tail.Next = node 52 | return c 53 | } 54 | 55 | func (c *handleChain) Iterator() error { 56 | curr := c.head 57 | for curr != nil { 58 | if err := curr.Handle(); err != nil { 59 | return err 60 | } 61 | curr = curr.Next 62 | } 63 | return nil 64 | } 65 | -------------------------------------------------------------------------------- /cmd/common_flag.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "strconv" 5 | 6 | "github.com/TCP404/OneTiny-cli/internal/conf" 7 | "github.com/urfave/cli/v2" 8 | ) 9 | 10 | func newGlobalFlag() []cli.Flag { 11 | return []cli.Flag{ 12 | &cli.PathFlag{ 13 | Name: "road", 14 | Aliases: []string{"r"}, 15 | Usage: "指定对外开放的目录`路径`", 16 | Value: conf.Config.RootPath, 17 | Required: false, 18 | DefaultText: conf.Config.RootPath, 19 | }, 20 | &cli.IntFlag{ 21 | Name: "port", 22 | Aliases: []string{"p"}, 23 | Usage: "指定开放的`端口`", 24 | Value: conf.Config.Port, 25 | Required: false, 26 | DefaultText: strconv.Itoa(conf.Config.Port), 27 | }, 28 | &cli.BoolFlag{ 29 | Name: "allow", 30 | Aliases: []string{"a"}, 31 | Usage: "指定`是否`允许访问者上传", 32 | Value: conf.Config.IsAllowUpload, 33 | Required: false, 34 | DefaultText: strconv.FormatBool(conf.Config.IsAllowUpload), 35 | }, 36 | &cli.IntFlag{ 37 | Name: "max", 38 | Aliases: []string{"x"}, 39 | Usage: "指定允许访问的`深度`,默认仅限访问共享目录", 40 | Value: int(conf.Config.MaxLevel), 41 | Required: false, 42 | DefaultText: "0", 43 | }, 44 | &cli.BoolFlag{ 45 | Name: "secure", 46 | Aliases: []string{"s"}, 47 | Usage: "指定是否开启访问登录", 48 | Value: conf.Config.IsSecure, 49 | Required: false, 50 | DefaultText: strconv.FormatBool(conf.Config.IsSecure), 51 | }, 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /internal/server/middleware/check-level.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "net/http" 5 | "os" 6 | "path" 7 | "path/filepath" 8 | "strings" 9 | 10 | "github.com/TCP404/OneTiny-cli/internal/conf" 11 | "github.com/TCP404/OneTiny-cli/internal/constant" 12 | 13 | "github.com/gin-gonic/gin" 14 | ) 15 | 16 | // CheckLevel 负责检查当前访问层级是否超出设定最大层级 17 | // 例如: 18 | // 19 | // 共享目录为 /a/b/ , 最大层级为 2 20 | // ✓: /a/b/ 根目录 21 | // ✓: /a/b/file 根目录下文件 22 | // ✓: /a/b/c/ 根目录下第一层目录 23 | // ✓: /a/b/c/file 根目录下第一层目录下的文件 24 | // ✓: /a/b/c/d/ 根目录下第二层目录 25 | // ✓: /a/b/c/d/file 根目录下第二层目录下的文件 26 | // x: /a/b/c/d/e/ 根目录下第三层目录 27 | // x: /a/b/c/d/e/file 根目录下第三层目录下的文件 28 | func CheckLevel(c *gin.Context) { 29 | filePath := strings.TrimPrefix(c.Param("filename"), constant.FileGroupPrefix) 30 | 31 | c.Set("filename", filePath) 32 | 33 | isD := isDir(filePath) 34 | c.Set("isDirectory", isD) 35 | isFile := !isD 36 | if isOverLevel(filePath, isFile, c.Query("action") == "dl") { 37 | c.String(http.StatusNotFound, "访问超出允许范围,请返回!") 38 | c.Abort() 39 | } 40 | } 41 | 42 | // 判断是否是目录 43 | func isDir(filePath string) bool { 44 | if filePath == constant.ROOT { 45 | return true 46 | } 47 | fInfo, _ := os.Stat(path.Join(conf.Config.RootPath, filePath)) 48 | return fInfo.IsDir() 49 | } 50 | 51 | // 检查当前访问的路径是否超过限定层级 52 | func isOverLevel(relPath string, isFile bool, isDl bool) bool { 53 | rel, _ := filepath.Rel(conf.Config.RootPath, filepath.Join(conf.Config.RootPath, relPath)) 54 | i := strings.Split(rel, string(filepath.Separator)) 55 | level := len(i) 56 | if i[0] == "." { 57 | level = 0 58 | } 59 | if isFile || isDl { 60 | level-- 61 | } 62 | return level > int(conf.Config.MaxLevel) 63 | } 64 | -------------------------------------------------------------------------------- /cmd/config.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "os" 5 | "path/filepath" 6 | 7 | "github.com/TCP404/OneTiny-cli/internal/kit/verify" 8 | "github.com/TCP404/OneTiny-cli/pkg/container" 9 | "github.com/fatih/color" 10 | "github.com/spf13/viper" 11 | "github.com/urfave/cli/v2" 12 | ) 13 | 14 | func configCmd() *cli.Command { 15 | return &cli.Command{ 16 | Name: "config", 17 | Aliases: []string{"c", "cf", "cfg", "conf"}, 18 | Usage: "设置默认配置", 19 | UsageText: "onetiny config [OPTIONS]", 20 | Description: "使用 onetiny config 命令可以将设置写入配置文件。\n使用方式与 onetiny 命令相同,仅多了一个 config 关键字,如:\n onetiny config -p 10240 可以将端口设置为 10240 写入配置\n onetiny config -a false 可以设置不允许访问者上传并写入配置", 21 | Flags: newGlobalFlag(), 22 | Before: beforeConfigAction, 23 | Action: configAction, 24 | } 25 | } 26 | 27 | func beforeConfigAction(c *cli.Context) error { 28 | chain := container.NewHandleChain() 29 | if c.IsSet("port") { 30 | chain.AddToHead(verify.NewPortVerifier(c.Int("port"))) 31 | } 32 | if c.IsSet("road") { 33 | p := c.Path("road") 34 | if p[0] == '.' { 35 | curr, _ := os.Getwd() 36 | p = filepath.Join(curr, p) 37 | } 38 | chain.AddToHead(verify.NewPathVerifier(p)) 39 | } 40 | return chain.Iterator() 41 | } 42 | 43 | func configAction(c *cli.Context) error { 44 | // tiny config -p=8080 -x=3 45 | if c.IsSet("port") { 46 | viper.Set("server.port", c.Int("port")) 47 | } 48 | if c.IsSet("allow") { 49 | viper.Set("server.allow_upload", c.Bool("allow")) 50 | } 51 | if c.IsSet("max") { 52 | viper.Set("server.max_level", c.Int("max")) 53 | } 54 | if c.IsSet("road") { 55 | p := c.Path("road") 56 | if p[0] == '.' { 57 | curr, _ := os.Getwd() 58 | p = filepath.Join(curr, p) 59 | } 60 | viper.Set("server.road", p) 61 | } 62 | if c.IsSet("secure") { 63 | viper.Set("account.secure", c.Bool("secure")) 64 | } 65 | if err := viper.WriteConfig(); err != nil { 66 | return cli.Exit(err.Error(), 11) 67 | } 68 | return cli.Exit(color.GreenString("配置成功~"), 0) 69 | } 70 | -------------------------------------------------------------------------------- /internal/kit/verify/vreify_ups.go: -------------------------------------------------------------------------------- 1 | package verify 2 | 3 | import ( 4 | "errors" 5 | 6 | "github.com/TCP404/OneTiny-cli/pkg/container" 7 | "github.com/TCP404/eutil" 8 | "github.com/spf13/viper" 9 | ) 10 | 11 | // ups 是 User、Pass、Secure 三个单词的首字母合并, 12 | // 用于设置 帐号、密码、访问登录 三个配置项的权值 13 | // 用法和 unix 系统中文件权限的 rwx 相同 14 | type ups uint8 15 | 16 | // USER 表示 已设置账户 17 | // PASS 表示 已设置密码 18 | // SECU 表示 已开启访问登录 19 | const ( 20 | USER ups = 1 << 0 // The weight of USER is 1 21 | PASS ups = 1 << 1 // The weight of PASS is 2 22 | SECU ups = 1 << 2 // The weight of SECU is 4 23 | ) 24 | 25 | type upsVerifier struct { 26 | weight ups 27 | } 28 | 29 | var _ container.Handler = (*upsVerifier)(nil) 30 | 31 | func NewUPSVerifier(weight uint8) *upsVerifier { return &upsVerifier{weight: ups(weight)} } 32 | 33 | // 检查UPS 34 | // 000 (0) 表示 用户仅仅执行了命令 `onetiny sec`,返回访问登录开启状态 35 | // 001 (1) 表示 开启访问登录 36 | // 010 (2) 表示 设置密码 37 | // 011 (3) 表示 开启访问登录,并设置密码 38 | // 100 (4) 表示 设置用户名 39 | // 101 (5) 表示 开启访问登录,并设置帐户名 40 | // 110 (6) 表示 设置账户、密码 41 | // 111 (7) 表示 开启访问登录,并设置账户、密码 42 | // 43 | // 设置规则: 44 | // 开启访问登录时,需配置文件中已设置帐号密码 45 | // 设置密码时,需配置文件中已设置账户 46 | // 设置账户时,需配置文件中已设置密码 47 | func (u *upsVerifier) Handle() error { 48 | switch u.weight { 49 | case USER | PASS | SECU: 50 | // 111 开启访问登录,并设置账户和密码 51 | return nil 52 | case USER | PASS: 53 | // 110 设置账户和密码 54 | return nil 55 | case USER | SECU: 56 | // 101 开启访问登录,并设置用户名,穿透下去检查是否有密码 57 | fallthrough 58 | case USER: 59 | // 100 设置用户名,需配置文件中有密码 60 | return eutil.If(viper.GetString("account.custom.pass") != "", nil, errors.New("未设置密码")) 61 | case PASS | SECU: 62 | // 011 开启访问登录,并设置密码,穿透下去检查是否有帐户名 63 | fallthrough 64 | case PASS: 65 | // 010 设置密码,需配置文件中有账户名 66 | return eutil.If(viper.GetString("account.custom.user") != "", nil, errors.New("未设置帐号")) 67 | case SECU: 68 | // 001 开启访问登录 69 | return eutil.If(viper.GetString("account.custom.user") != "" && viper.GetString("account.custom.pass") != "", nil, errors.New("未设置帐号和密码")) 70 | case 0: 71 | // 000 打印当前是否开启访问登录 72 | return nil 73 | default: 74 | return errors.New("设置失败~") 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /README/logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resource/template/list.tpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | OneTiny Server 8 | 9 | 10 | 11 |

Directory listing for {{- .pathTitle -}}

12 |

13 | {{if .upload}} 14 |
16 |
17 | 18 | 19 | 20 |
21 |
22 | {{end}} 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | {{range $i, $f := .files}} 37 | 38 | 39 | 40 | {{if $f.IsDir}} 41 | 45 | {{else}} 46 | 50 | {{end}} 51 | 52 | {{end}} 53 | 54 |
文件名文件大小操作
 . . /
{{$i}}. {{$f.Name}}{{$f.Size}} 42 | 下载 43 | 查看 44 | 47 | 下载 48 |  ->  49 |
55 | 56 | 57 | -------------------------------------------------------------------------------- /cmd/root.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "os" 7 | "path/filepath" 8 | 9 | "github.com/TCP404/OneTiny-cli/internal/conf" 10 | "github.com/TCP404/OneTiny-cli/internal/constant" 11 | "github.com/TCP404/OneTiny-cli/internal/kit/verify" 12 | "github.com/TCP404/OneTiny-cli/pkg/container" 13 | 14 | "github.com/urfave/cli/v2" 15 | ) 16 | 17 | func initCLI() { 18 | cli.VersionFlag = &cli.BoolFlag{ 19 | Name: "version", 20 | Aliases: []string{"v"}, 21 | Usage: "打印版本信息", 22 | } 23 | cli.VersionPrinter = func(c *cli.Context) { 24 | fmt.Println("当前版本: ", c.App.Version) 25 | os.Exit(0) 26 | } 27 | cli.HelpFlag = &cli.BoolFlag{ 28 | Name: "help", 29 | Aliases: []string{"h"}, 30 | Usage: "打印帮助信息", 31 | } 32 | cli.HelpPrinter = func(w io.Writer, temp string, data interface{}) { 33 | cli.HelpPrinterCustom(w, temp, data, nil) 34 | os.Exit(0) 35 | } 36 | cli.ErrWriter = conf.Config.Output 37 | } 38 | 39 | // CLI 函数作为程序入口,主要负责处理命令和 flag 40 | func CLI() *cli.App { 41 | initCLI() 42 | 43 | return &cli.App{ 44 | Name: "OneTiny", 45 | Usage: "一个用于局域网内共享文件的FTP程序", 46 | UsageText: "onetiny [GLOBAL OPTIONS] COMMAND [COMMAND OPTIONS] [参数...]", 47 | Version: constant.VERSION, 48 | Flags: newGlobalFlag(), 49 | Authors: []*cli.Author{{Name: "Boii", Email: "i@tcp404.com"}}, 50 | Commands: []*cli.Command{updateCmd(), configCmd(), secureCmd()}, 51 | CommandNotFound: func(c *cli.Context, s string) { cli.ShowAppHelpAndExit(c, 10) }, 52 | Writer: conf.Config.Output, 53 | ErrWriter: conf.Config.Output, 54 | After: afterRootAction, 55 | Action: rootAction, 56 | } 57 | } 58 | 59 | func afterRootAction(c *cli.Context) error { 60 | return container.NewHandleChain(). 61 | AddToHead(verify.NewPortVerifier(conf.Config.Port)). 62 | AddToHead(verify.NewPathVerifier(conf.Config.RootPath)). 63 | Iterator() 64 | } 65 | 66 | func rootAction(c *cli.Context) error { 67 | conf.Config.Port = c.Int("port") 68 | conf.Config.MaxLevel = uint8(c.Int("max")) 69 | conf.Config.IsAllowUpload = c.Bool("allow") 70 | conf.Config.IsSecure = c.Bool("secure") 71 | if c.IsSet("road") { 72 | road := c.Path("road") 73 | if road[0] == '.' { 74 | pwd, _ := os.Getwd() 75 | road = filepath.Join(pwd, road) 76 | } 77 | conf.Config.RootPath = road 78 | } 79 | // 开启登录的时候查一下是否有设置账号密码 80 | // if c.IsSet("secure") { 81 | 82 | // } 83 | 84 | return nil 85 | } 86 | -------------------------------------------------------------------------------- /internal/server/server.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "log" 7 | "net/http" 8 | "os" 9 | "os/signal" 10 | "strconv" 11 | "syscall" 12 | "time" 13 | 14 | "github.com/TCP404/OneTiny-cli/internal/conf" 15 | "github.com/TCP404/OneTiny-cli/internal/server/middleware" 16 | "github.com/TCP404/OneTiny-cli/internal/server/routes" 17 | "github.com/fatih/color" 18 | "github.com/gin-gonic/gin" 19 | ) 20 | 21 | // RunCore 函数负责启动 gin 实例,开始提供 HTTP 服务 22 | func RunCore() { 23 | var ( 24 | srv = initServer() 25 | q = make(chan os.Signal, 1) 26 | ) 27 | signal.Notify(q, syscall.SIGINT, syscall.SIGTERM) 28 | 29 | { 30 | go run(srv) 31 | <-q 32 | exit(srv) 33 | } 34 | } 35 | 36 | func initServer() *http.Server { 37 | gin.SetMode(gin.ReleaseMode) 38 | r := gin.New() 39 | middleware.Setup(r) 40 | routes.Setup(r) 41 | s := &http.Server{ 42 | Addr: ":" + strconv.Itoa(conf.Config.Port), 43 | Handler: r, 44 | } 45 | return s 46 | } 47 | 48 | func run(srv *http.Server) { 49 | printInfo() 50 | err := srv.ListenAndServe() 51 | if err != nil && err != http.ErrServerClosed { 52 | log.Println(color.RedString(err.Error())) 53 | } 54 | } 55 | 56 | func exit(srv *http.Server) { 57 | ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) 58 | defer cancel() 59 | if err := srv.Shutdown(ctx); err != nil { 60 | log.Println(color.RedString(err.Error())) 61 | } 62 | fmt.Println(color.GreenString("\nbye~")) 63 | os.Exit(0) 64 | } 65 | 66 | // printInfo 会在程序启动后打印本机 IP、共享目录、是否允许上传的信息 67 | func printInfo() { 68 | log.SetOutput(color.Output) 69 | // Print IP information 70 | if conf.Config.IP != "" { 71 | log.Printf("Run on [ %s ]", color.BlueString("http://%s:%d", conf.Config.IP, conf.Config.Port)) 72 | } else { 73 | log.Printf("%s", color.YellowString("Warning: [ 暂时获取不到您的IP,可以打开新的命令行窗口输入 -> ipconfig , 查看您的IP。]")) 74 | } 75 | 76 | // Print RootPath information 77 | log.Printf("Run with [ %s ]", color.BlueString("%s", conf.Config.RootPath)) 78 | 79 | // Print Max allow access level 80 | log.Printf("Allow access level: [ %s ]", color.BlueString("%d", conf.Config.MaxLevel)) 81 | 82 | // Print Allow upload Status 83 | status := color.RedString("%t", conf.Config.IsAllowUpload) 84 | if conf.Config.IsAllowUpload { 85 | status = color.GreenString("%t", conf.Config.IsAllowUpload) 86 | } 87 | log.Printf("Allow upload: [ %s ]", status) 88 | 89 | // Print Secure status 90 | status = color.RedString("%t", conf.Config.IsSecure) 91 | if conf.Config.IsSecure { 92 | status = color.GreenString("%t", conf.Config.IsSecure) 93 | } 94 | log.Printf("Need Login: [ %s ]\n\n", status) 95 | } 96 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/TCP404/OneTiny-cli 2 | 3 | go 1.18 4 | 5 | require ( 6 | github.com/TCP404/eutil v0.0.5 7 | github.com/fatih/color v1.13.0 8 | github.com/gin-contrib/sessions v0.0.5 9 | github.com/gin-gonic/gin v1.7.7 10 | github.com/parnurzeal/gorequest v0.2.16 11 | github.com/schollz/progressbar/v3 v3.8.6 12 | github.com/spf13/viper v1.12.0 13 | github.com/urfave/cli/v2 v2.8.1 14 | ) 15 | 16 | require ( 17 | github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect 18 | github.com/fsnotify/fsnotify v1.5.4 // indirect 19 | github.com/gin-contrib/sse v0.1.0 // indirect 20 | github.com/go-playground/locales v0.14.0 // indirect 21 | github.com/go-playground/universal-translator v0.18.0 // indirect 22 | github.com/go-playground/validator/v10 v10.11.0 // indirect 23 | github.com/golang/protobuf v1.5.2 // indirect 24 | github.com/gorilla/context v1.1.1 // indirect 25 | github.com/gorilla/securecookie v1.1.1 // indirect 26 | github.com/gorilla/sessions v1.2.1 // indirect 27 | github.com/hashicorp/hcl v1.0.0 // indirect 28 | github.com/json-iterator/go v1.1.12 // indirect 29 | github.com/jtolds/gls v4.20.0+incompatible // indirect 30 | github.com/leodido/go-urn v1.2.1 // indirect 31 | github.com/magiconair/properties v1.8.6 // indirect 32 | github.com/mattn/go-colorable v0.1.12 // indirect 33 | github.com/mattn/go-isatty v0.0.14 // indirect 34 | github.com/mattn/go-runewidth v0.0.13 // indirect 35 | github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect 36 | github.com/mitchellh/mapstructure v1.5.0 // indirect 37 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 38 | github.com/modern-go/reflect2 v1.0.2 // indirect 39 | github.com/pelletier/go-toml v1.9.5 // indirect 40 | github.com/pelletier/go-toml/v2 v2.0.1 // indirect 41 | github.com/pkg/errors v0.9.1 // indirect 42 | github.com/rivo/uniseg v0.2.0 // indirect 43 | github.com/russross/blackfriday/v2 v2.1.0 // indirect 44 | github.com/spf13/afero v1.8.2 // indirect 45 | github.com/spf13/cast v1.5.0 // indirect 46 | github.com/spf13/jwalterweatherman v1.1.0 // indirect 47 | github.com/spf13/pflag v1.0.5 // indirect 48 | github.com/subosito/gotenv v1.3.0 // indirect 49 | github.com/ugorji/go/codec v1.2.7 // indirect 50 | github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect 51 | golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e // indirect 52 | golang.org/x/net v0.0.0-20220621193019-9d032be2e588 // indirect 53 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a // indirect 54 | golang.org/x/term v0.0.0-20220526004731-065cf7ba2467 // indirect 55 | golang.org/x/text v0.3.7 // indirect 56 | google.golang.org/protobuf v1.28.0 // indirect 57 | gopkg.in/ini.v1 v1.66.5 // indirect 58 | gopkg.in/yaml.v2 v2.4.0 // indirect 59 | gopkg.in/yaml.v3 v3.0.1 // indirect 60 | moul.io/http2curl v1.0.0 // indirect 61 | ) 62 | -------------------------------------------------------------------------------- /.idea/watcherTasks.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 16 | 28 | 29 | 40 | 52 | 53 | 64 | 76 | 77 | -------------------------------------------------------------------------------- /internal/conf/conf.go: -------------------------------------------------------------------------------- 1 | package conf 2 | 3 | import ( 4 | "errors" 5 | "io" 6 | "log" 7 | "os" 8 | "path/filepath" 9 | "runtime" 10 | 11 | "github.com/TCP404/OneTiny-cli/internal/constant" 12 | "github.com/TCP404/eutil" 13 | "github.com/fatih/color" 14 | "github.com/spf13/viper" 15 | ) 16 | 17 | var Config = &config{ 18 | Output: eutil.If[io.Writer](runtime.GOOS == "windows", color.Output, os.Stderr), 19 | OS: runtime.GOOS, // 程序所在的操作系统,默认值 linux 20 | IP: constant.IP, // 本机局域网IP 21 | Pwd: "/", 22 | 23 | RootPath: constant.RootPath, // 共享目录的根路径,默认值:当前目录 24 | MaxLevel: constant.MaxLevel, // 允许访问的最大层级,默认值 0 25 | Port: constant.Port, // 指定的服务端口,默认值 9090 26 | IsAllowUpload: constant.IsAllowUpload, // 是否允许上传,默认值:否 27 | IsSecure: constant.IsSecure, // 是否开启访问登录,默认值:否 28 | 29 | SessionVal: eutil.RandString(64), 30 | Username: constant.Username, // 访问登录的帐号,默认值:admin 31 | Password: constant.Password, // 访问登录的密码,默认值:admin 32 | } 33 | 34 | type config struct { 35 | Output io.Writer 36 | OS string 37 | IP string 38 | Pwd string 39 | 40 | RootPath string // 共享目录的根路径,默认值:当前目录 41 | MaxLevel uint8 // 允许访问的最大层级,默认值 0 42 | Port int // 指定的服务端口,默认值 9090 43 | IsAllowUpload bool // 是否允许上传,默认值:否 44 | IsSecure bool // 是否开启访问登录,默认值:否 45 | 46 | SessionVal string 47 | Username string // 访问登录的帐号 48 | Password string // 访问登录的密码 49 | } 50 | 51 | func LoadConfig() error { 52 | userCfgDir, err := os.UserConfigDir() 53 | if err != nil { 54 | return errors.New("获取配置目录失败") 55 | } 56 | cfgDir := filepath.Join(userCfgDir, "tiny") 57 | cfgFile := filepath.Join(cfgDir, "config.yml") 58 | err = loadUserConfig(cfgDir, cfgFile) 59 | if err != nil { 60 | return err 61 | } 62 | 63 | Config.IP, err = eutil.GetLocalIP() 64 | if err != nil { 65 | log.Println(color.YellowString("获取不到本机的局域网IP")) 66 | } 67 | 68 | Config.RootPath = viper.GetString("server.road") 69 | if len(Config.RootPath) < 1 { 70 | wd, err := os.Getwd() 71 | if err != nil { 72 | return errors.New("获取不到共享路径") 73 | } 74 | Config.RootPath = wd 75 | Config.Pwd = wd 76 | } 77 | 78 | Config.MaxLevel = uint8(viper.GetInt("server.max_level")) 79 | Config.Port = viper.GetInt("server.port") 80 | Config.IsAllowUpload = viper.GetBool("server.allow_upload") 81 | Config.IsSecure = viper.GetBool("account.secure") 82 | Config.Username = viper.GetString("account.custom.user") 83 | Config.Password = viper.GetString("account.custom.pass") 84 | return nil 85 | } 86 | 87 | // loadUserConfig 负责加载用户配置文件,如果文件不存在则创建并设置默认值 88 | func loadUserConfig(cfgDir, cfgFile string) error { 89 | viper.AddConfigPath(cfgDir) 90 | viper.SetConfigName("config") 91 | viper.SetConfigType("yml") 92 | 93 | read: 94 | if err := viper.ReadInConfig(); err != nil { 95 | switch err.(type) { 96 | case viper.ConfigFileNotFoundError: 97 | log.Println(color.YellowString("未找到「自定义配置文件」, 正在创建中...")) 98 | if err := createCfgFile(cfgDir, cfgFile); err != nil { 99 | return err 100 | } 101 | log.Println(color.GreenString("创建成功,配置文件位于: %s", cfgFile)) 102 | goto read 103 | case viper.ConfigParseError: 104 | return errors.New("已找到「自定义配置文件」,但是解析失败!") 105 | case viper.ConfigMarshalError: 106 | return errors.New("已找到「自定义配置文件」,但是读取失败!") 107 | } 108 | } 109 | return nil 110 | } 111 | 112 | func createCfgFile(cfgDir, cfgFile string) error { 113 | _, err := os.Stat(cfgDir) 114 | if os.IsNotExist(err) { 115 | _ = os.MkdirAll(cfgDir, os.ModePerm) 116 | } 117 | _, err = os.Create(cfgFile) 118 | if err != nil { 119 | return errors.New("创建自定义配置文件失败!") 120 | } 121 | if err := setDefault(); err != nil { 122 | log.Println(color.YellowString("设置默认配置失败!")) 123 | } 124 | return nil 125 | } 126 | 127 | func setDefault() error { 128 | viper.Set("server.port", constant.Port) 129 | viper.Set("server.allow_upload", constant.IsAllowUpload) 130 | viper.Set("server.max_level", constant.MaxLevel) 131 | return viper.WriteConfig() 132 | } 133 | -------------------------------------------------------------------------------- /cmd/secure.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | // "github.com/TCP404/OneTiny-cli/common/verify" 5 | 6 | "errors" 7 | 8 | "github.com/TCP404/eutil" 9 | "github.com/fatih/color" 10 | "github.com/spf13/viper" 11 | "github.com/urfave/cli/v2" 12 | ) 13 | 14 | func secureCmd() *cli.Command { 15 | return &cli.Command{ 16 | Name: "sec", 17 | Aliases: []string{"s"}, 18 | Usage: "设置访问登录的账户和密码", 19 | UsageText: "onetiny sec [OPTIONS]", 20 | Description: "使用 onetiny sec 命令可以设置访问登录的帐号密码。\n允许的命令形式如下:\n 注册并开启:\t onetiny sec -u=账户名 -p=密码 -s\n 注册/覆盖账户:onetiny sec -u=账户名 -p=密码\n 重设密码:\t\t onetiny sec -p=密码", 21 | Flags: []cli.Flag{ 22 | &cli.StringFlag{ 23 | Name: "user", 24 | Aliases: []string{"u"}, 25 | Usage: "设置访问登录的`账户`名", 26 | Required: false, 27 | }, 28 | &cli.StringFlag{ 29 | Name: "pass", 30 | Aliases: []string{"p"}, 31 | Usage: "设置访问登录的`密码`", 32 | Required: false, 33 | }, 34 | &cli.BoolFlag{ 35 | Name: "secure", 36 | Aliases: []string{"s"}, 37 | Usage: "设置`开启`访问登录,效果同 onetiny -s 一样", 38 | Required: false, 39 | }, 40 | }, 41 | Action: func(c *cli.Context) error { 42 | weight, err := secureAction(c) 43 | if err != nil { 44 | return cli.Exit(color.RedString(err.Error()), 21) 45 | } 46 | if weight == 0 { 47 | return cli.Exit(color.GreenString("当前访问登录是否已开启: %t", viper.GetBool("account.secure")), 0) 48 | } 49 | if err := viper.WriteConfig(); err != nil { 50 | return cli.Exit(color.RedString("配置文件写入失败~"), 22) 51 | } 52 | return cli.Exit(color.GreenString("设置成功~"), 0) 53 | }, 54 | } 55 | } 56 | 57 | // ups 是 User、Pass、Secure 三个单词的首字母合并, 58 | // 用于设置 帐号、密码、访问登录 三个配置项的权值 59 | // 用法和 unix 系统中文件权限的 rwx 相同 60 | type ups uint8 61 | 62 | // USER 表示 已设置账户 63 | // PASS 表示 已设置密码 64 | // SECU 表示 已开启访问登录 65 | const ( 66 | USER ups = 1 << 0 // The weight of USER is 1 67 | PASS ups = 1 << 1 // The weight of PASS is 2 68 | SECU ups = 1 << 2 // The weight of SECU is 4 69 | ) 70 | 71 | func secureAction(c *cli.Context) (ups, error) { 72 | var weight ups 73 | // 当填写了 -s 选项并且 -s 的值为 true 时才设置 74 | if is, s := c.IsSet("secure"), c.Bool("secure"); is { 75 | if s { 76 | weight |= SECU 77 | } 78 | viper.Set("account.secure", s) 79 | } 80 | // 当填写了 -u 选项并且 -u 的值不为 空 时才设置 81 | if is, u := c.IsSet("user"), c.String("user"); is && u != "" { 82 | weight |= USER 83 | viper.Set("account.custom.user", eutil.MD5(u)) 84 | } 85 | // 当填写了 -p 选项并且 -p 的值不为 空 时才设置 86 | if is, p := c.IsSet("pass"), c.String("pass"); is && p != "" { 87 | weight |= PASS 88 | viper.Set("account.custom.pass", eutil.MD5(p)) 89 | } 90 | return weight, Handle(weight) 91 | } 92 | 93 | // Handle 检查UPS 94 | // 000 (0) 表示 用户仅仅执行了命令 `onetiny sec`,返回访问登录开启状态 95 | // 001 (1) 表示 开启访问登录 96 | // 010 (2) 表示 设置密码 97 | // 011 (3) 表示 开启访问登录,并设置密码 98 | // 100 (4) 表示 设置用户名 99 | // 101 (5) 表示 开启访问登录,并设置帐户名 100 | // 110 (6) 表示 设置账户、密码 101 | // 111 (7) 表示 开启访问登录,并设置账户、密码 102 | // 103 | // 设置规则: 104 | // 开启访问登录时,需配置文件中已设置帐号密码 105 | // 设置密码时,需配置文件中已设置账户 106 | // 设置账户时,需配置文件中已设置密码 107 | func Handle(weight ups) error { 108 | switch weight { 109 | case USER | PASS | SECU: 110 | // 111 开启访问登录,并设置账户和密码 111 | return nil 112 | case USER | PASS: 113 | // 110 设置账户和密码 114 | return nil 115 | case USER | SECU: 116 | // 101 开启访问登录,并设置用户名,穿透下去检查是否有密码 117 | fallthrough 118 | case USER: 119 | // 100 设置用户名,需配置文件中有密码 120 | return eutil.If(viper.GetString("account.custom.pass") != "", nil, errors.New("未找到您的帐号,请使用 `onetiny sec -u=帐号 -p=密码` 进行设置。")) 121 | case PASS | SECU: 122 | // 011 开启访问登录,并设置密码,穿透下去检查是否有帐户名 123 | fallthrough 124 | case PASS: 125 | // 010 设置密码,需配置文件中有账户名 126 | return eutil.If(viper.GetString("account.custom.user") != "", nil, errors.New("未找到您的帐号,请使用 `onetiny sec -u=帐号 -p=密码` 进行设置。")) 127 | case SECU: 128 | // 001 开启访问登录 129 | return eutil.If(viper.GetString("account.custom.user") != "" && viper.GetString("account.custom.pass") != "", nil, errors.New("开启访问登录需先设置帐号密码,请使用 `onetiny sec -u=帐号 -p=密码` 进行设置。")) 130 | case 0: 131 | // 000 打印当前是否开启访问登录 132 | return nil 133 | default: 134 | return errors.New("设置失败~") 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /internal/handle/core/download.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "bufio" 5 | "errors" 6 | "io" 7 | "log" 8 | "mime" 9 | "net/http" 10 | "os" 11 | "path" 12 | "path/filepath" 13 | "strconv" 14 | "strings" 15 | 16 | "github.com/TCP404/OneTiny-cli/internal/conf" 17 | "github.com/TCP404/OneTiny-cli/internal/constant" 18 | "github.com/TCP404/OneTiny-cli/internal/handle" 19 | "github.com/TCP404/OneTiny-cli/pkg" 20 | "github.com/TCP404/eutil" 21 | "github.com/gin-gonic/gin" 22 | ) 23 | 24 | type fileStructure struct { 25 | Size string 26 | IsDir bool 27 | DeviceAbsPath string 28 | URLRelPath string 29 | Name string 30 | } 31 | type agent struct { 32 | abs string 33 | rel string 34 | action string 35 | isDir bool 36 | } 37 | 38 | func Downloader(c *gin.Context) { 39 | road := c.GetString("filename") 40 | a := &agent{ 41 | abs: filepath.Join(conf.Config.RootPath, road), 42 | rel: road, 43 | action: c.Query("action"), 44 | isDir: c.GetBool("isDirectory"), 45 | } 46 | 47 | if a.rel == constant.ROOT { 48 | a.readDir(c) 49 | return 50 | } 51 | if a.isDir { 52 | a.dir(c) 53 | return 54 | } 55 | a.file(c) 56 | } 57 | 58 | func (a *agent) file(c *gin.Context) { 59 | src, err := os.Open(a.abs) 60 | if err != nil { 61 | handle.ErrorHandle(c, err.Error()) 62 | } 63 | defer func(src *os.File) { _ = src.Close() }(src) 64 | 65 | log.Println("preparing file...") 66 | contentLen := getContentLen(a.abs) 67 | 68 | c.Status(http.StatusOK) 69 | if a.action == "dl" { 70 | c.Header("Content-Disposition", "attachment; filename="+filepath.Base(a.rel)) 71 | c.Header("Content-Length", strconv.Itoa(int(contentLen))) 72 | } 73 | 74 | buf := make([]byte, constant.BufferLimit) 75 | bar := pkg.GetBar(a.rel, contentLen, conf.Config.Output) 76 | 77 | // 小于 buf 时直接读取到 buf 中然后返回 78 | if contentLen < int64(len(buf)) { 79 | _, _ = io.CopyBuffer(io.MultiWriter(c.Writer, bar), src, buf) 80 | return 81 | } 82 | // 超过 buf 的大小时分片传输 83 | a.flush(c, src, buf) 84 | } 85 | 86 | func (a *agent) dir(c *gin.Context) { 87 | if a.action != "dl" { 88 | a.readDir(c) 89 | return 90 | } 91 | 92 | log.Println("preparing archive directory...") 93 | contentLen := getContentLen(a.abs) 94 | c.Status(http.StatusOK) 95 | c.Header("Content-Disposition", "attachment; filename="+filepath.Base(a.rel)+".zip") 96 | c.Header("Content-Length", strconv.Itoa(int(contentLen))) 97 | c.Header("Content-Type", mime.TypeByExtension("zip")) 98 | 99 | // 创建准备写入的压缩文件 100 | srcZip, err := os.CreateTemp(os.TempDir(), "temp.*.zip") 101 | if err != nil { 102 | handle.ErrorHandle(c, "压缩目录失败") 103 | } 104 | defer func(srcZip *os.File) { 105 | _ = srcZip.Close() 106 | _ = os.Remove(srcZip.Name()) 107 | }(srcZip) 108 | 109 | err = eutil.Zip(srcZip, filepath.Join(conf.Config.RootPath, a.rel)) 110 | if err != nil { 111 | handle.ErrorHandle(c, "压缩目录失败, "+err.Error()) 112 | } 113 | 114 | buf := make([]byte, constant.BufferLimit) 115 | if contentLen < int64(len(buf)) { 116 | c.File(srcZip.Name()) 117 | return 118 | } 119 | a.flush(c, srcZip, buf) 120 | } 121 | 122 | func (a *agent) readDir(c *gin.Context) { 123 | files := getFileInfos(c, filepath.Join(conf.Config.RootPath, a.rel)) 124 | c.HTML(http.StatusOK, "list.tpl", gin.H{ 125 | "pathTitle": a.rel, 126 | "upload": conf.Config.IsAllowUpload, 127 | "files": files, 128 | }) 129 | } 130 | 131 | func getFileInfos(c *gin.Context, absPath string) []fileStructure { 132 | dirEntries, err := os.ReadDir(absPath) 133 | if err != nil { 134 | handle.ErrorHandle(c, "目录读取失败!") 135 | return nil 136 | } 137 | 138 | relPath := strings.TrimPrefix(absPath, conf.Config.RootPath) 139 | fileInfos := make([]fileStructure, len(dirEntries)) 140 | 141 | for i, f := range dirEntries { 142 | info, _ := f.Info() 143 | size := info.Size() 144 | deviceAbsPath := filepath.Join(absPath, f.Name()) 145 | urlRelPath := path.Join(constant.FileGroupPrefix, relPath, f.Name()) 146 | isDir := f.Type().IsDir() 147 | 148 | if isDir { // 将目录的 size 设置为 0,文件则照常 149 | size = 0 150 | deviceAbsPath += string(filepath.Separator) // 如果是目录,不在路径末尾加上分隔符的话,返回上一级会有问题 151 | urlRelPath += string(filepath.Separator) 152 | } 153 | fileInfos[i] = fileStructure{ 154 | DeviceAbsPath: deviceAbsPath, 155 | URLRelPath: urlRelPath, 156 | Name: f.Name(), 157 | Size: eutil.SizeFmt(size), 158 | IsDir: isDir, 159 | } 160 | } 161 | return fileInfos 162 | } 163 | 164 | func (a *agent) flush(c *gin.Context, src io.Reader, buf []byte) { 165 | data := bufio.NewReader(src) 166 | bar := pkg.GetBar(a.rel, getContentLen(a.abs), conf.Config.Output) 167 | 168 | for { 169 | _, err := data.Read(buf) 170 | if errors.Is(err, io.EOF) { 171 | break 172 | } 173 | if err != nil { 174 | handle.ErrorHandle(c, "server error!") 175 | return 176 | } 177 | _, _ = bar.Write(buf) 178 | _, _ = c.Writer.Write(buf) 179 | c.Writer.(http.Flusher).Flush() 180 | } 181 | } 182 | 183 | func getContentLen(absPath string) int64 { 184 | var contentLen int64 = -1 185 | info, err := os.Stat(absPath) 186 | if err == nil { 187 | contentLen = info.Size() 188 | } 189 | return contentLen 190 | } 191 | -------------------------------------------------------------------------------- /cmd/update.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | "fmt" 7 | "os" 8 | "path/filepath" 9 | "strings" 10 | 11 | "github.com/TCP404/OneTiny-cli/internal/conf" 12 | "github.com/TCP404/OneTiny-cli/internal/constant" 13 | 14 | "github.com/TCP404/eutil" 15 | "github.com/fatih/color" 16 | "github.com/parnurzeal/gorequest" 17 | 18 | "github.com/urfave/cli/v2" 19 | ) 20 | 21 | func updateCmd() *cli.Command { 22 | return &cli.Command{ 23 | Name: "update", 24 | Aliases: []string{"u", "up"}, 25 | Usage: "更新 OneTiny 到最新版", 26 | Flags: []cli.Flag{ 27 | &cli.BoolFlag{ 28 | Name: "list", 29 | Aliases: []string{"l"}, 30 | Usage: "列出远程服务器上所有可用版本", 31 | Required: false, 32 | DefaultText: "false", 33 | }, 34 | &cli.StringFlag{ 35 | Name: "use", 36 | Usage: "指定版本号", 37 | Required: false, 38 | }, 39 | }, 40 | Action: updateAction, 41 | } 42 | } 43 | 44 | type update struct { 45 | currVersion []string 46 | msg string 47 | } 48 | 49 | func updateAction(c *cli.Context) error { 50 | var u = &update{currVersion: splitVersion(constant.VERSION)} 51 | err := u.updateAction(c) 52 | if err != nil { 53 | return cli.Exit(err.Error(), 31) 54 | } 55 | return cli.Exit(u.msg, 0) 56 | } 57 | 58 | type ReleaseInfo struct { 59 | TagName string `json:"tag_name"` 60 | Assets []ReleaseAsset `json:"assets"` 61 | } 62 | 63 | type ReleaseAsset struct { 64 | URL string `json:"url"` 65 | ID int `json:"id"` 66 | Name string `json:"name"` 67 | ContentType string `json:"content_type"` 68 | Size int `json:"size"` 69 | DownloadURL string `json:"browser_download_url"` 70 | } 71 | 72 | type TagList struct { 73 | TagName string `json:"name"` 74 | } 75 | 76 | func (u *update) updateAction(c *cli.Context) error { 77 | switch { 78 | case c.IsSet("list"): 79 | return u.updateList() 80 | case c.IsSet("use"): 81 | return u.updateVersion(c.String("use")) 82 | } 83 | return u.updateLatest() 84 | } 85 | 86 | func (u *update) updateList() error { 87 | tags, err := getVersionList() 88 | if err != nil { 89 | return err 90 | } 91 | for _, tag := range tags { 92 | fmt.Println(color.GreenString("%v", tag.TagName)) 93 | } 94 | return nil 95 | } 96 | 97 | func (u *update) updateVersion(version string) error { 98 | if err := checkVersion(version); err != nil { 99 | return err 100 | } 101 | 102 | _, body, errs := gorequest.New().Get(constant.VersionByTagURL + version).End() 103 | if len(errs) != 0 { 104 | return errors.New("网络抖动了一下~请重试") 105 | } 106 | var versionInfo = new(ReleaseInfo) 107 | err := json.Unmarshal([]byte(body), versionInfo) 108 | if err != nil { 109 | return errors.New("网络抖动了一下~请重试") 110 | } 111 | 112 | // 检查当前系统是 linux 还是 mac 还是 windows决定 Assets 用哪个,然后进行下载 113 | name, ok := constant.ReleaseName[conf.Config.OS] 114 | if !ok { 115 | u.msg = color.YellowString("暂时没有适合您的系统的版本,请自行下载编译") 116 | return nil 117 | } 118 | var ( 119 | assert *ReleaseAsset 120 | l = len(versionInfo.Assets) 121 | ) 122 | for i := 0; i < l; i++ { 123 | if versionInfo.Assets[i].Name == name { 124 | assert = &versionInfo.Assets[i] 125 | break 126 | } 127 | } 128 | if assert == nil { 129 | u.msg = color.YellowString("暂时没有适合您的系统的版本,请自行下载编译") 130 | return nil 131 | } 132 | 133 | // 进行下载 134 | p, err := os.UserHomeDir() 135 | if err != nil { 136 | u.msg = color.HiYellowString("获取 Home 目录失败") 137 | p = conf.Config.Pwd 138 | } 139 | path := filepath.Join(p, assert.Name) 140 | errs = eutil.DownloadBinary(assert.DownloadURL, path) 141 | if len(errs) != 0 { 142 | return errors.New("网络抖动了一下~请重试") 143 | } 144 | u.msg += color.HiGreenString("更新完成~, 文件存放于: %s", path) 145 | return nil 146 | } 147 | 148 | func (u *update) updateLatest() error { 149 | // 获取当前最新版本 150 | req := gorequest.New() 151 | _, body, errs := req.Get(constant.VersionLatestURL).End() 152 | if len(errs) != 0 { 153 | return errors.New("网络抖动了一下~请重试") 154 | } 155 | 156 | var latestInfo = new(ReleaseInfo) 157 | err := json.Unmarshal([]byte(body), latestInfo) 158 | if err != nil { 159 | return errors.New("网络抖动了一下~请重试") 160 | } 161 | 162 | // 检查最新版本与当前版本 163 | latestVersion := splitVersion(latestInfo.TagName) 164 | if u.isLatest(latestVersion) { 165 | u.msg = color.GreenString("当前已是最新版本~") 166 | return nil 167 | } 168 | // 进行更新 169 | return u.updateVersion(latestInfo.TagName) 170 | } 171 | 172 | func (u *update) isLatest(version []string) bool { 173 | max := len(version) 174 | if max > len(u.currVersion) { 175 | max = len(u.currVersion) 176 | } 177 | 178 | for i := 0; i < max; i++ { 179 | if version[i] < u.currVersion[i] { 180 | return false 181 | } 182 | } 183 | return true 184 | } 185 | 186 | func splitVersion(version string) (v []string) { 187 | var major, minor, revision = "0", "0", "0" 188 | sArr := strings.Split(strings.TrimLeft(version, "v"), ".") 189 | if len(sArr) >= 3 { 190 | revision = sArr[2] 191 | } 192 | if len(sArr) >= 2 { 193 | minor = sArr[1] 194 | } 195 | if len(sArr) >= 1 { 196 | major = sArr[0] 197 | } 198 | return append(v, major, minor, revision) 199 | } 200 | 201 | func checkVersion(version string) error { 202 | tags, err := getVersionList() 203 | if err != nil { 204 | return err 205 | } 206 | for _, tag := range tags { 207 | if strings.Compare(tag.TagName, version) == 0 { 208 | return nil 209 | } 210 | } 211 | return errors.New("找不到您指定的版本,请检查您输入的版本号") 212 | } 213 | 214 | func getVersionList() ([]TagList, error) { 215 | _, body, errs := gorequest.New().Set("Accept", "application/vnd.github.v3+json").Get(constant.VersionListURL).End() 216 | if len(errs) != 0 { 217 | return nil, errors.New("网络抖动了一下~请重试") 218 | } 219 | var tags []TagList 220 | err := json.Unmarshal([]byte(body), &tags) 221 | if err != nil { 222 | return nil, errors.New("网络抖动了一下~请重试") 223 | } 224 | return tags, nil 225 | } 226 | -------------------------------------------------------------------------------- /resource/template/login.tpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | OneTiny 6 | 161 | 162 | 163 | 164 |
165 |
166 |
167 |

登录

168 |
169 |
170 | 171 |
172 | 173 |
174 |
175 | 176 |
177 | 178 |
179 |
180 |
181 | 182 | 216 | 217 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.toptal.com/developers/gitignore/api/windows,macos,linux,visualstudiocode,goland,sublimetext 3 | # Edit at https://www.toptal.com/developers/gitignore?templates=windows,macos,linux,visualstudiocode,goland,sublimetext 4 | 5 | ### GoLand ### 6 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider 7 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 8 | 9 | # User-specific stuff 10 | .idea/**/workspace.xml 11 | .idea/**/tasks.xml 12 | .idea/**/usage.statistics.xml 13 | .idea/**/dictionaries 14 | .idea/**/shelf 15 | 16 | # AWS User-specific 17 | .idea/**/aws.xml 18 | 19 | # Generated files 20 | .idea/**/contentModel.xml 21 | 22 | # Sensitive or high-churn files 23 | .idea/**/dataSources/ 24 | .idea/**/dataSources.ids 25 | .idea/**/dataSources.local.xml 26 | .idea/**/sqlDataSources.xml 27 | .idea/**/dynamic.xml 28 | .idea/**/uiDesigner.xml 29 | .idea/**/dbnavigator.xml 30 | 31 | # Gradle 32 | .idea/**/gradle.xml 33 | .idea/**/libraries 34 | 35 | # Gradle and Maven with auto-import 36 | # When using Gradle or Maven with auto-import, you should exclude module files, 37 | # since they will be recreated, and may cause churn. Uncomment if using 38 | # auto-import. 39 | # .idea/artifacts 40 | # .idea/compiler.xml 41 | # .idea/jarRepositories.xml 42 | # .idea/modules.xml 43 | # .idea/*.iml 44 | # .idea/modules 45 | # *.iml 46 | # *.ipr 47 | 48 | # CMake 49 | cmake-build-*/ 50 | 51 | # Mongo Explorer plugin 52 | .idea/**/mongoSettings.xml 53 | 54 | # File-based project format 55 | *.iws 56 | 57 | # IntelliJ 58 | out/ 59 | 60 | # mpeltonen/sbt-idea plugin 61 | .idea_modules/ 62 | 63 | # JIRA plugin 64 | atlassian-ide-plugin.xml 65 | 66 | # Cursive Clojure plugin 67 | .idea/replstate.xml 68 | 69 | # SonarLint plugin 70 | .idea/sonarlint/ 71 | 72 | # Crashlytics plugin (for Android Studio and IntelliJ) 73 | com_crashlytics_export_strings.xml 74 | crashlytics.properties 75 | crashlytics-build.properties 76 | fabric.properties 77 | 78 | # Editor-based Rest Client 79 | .idea/httpRequests 80 | 81 | # Android studio 3.1+ serialized cache file 82 | .idea/caches/build_file_checksums.ser 83 | 84 | ### GoLand Patch ### 85 | # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 86 | 87 | # *.iml 88 | # modules.xml 89 | # .idea/misc.xml 90 | # *.ipr 91 | 92 | # Sonarlint plugin 93 | # https://plugins.jetbrains.com/plugin/7973-sonarlint 94 | .idea/**/sonarlint/ 95 | 96 | # SonarQube Plugin 97 | # https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin 98 | .idea/**/sonarIssues.xml 99 | 100 | # Markdown Navigator plugin 101 | # https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced 102 | .idea/**/markdown-navigator.xml 103 | .idea/**/markdown-navigator-enh.xml 104 | .idea/**/markdown-navigator/ 105 | 106 | # Cache file creation bug 107 | # See https://youtrack.jetbrains.com/issue/JBR-2257 108 | .idea/$CACHE_FILE$ 109 | 110 | # CodeStream plugin 111 | # https://plugins.jetbrains.com/plugin/12206-codestream 112 | .idea/codestream.xml 113 | 114 | ### Linux ### 115 | *~ 116 | 117 | # temporary files which can be created if a process still has a handle open of a deleted file 118 | .fuse_hidden* 119 | 120 | # KDE directory preferences 121 | .directory 122 | 123 | # Linux trash folder which might appear on any partition or disk 124 | .Trash-* 125 | 126 | # .nfs files are created when an open file is removed but is still being accessed 127 | .nfs* 128 | 129 | ### macOS ### 130 | # General 131 | .DS_Store 132 | .AppleDouble 133 | .LSOverride 134 | 135 | # Icon must end with two \r 136 | Icon 137 | 138 | 139 | # Thumbnails 140 | ._* 141 | 142 | # Files that might appear in the root of a volume 143 | .DocumentRevisions-V100 144 | .fseventsd 145 | .Spotlight-V100 146 | .TemporaryItems 147 | .Trashes 148 | .VolumeIcon.icns 149 | .com.apple.timemachine.donotpresent 150 | 151 | # Directories potentially created on remote AFP share 152 | .AppleDB 153 | .AppleDesktop 154 | Network Trash Folder 155 | Temporary Items 156 | .apdisk 157 | 158 | ### macOS Patch ### 159 | # iCloud generated files 160 | *.icloud 161 | 162 | ### SublimeText ### 163 | # Cache files for Sublime Text 164 | *.tmlanguage.cache 165 | *.tmPreferences.cache 166 | *.stTheme.cache 167 | 168 | # Workspace files are user-specific 169 | *.sublime-workspace 170 | 171 | # Project files should be checked into the repository, unless a significant 172 | # proportion of contributors will probably not be using Sublime Text 173 | # *.sublime-project 174 | 175 | # SFTP configuration file 176 | sftp-config.json 177 | sftp-config-alt*.json 178 | 179 | # Package control specific files 180 | Package Control.last-run 181 | Package Control.ca-list 182 | Package Control.ca-bundle 183 | Package Control.system-ca-bundle 184 | Package Control.cache/ 185 | Package Control.ca-certs/ 186 | Package Control.merged-ca-bundle 187 | Package Control.user-ca-bundle 188 | oscrypto-ca-bundle.crt 189 | bh_unicode_properties.cache 190 | 191 | # Sublime-github package stores a github token in this file 192 | # https://packagecontrol.io/packages/sublime-github 193 | GitHub.sublime-settings 194 | 195 | ### VisualStudioCode ### 196 | .vscode/* 197 | !.vscode/settings.json 198 | !.vscode/tasks.json 199 | !.vscode/launch.json 200 | !.vscode/extensions.json 201 | !.vscode/*.code-snippets 202 | 203 | # Local History for Visual Studio Code 204 | .history/ 205 | 206 | # Built Visual Studio Code Extensions 207 | *.vsix 208 | 209 | ### VisualStudioCode Patch ### 210 | # Ignore all local history of files 211 | .history 212 | .ionide 213 | 214 | # Support for Project snippet scope 215 | .vscode/*.code-snippets 216 | 217 | # Ignore code-workspaces 218 | *.code-workspace 219 | 220 | ### Windows ### 221 | # Windows thumbnail cache files 222 | Thumbs.db 223 | Thumbs.db:encryptable 224 | ehthumbs.db 225 | ehthumbs_vista.db 226 | 227 | # Dump file 228 | *.stackdump 229 | 230 | # Folder config file 231 | [Dd]esktop.ini 232 | 233 | # Recycle Bin used on file shares 234 | $RECYCLE.BIN/ 235 | 236 | # Windows Installer files 237 | *.cab 238 | *.msi 239 | *.msix 240 | *.msm 241 | *.msp 242 | 243 | # Windows shortcuts 244 | *.lnk 245 | 246 | 247 | ### Custom ### 248 | exe/ 249 | 250 | # End of https://www.toptal.com/developers/gitignore/api/windows,macos,linux,visualstudiocode,goland,sublimetext -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | > The documentation was written on 06 June 2021. 2 | 3 |

4 | logo 5 |

6 |

OneTiny

7 |
8 | 9 | BLOG 10 | 11 |
12 |
13 |
14 | 局域网文件共享工具 15 |
16 |
17 | 18 |
19 | 20 | 21 | platform 22 | platform 23 | platform 24 |
25 |
26 | 27 | 28 | GitHub release 29 | 30 | 31 | GitHub 32 | 33 | 34 | language 35 | 36 |
37 |
38 | 39 | 40 | OneTiny 是一个用于局域网内共享文件的微型程序,它能将当前工作目录临时共享目录,对局域网内其他主机共享,通过浏览器访问 `http://局域网IP:8192` 来访问和下载共享目录中的文件。 41 | 42 | 简而言之与命令 `python -m http.server 8192` 做的是同样的事情。 43 | 44 | ## [需求] 45 | 46 | 我有两台设备,一台装着 Linux 系统,一台装着 Windows 系统,偶尔需要互相传输文件。 47 | 48 | 在 Linux 上我可以在任意一个目录下使用命令 `python -m http.server 8192`,从而在 Windows 上或局域网内其他主机上通过浏览器访问 `http://局域网IP:8192` 查看所有文件,也可以下载; 49 | 50 | 但是这条命令在 Windows 上不可行,所以需要编写一个程序可以运行在 Windows 上实现同样的功能。 51 | 52 | ## [开发技术] 53 | - 核心功能:[gin](https://gin-gonic.com/zh-cn/) 54 | - 配置管理:[viper](https://github.com/spf13/viper) 55 | - CLI管理:[urfave/cli](https://github.com/urfave/cli/v2) 56 | 57 | ## [使用说明] 58 | 可从本仓库的 [Release](https://github.com/TCP404/OneTiny/releases/) 中下载对应版本。已提供 [Linux 版](https://github.com/TCP404/OneTiny/releases/download/v0.5.0/OneTiny)、[Windows 版](https://github.com/TCP404/OneTiny/releases/download/v0.5.0/OneTiny.exe),[Mac 版](https://github.com/TCP404/OneTiny/releases/download/v0.5.0/OneTiny_mac),其他系统的同学请下载后自行编译。 59 | 60 | ### [下载] 61 | **Linux**、**Mac**: 62 | ```bash 63 | $ curl -LJ https://github.com/TCP404/OneTiny/releases/download/v0.5.0/OneTiny -o onetiny 64 | $ chmod u+x ./onetiny # 赋予执行权限 65 | $ cp onetiny /usr/bin # 复制到可执行文件目录并修改可执行文件名称 66 | ``` 67 | 68 | > Mac 用户注意: 69 | > 70 | > 第一次打开会提示未验证开发者,可以打开 「访达」 ,打开文件所在目录,在 `onetiny` 文件处右键打开。 71 | > 之后就可以直接用命令行运行了。 72 | 73 | **Windows** 74 | 75 | :point_right: [![](https://img.shields.io/github/release/TCP404/OneTiny.svg?style=flat-square&color=1296DB&sort=semver)](https://github.com/TCP404/OneTiny/releases/latest) 76 | 77 | 点击最新版本的 `OneTiny.exe` 进行下载 78 | 79 | > Windows 用户注意: 80 | > 下载时可能会有损害计算机提示,点击 「仍然保留」 即可。 81 | 82 | 83 | 84 | 85 | ### 安装(其他系统必选,Linux、Windows、MacOS 可选) 86 | **需先安装[Golang](https://golang.org)** 87 | ```bash 88 | $ git clone https://github.com/TCP404/OneTiny.git 89 | $ go mod tidy 90 | $ go build 91 | ``` 92 | 93 | ### [Command Map] 94 | ``` 95 | onetiny 96 | ├── 不指定时可直接运行,采用默认值 97 | ├── --road -r 指定共享目录路径 98 | ├── --port -p 指定访问端口 99 | ├── --allow -a 指定是否允许上传 100 | ├── --max -x 指定最大访问层级 101 | ├── --secure -s 指定是否开启访问登录 102 | │ 103 | ├── config 「配置命令,用于自定义默认值」 104 | │ ├── --road -r 设置共享目录路径 105 | │ ├── --port -p 设置访问端口 106 | │ ├── --allow -a 设置是否允许上传 107 | │ ├── --max -x 设置最大访问层级 108 | │ └── --secure -s 设置是否开启访问登录 109 | │ 110 | ├── update 「更新命令,用于更新版本」 111 | │ ├── 不指定时默认更新到最新版本 112 | │ ├── --list -l 列出所有版本 113 | │ └── --use 更新到指定版本 114 | │ 115 | └── sec 「配置用于访问登录时的账号密码」 116 | ├── --user -u 设置登录时的帐号 117 | ├── --pass -p 设置登录时的密码 118 | └── --secure -s 设置是否开启访问登录 119 | ``` 120 | 121 | ### [运行] 122 | **Windows**: 123 | 下载后双击 `OneTiny.exe` 即可运行(需管理员权限)。 124 | 可以在CMD中切换到 `OneTiny.exe` 所在目录,执行以下任一命令: 125 | ```cmd 126 | > OneTiny # 将运行在 http://本机局域网IP:8192,共享目录为当前工作目录,禁止上传 127 | > OneTiny.exe # 将运行在 http://本机局域网IP:8192,共享目录为当前工作目录,禁止上传 128 | > OneTiny -p {端口号} # 将运行在 http://本机局域网IP:端口号,共享目录为当前工作目录,禁止上传 129 | > OneTiny -r {共享目录绝对路径} # 将运行在 http://本机局域网IP:8192,共享目录为指定目录,禁止上传 130 | > OneTiny -a [1|t|T|true|True|TRUE|空] # 将运行在 http://本机局域网IP:8192,共享目录为当前工作目录,允许上传 131 | > OneTiny -x {0~255} # 将运行在 http://本机局域网IP:8192,共享目录为当前工作目录,禁止上传,默认仅允许访问共享目录一层,禁止进入下一层目录 132 | > onetiny -s [1|t|T|true|True|TRUE|空] # 开启后访问时需登录帐号密码 133 | 134 | 配置命令,配置后仅使用 onetiny 命令运行,即可使用配置好的端口、路径、层级、上传允许状态等,无需再次指定 135 | > onetiny c -p {端口号} # 将运行在 http://本机局域网IP:端口号,共享目录为当前工作目录,禁止上传 136 | > onetiny cf -r {共享目录绝对路径} # 将运行在 http://本机局域网IP:8192,共享目录为指定工作目录,禁止上传 137 | > onetiny conf -a [1|t|T|true|True|TRUE|空] # 将运行在 http://本机局域网IP:8192,共享目录为当前工作目录,允许上传 138 | > onetiny config -x {0~255} # 将运行在 http://本机局域网IP:8192,共享目录为当前工作目录,禁止上传,默认仅允许访问共享目录一层,禁止进入下一层目录 139 | > onetiny -s [1|t|T|true|True|TRUE|空] # 配置后访问时需登录帐号密码 140 | 141 | 开启访问登录,需先设置账号密码 142 | > onetiny s -u boii -p=123 # 设置帐号名为 boii,密码为 123 143 | > onetiny sec -s # 设置开启访问登录 144 | > onetiny sec -u=http403 # 重设帐号名为 http403 145 | > onetiny s -p 1234 # 重设密码为 1234 146 | > onetiny sec # 查看当前访问登录状态 147 | 148 | 更新命令 149 | > onetiny u # 更新到最新版本 150 | > onetiny up # 更新到最新版本 151 | > onetiny update # 更新到最新版本 152 | > onetiny update -l # 列出所有版本 153 | > onetiny updata --use # 更新到指定版本 154 | 155 | > onetiny -h # 打印帮助信息 156 | > onetiny --help 157 | > onetiny -v # 打印版本信息 158 | > onetiny --version 159 | 160 | > OneTiny -r=C:\Users\Boii -p=8192 -a -x=2 # 将运行在 「http://本机局域网IP:8192」,共享目录为 「C:\Users\Boii」 ,允许上传,允许访问共享目录往下2层 161 | ``` 162 | 163 | **Linux**、**Mac**: 164 | ```bash 165 | $ onetiny # 将运行在 http://本机局域网IP:8192,共享目录为当前工作目录,禁止上传 166 | $ onetiny -p {端口号} # 将运行在 http://本机局域网IP:端口号,共享目录为当前工作目录,禁止上传 167 | $ onetiny -r {共享目录绝对路径} # 将运行在 http://本机局域网IP:8192,共享目录为指定工作目录,禁止上传 168 | $ onetiny -a [1|t|T|true|True|TRUE|空] # 将运行在 http://本机局域网IP:8192,共享目录为当前工作目录,允许上传 169 | $ onetiny -x {0~255} # 将运行在 http://本机局域网IP:8192,共享目录为当前工作目录,禁止上传,默认仅允许访问共享目录一层,禁止进入下一层目录 170 | $ onetiny -s [1|t|T|true|True|TRUE|空] # 开启后访问时需登录帐号密码 171 | 172 | 配置命令,配置后仅使用 onetiny 命令运行,即可使用配置好的端口、路径、层级、上传允许状态等 173 | $ onetiny c -p {端口号} # 将运行在 http://本机局域网IP:端口号,共享目录为当前工作目录,禁止上传 174 | $ onetiny cf -r {共享目录绝对路径} # 将运行在 http://本机局域网IP:8192,共享目录为指定工作目录,禁止上传 175 | $ onetiny conf -a [1|t|T|true|True|TRUE|空] # 将运行在 http://本机局域网IP:8192,共享目录为当前工作目录,允许上传 176 | $ onetiny config -x {0~255} # 将运行在 http://本机局域网IP:8192,共享目录为当前工作目录,禁止上传,默认仅允许访问共享目录一层,禁止进入下一层目录 177 | $ onetiny -s [1|t|T|true|True|TRUE|空] # 配置后访问时需登录帐号密码 178 | 179 | 开启访问登录,需先设置账号密码 180 | $ onetiny s -u boii -p=123 # 设置帐号为 boii,密码为 123 181 | $ onetiny sec -s # 设置开启访问登录 182 | $ onetiny sec # 查看当前访问登录状态 183 | 184 | 更新命令 185 | $ onetiny u # 更新到最新版本 186 | $ onetiny up # 更新到最新版本 187 | $ onetiny update # 更新到最新版本 188 | $ onetiny update -l # 列出所有版本 189 | $ onetiny updata --use # 更新到指定版本 190 | 191 | $ onetiny -h # 打印帮助信息 192 | $ onetiny --help 193 | $ onetiny -v # 打印版本信息 194 | $ onetiny --version 195 | 196 | $ onetiny -r=/home/boii -p=8192 -a -x=2 # 将运行在 「http://本机局域网IP:8192」,共享目录为 「/home/boii」,允许上传,允许访问共享目录往下2层 197 | ``` 198 | 199 | 200 | **更多信息**: 201 | 202 | 全局命令: 203 | ```bash 204 | $ onetiny -h 205 | NAME: 206 | OneTiny - 一个用于局域网内共享文件的FTP程序 207 | 208 | USAGE: 209 | onetiny [GLOBAL OPTIONS] COMMAND [COMMAND OPTIONS] [参数...] 210 | 211 | VERSION: 212 | v0.3.0 213 | 214 | AUTHOR: 215 | Boii 216 | 217 | COMMANDS: 218 | update, u, up 更新 OneTiny 到最新版 219 | config, c, cf, cfg, conf 设置默认配置 220 | sec, s 设置访问登录的账户和密码 221 | help, h Shows a list of commands or help for one command 222 | 223 | GLOBAL OPTIONS: 224 | --road 路径, -r 路径 指定对外开放的目录路径 (default: /home/boii) 225 | --port 端口, -p 端口 指定开放的端口 (default: 8192) 226 | --allow 是否, -a 是否 指定是否允许访问者上传 (default: true) 227 | --max 深度, -x 深度 指定允许访问的深度,默认仅限访问共享目录 (default: 0) 228 | --secure 开启, -s 开启 指定是否开启访问登录 (default: true) 229 | --help, -h 打印帮助信息 (default: false) 230 | --version, -v 打印版本信息 (default: false) 231 | ``` 232 | 233 | config 子命令: 234 | ```bash 235 | $ onetiny c -h 236 | NAME: 237 | onetiny config - 设置默认配置 238 | 239 | USAGE: 240 | onetiny config [OPTIONS] 241 | 242 | DESCRIPTION: 243 | 使用 onetiny config 命令可以将设置写入配置文件。 244 | 使用方式与 onetiny 命令相同,仅多了一个 config 关键字,如: 245 | onetiny config -p 10240 可以将端口设置为 10240 写入配置 246 | onetiny config -a false 可以设置不允许访问者上传并写入配置 247 | 248 | OPTIONS: 249 | --road 路径, -r 路径 指定对外开放的目录路径 (default: /home/boii) 250 | --port 端口, -p 端口 指定开放的端口 (default: 8192) 251 | --allow 是否, -a 是否 指定是否允许访问者上传 (default: false) 252 | --max 深度, -x 深度 指定允许访问的深度,默认仅限访问共享目录 (default: 0) 253 | --secure 开启, -s 开启 指定是否开启访问登录 (default: false) 254 | --help, -h 打印帮助信息 (default: false) 255 | ``` 256 | 257 | secure 子命令: 258 | ```bash 259 | $ onetiny s -h 260 | NAME: 261 | tiny sec - 设置访问登录的账户和密码 262 | 263 | USAGE: 264 | onetiny sec [OPTIONS] 265 | 266 | DESCRIPTION: 267 | 使用 onetiny sec 命令可以设置访问登录的帐号密码。 268 | 允许的命令形式如下: 269 | 注册并开启: onetiny sec -u=账户名 -p=密码 -s 270 | 注册/覆盖账户: onetiny sec -u=账户名 -p=密码 271 | 重设密码: onetiny sec -p=密码 272 | 273 | OPTIONS: 274 | --user 账户, -u 账户 设置访问登录的账户名 275 | --pass 密码, -p 密码 设置访问登录的密码 276 | --secure 开启, -s 开启 设置开启访问登录,效果同 onetiny -s 一样 (default: false) 277 | --help, -h 打印帮助信息 (default: false) 278 | ``` 279 | 280 | update 子命令: 281 | ```bash 282 | NAME: 283 | onetiny update - 更新 OneTiny 到最新版 284 | 285 | USAGE: 286 | onetiny update [command options] [arguments...] 287 | 288 | OPTIONS: 289 | --list, -l 列出远程服务器上所有可用版本 (default: false) 290 | --use value 指定版本号 291 | --help, -h 打印帮助信息 (default: false) 292 | ``` 293 | 294 | ### [访问] 295 | 1. 运行程序后,程序会提示此次服务运行在哪个端口,共享的是哪个目录,是否允许上传,允许访问的最大深度。 296 | 2. 打开浏览器,输入程序提示框中给出的地址,回车即可访问。 297 | 298 | 举个栗子: 299 | ![](README/command.png) 300 | 红色箭头所指即为服务运行地址。 301 | 302 | 打开浏览器输入程序给出的地址,即可访问共享目录中的文件: 303 | 304 | ![](README/browser.png) 305 | 306 | **点击文件链接即可下载。** 307 | 308 | ### [结束运行] 309 | **Linux**: 310 | 命令行中使用快捷键 `Ctrl + C` 即可停止程序运行。 311 | 312 | **Windows**: 313 | 关闭cmd命令框即可。 314 | 315 | > 注意: 316 | > 1. 在 Linux 或 Mac 系统下,需要将可执行文件移动至 `/usr/bin` 下: 317 | > ```bash 318 | > mv ./OneTiny /usr/bin/tiny 319 | > ``` 320 | > 才能像内置命令一样使用。可以改成你喜欢的名字。 321 | > 2. Windows 下载时可能会有损害计算机提示,点击仍然保留即可。 322 | 323 | ## [TODO] 324 | - [x] 上传功能 325 | - [x] 限定访问层级 326 | - [x] 密码验证功能(防止局域网内监听) 327 | - [x] 打包目录至 .zip 并下载 328 | - [ ] 断点续传 329 | - [ ] 大文件多线程下载 330 | - [ ] 自动检查更新功能 331 | - [ ] 增加图形界面(使用 [fyne](https://fyne.io/)) 332 | 333 | ## [鸣谢] 334 | - [gin](https://gin-gonic.com/zh-cn/) 335 | - [viper](https://github.com/spf13/viper) 336 | - [urfave/cli](https://github.com/urfave/cli/v2) 337 | - [fatih/color](https://github.com/fatih/color) 338 | - [schollz/progressbar](https://github.com/schollz/progressbar/v3) 339 | - [gin-contrib/sessions](https://github.com/gin-contrib/sessions) -------------------------------------------------------------------------------- /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/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= 21 | cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= 22 | cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= 23 | cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= 24 | cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= 25 | cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= 26 | cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= 27 | cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= 28 | cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= 29 | cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= 30 | cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= 31 | cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= 32 | cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= 33 | cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= 34 | cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= 35 | cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= 36 | cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= 37 | cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= 38 | dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= 39 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 40 | github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= 41 | github.com/TCP404/eutil v0.0.5 h1:/5pOs1NfaFQP3QtOL4Hp+CnDZuB4tjZPz6jPt2xZ41I= 42 | github.com/TCP404/eutil v0.0.5/go.mod h1:SXMR1WFmqPr7mERx5AjqUXCVwiOn+g+gPEzU5b+geLc= 43 | github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 44 | github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= 45 | github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= 46 | github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= 47 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 48 | github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= 49 | github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= 50 | github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= 51 | github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= 52 | github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= 53 | github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 54 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 55 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 56 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 57 | github.com/elazarl/goproxy v0.0.0-20220417044921-416226498f94 h1:VIy7cdK7ufs7ctpTFkXJHm1uP3dJSnCGSPysEICB1so= 58 | github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 59 | github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 60 | github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= 61 | github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= 62 | github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= 63 | github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= 64 | github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= 65 | github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= 66 | github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= 67 | github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= 68 | github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= 69 | github.com/gin-contrib/sessions v0.0.5 h1:CATtfHmLMQrMNpJRgzjWXD7worTh7g7ritsQfmF+0jE= 70 | github.com/gin-contrib/sessions v0.0.5/go.mod h1:vYAuaUPqie3WUSsft6HUlCjlwwoJQs97miaG2+7neKY= 71 | github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= 72 | github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= 73 | github.com/gin-gonic/gin v1.7.7 h1:3DoBmSbJbZAWqXJC3SLjAPfutPJJRN1U5pALB7EeTTs= 74 | github.com/gin-gonic/gin v1.7.7/go.mod h1:axIBovoeJpVj8S3BwE0uPMTeReE4+AfFtqpqaZ1qq1U= 75 | github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= 76 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 77 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 78 | github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= 79 | github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= 80 | github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= 81 | github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU= 82 | github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= 83 | github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= 84 | github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho= 85 | github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= 86 | github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= 87 | github.com/go-playground/validator/v10 v10.11.0 h1:0W+xRM511GY47Yy3bZUbJVitCNg2BOGlCyvTqsp/xIw= 88 | github.com/go-playground/validator/v10 v10.11.0/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU= 89 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 90 | github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 91 | github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 92 | github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 93 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 94 | github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 95 | github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= 96 | github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 97 | github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 98 | github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 99 | github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= 100 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 101 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 102 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 103 | github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 104 | github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 105 | github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= 106 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= 107 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= 108 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= 109 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 110 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= 111 | github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= 112 | github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 113 | github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 114 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 115 | github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= 116 | github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 117 | github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 118 | github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 119 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 120 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 121 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 122 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 123 | github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 124 | github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 125 | github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 126 | github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 127 | github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 128 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 129 | github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= 130 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 131 | github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= 132 | github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= 133 | github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= 134 | github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 135 | github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 136 | github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 137 | github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 138 | github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 139 | github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 140 | github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 141 | github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 142 | github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 143 | github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 144 | github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= 145 | github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 146 | github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= 147 | github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= 148 | github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= 149 | github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25dO0g= 150 | github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8= 151 | github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= 152 | github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ= 153 | github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= 154 | github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI= 155 | github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= 156 | github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 157 | github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 158 | github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= 159 | github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= 160 | github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= 161 | github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= 162 | github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 163 | github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= 164 | github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= 165 | github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= 166 | github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= 167 | github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= 168 | github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= 169 | github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw= 170 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 171 | github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= 172 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 173 | github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= 174 | github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= 175 | github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= 176 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 177 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 178 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 179 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 180 | github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= 181 | github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w= 182 | github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= 183 | github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo= 184 | github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= 185 | github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= 186 | github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= 187 | github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= 188 | github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= 189 | github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= 190 | github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= 191 | github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= 192 | github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= 193 | github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ= 194 | github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw= 195 | github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= 196 | github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= 197 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 198 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= 199 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 200 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 201 | github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= 202 | github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= 203 | github.com/parnurzeal/gorequest v0.2.16 h1:T/5x+/4BT+nj+3eSknXmCTnEVGSzFzPGdpqmUVVZXHQ= 204 | github.com/parnurzeal/gorequest v0.2.16/go.mod h1:3Kh2QUMJoqw3icWAecsyzkpY7UzRfDhbRdTjtNwNiUE= 205 | github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= 206 | github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= 207 | github.com/pelletier/go-toml/v2 v2.0.1 h1:8e3L2cCQzLFi2CR4g7vGFuFxX7Jl1kKX8gW+iV0GUKU= 208 | github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo= 209 | github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= 210 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 211 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 212 | github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= 213 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 214 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 215 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 216 | github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= 217 | github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= 218 | github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 219 | github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= 220 | github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8= 221 | github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= 222 | github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= 223 | github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 224 | github.com/schollz/progressbar/v3 v3.8.6 h1:QruMUdzZ1TbEP++S1m73OqRJk20ON11m6Wqv4EoGg8c= 225 | github.com/schollz/progressbar/v3 v3.8.6/go.mod h1:W5IEwbJecncFGBvuEh4A7HT1nZZ6WNIL2i3qbnI0WKY= 226 | github.com/smartystreets/assertions v1.13.0 h1:Dx1kYM01xsSqKPno3aqLnrwac2LetPvN23diwyr69Qs= 227 | github.com/smartystreets/goconvey v1.7.2 h1:9RBaZCeXEQ3UselpuwUQHltGVXvdwm6cv1hgR6gDIPg= 228 | github.com/spf13/afero v1.8.2 h1:xehSyVa0YnHWsJ49JFljMpg1HX19V6NDZ1fkm1Xznbo= 229 | github.com/spf13/afero v1.8.2/go.mod h1:CtAatgMJh6bJEIs48Ay/FOnkljP3WeGUG0MC1RfAqwo= 230 | github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= 231 | github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= 232 | github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= 233 | github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= 234 | github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= 235 | github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 236 | github.com/spf13/viper v1.12.0 h1:CZ7eSOd3kZoaYDLbXnmzgQI5RlciuXBMA+18HwHRfZQ= 237 | github.com/spf13/viper v1.12.0/go.mod h1:b6COn30jlNxbm/V2IqWiNWkJ+vZNiMNksliPCiuKtSI= 238 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 239 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 240 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 241 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 242 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 243 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 244 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 245 | github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= 246 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 247 | github.com/subosito/gotenv v1.3.0 h1:mjC+YW8QpAdXibNi+vNWgzmgBH4+5l5dCXv8cNysBLI= 248 | github.com/subosito/gotenv v1.3.0/go.mod h1:YzJjq/33h7nrwdY+iHMhEOEEbW0ovIz0tB6t6PwAXzs= 249 | github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= 250 | github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M= 251 | github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= 252 | github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0= 253 | github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= 254 | github.com/urfave/cli/v2 v2.8.1 h1:CGuYNZF9IKZY/rfBe3lJpccSoIY1ytfvmgQT90cNOl4= 255 | github.com/urfave/cli/v2 v2.8.1/go.mod h1:Z41J9TPoffeoqP0Iza0YbAhGvymRdZAd2uPmZ5JxRdY= 256 | github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= 257 | github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= 258 | github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 259 | github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 260 | github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 261 | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 262 | go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= 263 | go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= 264 | go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 265 | go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 266 | go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 267 | go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= 268 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 269 | golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 270 | golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 271 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 272 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 273 | golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= 274 | golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 275 | golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= 276 | golang.org/x/crypto v0.0.0-20220131195533-30dcbda58838/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= 277 | golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e h1:T8NU3HyQ8ClP4SEE+KbFlg6n0NhuTsN4MyznaarGsZM= 278 | golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= 279 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 280 | golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 281 | golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= 282 | golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= 283 | golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= 284 | golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 285 | golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 286 | golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 287 | golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= 288 | golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= 289 | golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= 290 | golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= 291 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 292 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 293 | golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 294 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 295 | golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 296 | golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 297 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 298 | golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= 299 | golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 300 | golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 301 | golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 302 | golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= 303 | golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= 304 | golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= 305 | golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= 306 | golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 307 | golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 308 | golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 309 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 310 | golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 311 | golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 312 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 313 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 314 | golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 315 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 316 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 317 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 318 | golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 319 | golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 320 | golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 321 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 322 | golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 323 | golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 324 | golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 325 | golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 326 | golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 327 | golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 328 | golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 329 | golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 330 | golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 331 | golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 332 | golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 333 | golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 334 | golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 335 | golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 336 | golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 337 | golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 338 | golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 339 | golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 340 | golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 341 | golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 342 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 343 | golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 344 | golang.org/x/net v0.0.0-20220621193019-9d032be2e588 h1:9ubFuySsnAJYGyJrZ3koiEv8FyqofCBdz3G9Mbf2YFc= 345 | golang.org/x/net v0.0.0-20220621193019-9d032be2e588/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 346 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 347 | golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 348 | golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 349 | golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 350 | golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 351 | golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 352 | golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 353 | golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 354 | golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 355 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 356 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 357 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 358 | golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 359 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 360 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 361 | golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 362 | golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 363 | golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 364 | golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 365 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 366 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 367 | golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 368 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 369 | golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 370 | golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 371 | golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 372 | golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 373 | golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 374 | golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 375 | golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 376 | golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 377 | golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 378 | golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 379 | golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 380 | golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 381 | golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 382 | golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 383 | golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 384 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 385 | golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 386 | golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 387 | golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 388 | golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 389 | golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 390 | golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 391 | golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 392 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 393 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 394 | golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 395 | golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 396 | golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 397 | golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 398 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 399 | golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 400 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 401 | golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 402 | golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 403 | golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 404 | golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 405 | golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 406 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k= 407 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 408 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 409 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 410 | golang.org/x/term v0.0.0-20220526004731-065cf7ba2467 h1:CBpWXWQpIRjzmkkA+M7q9Fqnwd2mZr3AFqexg8YTfoM= 411 | golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 412 | golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 413 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 414 | golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 415 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 416 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 417 | golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 418 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 419 | golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= 420 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 421 | golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 422 | golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 423 | golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 424 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 425 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 426 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 427 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 428 | golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 429 | golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 430 | golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 431 | golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 432 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 433 | golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 434 | golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 435 | golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 436 | golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 437 | golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 438 | golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 439 | golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 440 | golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 441 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 442 | golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 443 | golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 444 | golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 445 | golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 446 | golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 447 | golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 448 | golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 449 | golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 450 | golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 451 | golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 452 | golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 453 | golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 454 | golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= 455 | golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= 456 | golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= 457 | golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 458 | golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 459 | golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 460 | golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 461 | golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 462 | golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 463 | golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 464 | golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= 465 | golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 466 | golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 467 | golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 468 | golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 469 | golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 470 | golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= 471 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 472 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 473 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 474 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 475 | google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= 476 | google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= 477 | google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 478 | google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 479 | google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 480 | google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 481 | google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 482 | google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 483 | google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 484 | google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 485 | google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 486 | google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 487 | google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= 488 | google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= 489 | google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= 490 | google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= 491 | google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= 492 | google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= 493 | google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= 494 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 495 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 496 | google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 497 | google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= 498 | google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 499 | google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 500 | google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 501 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 502 | google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 503 | google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 504 | google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 505 | google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 506 | google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 507 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 508 | google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= 509 | google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 510 | google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 511 | google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 512 | google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 513 | google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 514 | google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 515 | google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= 516 | google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 517 | google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 518 | google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 519 | google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 520 | google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 521 | google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 522 | google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 523 | google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 524 | google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= 525 | google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= 526 | google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= 527 | google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 528 | google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 529 | google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 530 | google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 531 | google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 532 | google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 533 | google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 534 | google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 535 | google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 536 | google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 537 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 538 | google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= 539 | google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= 540 | google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 541 | google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= 542 | google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 543 | google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 544 | google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 545 | google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= 546 | google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= 547 | google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= 548 | google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= 549 | google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= 550 | google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= 551 | google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= 552 | google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= 553 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 554 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 555 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= 556 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= 557 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= 558 | google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 559 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 560 | google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 561 | google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= 562 | google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= 563 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 564 | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 565 | google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= 566 | google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= 567 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 568 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 569 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= 570 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= 571 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 572 | gopkg.in/ini.v1 v1.66.5 h1:zfiCO0p88Fj4f6NR6KR5WdGMQ02U8vlDnN6HuD2xv5o= 573 | gopkg.in/ini.v1 v1.66.5/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= 574 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 575 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 576 | gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= 577 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 578 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 579 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 580 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 581 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 582 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 583 | honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 584 | honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 585 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 586 | honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= 587 | honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= 588 | honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= 589 | moul.io/http2curl v1.0.0 h1:6XwpyZOYsgZJrU8exnG87ncVkU1FVCcTRpwzOkTDUi8= 590 | moul.io/http2curl v1.0.0/go.mod h1:f6cULg+e4Md/oW1cYmwW4IWQOVl2lGbmCNGOHvzX2kE= 591 | rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= 592 | rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= 593 | rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= 594 | --------------------------------------------------------------------------------