diff --git a/README.md b/README.md index 51770c99..bcaf291b 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,19 @@ https://natnps.com/ ## 更新日志 +- 2025-01-07 v0.26.21 + 新增: + - 客户端列表页面新增【快捷启动命令】,此命令为连接地址+链接秘钥的base64编码,方便一键启动、一键安装客户端。 + + 优化: + - 修改vkey生成方式,由16位缩短至10位,截取uuid前10位,避免重复。 + - 优化客户端启动方式,当npc文件目录下无配置文件时,可直接双击运行客户端,输入命令完成启动、安装、卸载客户端,直接启动和安装服务时需要输入【快捷启动命令】,卸载服务、启动服务、停止服务时,只需输入隧道秘钥【vkey】即可。 + ![image](image/new/cmd.png) + + + **注意** + 强烈推荐使用无配置文件模式启动客户端,所有数据应该在服务端保存和配置,而客户端只做连接转发。客户端配置文件对小白极不友好,配置繁琐,容易出错。 + - 2024-11-07 v0.26.20 新增: - 客户端增加创建时间 diff --git a/build.sh b/build.sh index 36d9f927..31aa9d84 100755 --- a/build.sh +++ b/build.sh @@ -1,5 +1,5 @@ #/bash/sh -export VERSION=0.26.20 +export VERSION=0.26.21 export GOPROXY=direct sudo apt-get update diff --git a/cmd/npc/npc.go b/cmd/npc/npc.go index 9daea56b..a9d35f24 100644 --- a/cmd/npc/npc.go +++ b/cmd/npc/npc.go @@ -1,6 +1,8 @@ package main import ( + "bufio" + "ehang.io/nps/lib/crypt" "flag" "fmt" "os" @@ -18,6 +20,7 @@ import ( "ehang.io/nps/lib/version" "github.com/astaxie/beego/logs" "github.com/ccding/go-stun/stun" + "github.com/fatih/color" "github.com/kardianos/service" ) @@ -252,6 +255,266 @@ func run() { if *configPath == "" { *configPath = common.GetConfigPath() } - go client.StartFromFile(*configPath) + + // 判断路径下是否有配置文件 + if common.FileExists(*configPath) { + logs.Info("配置文件模式启动") + go client.StartFromFile(*configPath) + } else { + // 无配置文件模式双击运行 + printSlogan() + inputCmd() + } + } +} + +func printSlogan() { + green := color.New(color.FgGreen).SprintFunc() + // 第一次输入,如果输入 1,2,3,4 则需要输入秘钥,否则 + + fmt.Printf("%s", green("")) + + fmt.Printf("\033[32;0m###########################################################\n") + fmt.Printf("\033[32;0m# \033[31mNPS内网穿透客户端\033[0m #\n") + fmt.Printf("\033[32;0m# #\n") + fmt.Printf("\033[32;0m#\033[32m 地址:\033[31;0mhttps://github.com/yisier/nps\033[0m #\n") + fmt.Printf("\033[32;0m#\033[32m 提示:\033[32;0m1、涉及到系统服务的需要以管理员身份运行\033[0m\033[32;0m #\n") + fmt.Printf("\033[32;0m#\033[32m \033[32;0m2、直接启动或[注册系统服务]需要使用[快捷启动命令]\033[0m\033[32;0m #\n") + fmt.Printf("\033[32;0m#\033[32m \033[32;0m3、其他命令如卸载/启动/停止只需要输入[vkey]\033[0m\033[32;0m #\n") + fmt.Printf("\033[32;0m###########################################################\n") + fmt.Printf("\033[0m") // 重置颜色 + + fmt.Printf("\n") + + fmt.Printf("\u001B[32m输入[1]\u001B[0m - 注册系统服务\n") + fmt.Printf("\u001B[32m输入[2]\u001B[0m - 卸载系统服务\n") + fmt.Printf("---------------------\n") + fmt.Printf("\u001B[32m输入[3]\u001B[0m - 启动系统服务\n") + fmt.Printf("\u001B[32m输入[4]\u001B[0m - 停止系统服务\n") + fmt.Printf("---------------------\n") + fmt.Printf("\u001B[32m输入[0]\u001B[0m - 退出\n") + fmt.Printf("---------------------\n") + fmt.Printf("直接输入[快捷启动命令]则是启动隧道,多个[快捷启动命令]用英文逗号拼接\n") + fmt.Printf("\n") +} + +func inputCmd() { + + var flag string + fmt.Printf("请输入:") + + stdin := bufio.NewReader(os.Stdin) + _, err := fmt.Fscanln(stdin, &flag) + if err != nil { + fmt.Println("输入有误") + } else { + if flag == "0" { + os.Exit(0) + } + + flag := strings.Replace(flag, " ", "", -1) + + // 如果输入不等于 1,2,3,4,则启动隧道 + if flag != "1" && flag != "2" && flag != "3" && flag != "4" { + + vkeys := strings.Split(flag, `,`) + var cmdArray []string + + for _, key := range vkeys { + startCmd, err := crypt.Base64Decoding(key) + if err != nil { + fmt.Println("快捷启动命令解析失败") + inputCmd() + return + } + + cmdArray = append(cmdArray, startCmd) + } + + for _, item := range cmdArray { + startNpcServer(item) + } + + } else { + systemService(flag) + } + } +} + +func startNpcServer(startCmd string) { + var serAddr string + var vkey string + array := strings.Fields(startCmd) + serAddr = array[0] + vkey = array[1] + + go func() { + for { + logs.Info("start cmd:-server=" + serAddr + " -vkey=" + vkey) + logs.Info("the version of client is %s, the core version of client is %s", version.VERSION, version.GetVersion()) + client.NewRPClient(serAddr, vkey, *connType, *proxyUrl, nil, *disconnectTime).Start() + logs.Info("Client closed! It will be reconnected in five seconds") + time.Sleep(time.Second * 5) + } + }() +} + +func systemService(flag string) { + + if flag == "1" { + fmt.Printf("请输入[快捷启动命令],多个[快捷启动命令]用英文逗号拼接:") + } else { + fmt.Printf("请输入[VKEY],多个[VKEY]用英文逗号拼接:") + } + + var vkey string + stdin := bufio.NewReader(os.Stdin) + _, err := fmt.Fscanln(stdin, &vkey) + + if err != nil { + fmt.Println("输入错误,请重试") + systemService(flag) + return + } else { + if vkey == "0" { + os.Exit(0) + } + } + + vkey = strings.Replace(vkey, " ", "", -1) + + vkeys := strings.Split(vkey, `,`) + + if flag == "1" { + var cmdArray []string + for _, key := range vkeys { + startCmd, err := crypt.Base64Decoding(key) + if err != nil { + fmt.Println("快捷启动命令解析失败") + systemService(flag) + return + } + cmdArray = append(cmdArray, startCmd) + } + + for _, item := range cmdArray { + array := strings.Fields(item) + systemPro(flag, array[0], array[1]) + } + } else { + for _, key := range vkeys { + systemPro(flag, "", key) + } + + } + + inputCmd() + return +} + +func systemPro(flag string, serAddr string, vkey string) { + // init service + prg := &npc{ + exit: make(chan struct{}), + } + options := make(service.KeyValue) + svcConfig := &service.Config{ + Name: "nps-client-" + vkey, + DisplayName: "nps-client-" + vkey, + Description: "NPS内网穿透客户端,支持tcp、udp流量转发,支持内网http代理,地址:https://github.com/yisier/nps", + Option: options, + } + s, _ := service.New(prg, svcConfig) + + switch flag { + case "1": + svcConfig.Arguments = append(svcConfig.Arguments, "-server="+serAddr) + svcConfig.Arguments = append(svcConfig.Arguments, "-vkey="+vkey) + svcConfig.Arguments = append(svcConfig.Arguments, "-debug=false") + install.InstallNpc() + err := service.Control(s, "install") + if err != nil { + fmt.Println("隧道["+vkey+"]安装到系统服务失败", err) + return + } else { + fmt.Println("隧道[" + vkey + "]已经安装到系统") + } + if service.Platform() == "unix-systemv" { + logs.Info("unix-systemv service") + confPath := "/etc/init.d/" + svcConfig.Name + os.Symlink(confPath, "/etc/rc.d/S90"+svcConfig.Name) + os.Symlink(confPath, "/etc/rc.d/K02"+svcConfig.Name) + } + + err2 := service.Control(s, "start") + if err2 != nil { + fmt.Println("隧道["+vkey+"]启动服务失败", err2) + } else { + fmt.Println("隧道[" + vkey + "]服务已启动") + } + + return + case "2": + // 卸载系统服务 + err := service.Control(s, "stop") + if err != nil { + fmt.Println("隧道["+vkey+"]服务停止失败", err) + } else { + fmt.Println("隧道[" + vkey + "]服务已停止") + } + + err = service.Control(s, "uninstall") + if err != nil { + fmt.Println("隧道["+vkey+"]服务卸载失败", err) + } + if service.Platform() == "unix-systemv" { + fmt.Println("unix-systemv service") + os.Remove("/etc/rc.d/S90" + svcConfig.Name) + os.Remove("/etc/rc.d/K02" + svcConfig.Name) + } + + if err == nil { + fmt.Println("隧道[" + vkey + "]服务已卸载成功") + } + + return + + case "3": + //启动系统服务 + if service.Platform() == "unix-systemv" { + logs.Info("unix-systemv service") + cmd := exec.Command("/etc/init.d/"+svcConfig.Name, "start") + err := cmd.Run() + if err != nil { + logs.Error(err) + } + return + } + err := service.Control(s, "start") + if err != nil { + fmt.Println("隧道["+vkey+"]服务启动失败", err) + } else { + fmt.Println("隧道[" + vkey + "]服务启动成功") + } + + return + case "4": + if service.Platform() == "unix-systemv" { + logs.Info("unix-systemv service") + cmd := exec.Command("/etc/init.d/"+svcConfig.Name, "stop") + err := cmd.Run() + if err != nil { + logs.Error(err) + } + return + } + err := service.Control(s, "stop") + if err != nil { + fmt.Println("隧道["+vkey+"]服务停止失败", err) + } else { + fmt.Println("隧道[" + vkey + "]服务停止成功") + } + + return } } diff --git a/conf/clients.json b/conf/clients.json index ff1090de..62da66d7 100644 --- a/conf/clients.json +++ b/conf/clients.json @@ -1,2 +1,2 @@ -{"Cnf":{"U":"","P":"","Compress":false,"Crypt":false},"Id":2,"VerifyKey":"a82kjhdyllqnlruk","Addr":"127.0.0.1","Remark":"","Status":true,"IsConnect":false,"RateLimit":0,"Flow":{"ExportFlow":551,"InletFlow":551,"FlowLimit":0},"Rate":{"NowRate":0},"NoStore":false,"NoDisplay":false,"MaxConn":0,"NowConn":0,"WebUserName":"","WebPassword":"","ConfigConnAllow":true,"MaxTunnelNum":0,"Version":"0.26.19","BlackIpList":[""],"LastOnlineTime":"2024-05-28 15:08:39"} +{"Cnf":{"U":"","P":"","Compress":false,"Crypt":false},"Id":34,"VerifyKey":"a1efa114df","Addr":"127.0.0.1","Remark":"","Status":true,"IsConnect":false,"RateLimit":0,"Flow":{"ExportFlow":0,"InletFlow":0,"FlowLimit":0},"Rate":{"NowRate":0},"NoStore":false,"NoDisplay":false,"MaxConn":0,"NowConn":0,"WebUserName":"","WebPassword":"","ConfigConnAllow":true,"MaxTunnelNum":0,"Version":"0.26.20","BlackIpList":[""],"CreateTime":"2025-01-03 17:07:37","LastOnlineTime":"2025-01-07 11:12:00"} *#* \ No newline at end of file diff --git a/conf/hosts.json b/conf/hosts.json index 5c17559a..e69de29b 100644 --- a/conf/hosts.json +++ b/conf/hosts.json @@ -1,2 +0,0 @@ -{"Id":1,"Host":"test.test.pp2pdf.com","HeaderChange":"","HostChange":"","Location":"/","Remark":"","Scheme":"all","CertFilePath":"-----BEGIN CERTIFICATE-----\r\nMIIGADCCBOigAwIBAgIQC0shW9rSx8iw/8ZZiE2w3TANBgkqhkiG9w0BAQsFADBu\r\nMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3\r\nd3cuZGlnaWNlcnQuY29tMS0wKwYDVQQDEyRFbmNyeXB0aW9uIEV2ZXJ5d2hlcmUg\r\nRFYgVExTIENBIC0gRzIwHhcNMjQwNTI4MDAwMDAwWhcNMjQwODI2MjM1OTU5WjAf\r\nMR0wGwYDVQQDExR0ZXN0LnRlc3QucHAycGRmLmNvbTCCASIwDQYJKoZIhvcNAQEB\r\nBQADggEPADCCAQoCggEBALjLe8gKybQuTd8B07mx1VPq+DkZ4Am3VVi4dBozi/47\r\nu/8uP35hpDi5evCfR5dm6z3KgyE1NLfd8HwFKFPoD6CRqMkj0Idtr89rsvufzGSx\r\nD1M91Tm0Im/jFGxFfCq8UymrQ9oMy+faBZ7fWsIwtYDJrk3qDoQ9kUyIJi7A2g8b\r\neWMNNW80lSn06Tgbhpc3HL9YGBUMihlKy1Tw3lcqfh9ua3AyJof/CEzMC/9Q7X38\r\nrdQjnWpsk3+mfmWvb82X4fqdcYUY3UPvI3vEdIDug96T9mcVxO7WhfhPXJat/99I\r\npIHlGwtsbSNCGRwitg9jvq1a8DPVBl8WMXFpX7it7S8CAwEAAaOCAucwggLjMB8G\r\nA1UdIwQYMBaAFHjfkZBf7t6s9sV169VMVVPvJEq2MB0GA1UdDgQWBBS11bj+JIYy\r\n9pGI0b6OMwsUI3IwTzAfBgNVHREEGDAWghR0ZXN0LnRlc3QucHAycGRmLmNvbTA+\r\nBgNVHSAENzA1MDMGBmeBDAECATApMCcGCCsGAQUFBwIBFhtodHRwOi8vd3d3LmRp\r\nZ2ljZXJ0LmNvbS9DUFMwDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUF\r\nBwMBBggrBgEFBQcDAjCBgAYIKwYBBQUHAQEEdDByMCQGCCsGAQUFBzABhhhodHRw\r\nOi8vb2NzcC5kaWdpY2VydC5jb20wSgYIKwYBBQUHMAKGPmh0dHA6Ly9jYWNlcnRz\r\nLmRpZ2ljZXJ0LmNvbS9FbmNyeXB0aW9uRXZlcnl3aGVyZURWVExTQ0EtRzIuY3J0\r\nMAwGA1UdEwEB/wQCMAAwggF+BgorBgEEAdZ5AgQCBIIBbgSCAWoBaAB3AHb/iD8K\r\ntvuVUcJhzPWHujS0pM27KdxoQgqf5mdMWjp0AAABj72xAXEAAAQDAEgwRgIhAIwF\r\nnyFmHkP26yxDyRuia6iVV/HH7n0PQoixiMQTyzXWAiEAiSOrrfuCbw7u+GZ9rMRV\r\nCRAH1zIQuulDUkO9Ob9AeWAAdgBIsONr2qZHNA/lagL6nTDrHFIBy1bdLIHZu7+r\r\nOdiEcwAAAY+9sQFnAAAEAwBHMEUCIHNMxGqlB5lNAtF52V3oJTEcuhzroQHHpvMB\r\nUJomOKCGAiEA8jYJg2cjePys7LZhAcZ1qOcpML1JHi1Dg7EW8y3l4hwAdQDatr9r\r\nP7W2Ip+bwrtca+hwkXFsu1GEhTS9pD0wSNf7qwAAAY+9sQFiAAAEAwBGMEQCIEqR\r\nR3SALmhmand0/20AozuPWPWDc/pvWhjABljN0dalAiBcoHTZZZgA1YMUEAmxC3tv\r\nfCkB0t9191fmFpIJ/e/zBDANBgkqhkiG9w0BAQsFAAOCAQEAHr7FK816dKlzAV+2\r\nH+J4rfXYl/9hudEz5T5yDP/8MIf13ThQqUQQ5QyoOZSdBFjLqxnqTRrxQJ32puGc\r\nVzpqzwR7l3J+46XHN9C8winqzgd7ZNpBz5e82cW7AOp5nYzogO3AGyVQjEZab2Nc\r\nNs0CZJ5RjyMMGYqO0xVNAO4nMmG9sMd4tuIzz1dYb7BrjNbhwqSnXguKZ/XnHkn8\r\n150FEy62kF292o9VFpDVnObbR4haIyUweA6gz+8/ccShbs58ToNnIZgSTsjGUmnf\r\nd/NF3HlT7T1I0f47IKg8ZV7GYiBczpDJszk2pp49K+Fgu8gSMzzMxwrVkcLCRGD0\r\nF0mqVA==\r\n-----END CERTIFICATE-----\r\n-----BEGIN CERTIFICATE-----\r\nMIIEqjCCA5KgAwIBAgIQDeD/te5iy2EQn2CMnO1e0zANBgkqhkiG9w0BAQsFADBh\r\nMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3\r\nd3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBH\r\nMjAeFw0xNzExMjcxMjQ2NDBaFw0yNzExMjcxMjQ2NDBaMG4xCzAJBgNVBAYTAlVT\r\nMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j\r\nb20xLTArBgNVBAMTJEVuY3J5cHRpb24gRXZlcnl3aGVyZSBEViBUTFMgQ0EgLSBH\r\nMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAO8Uf46i/nr7pkgTDqnE\r\neSIfCFqvPnUq3aF1tMJ5hh9MnO6Lmt5UdHfBGwC9Si+XjK12cjZgxObsL6Rg1njv\r\nNhAMJ4JunN0JGGRJGSevbJsA3sc68nbPQzuKp5Jc8vpryp2mts38pSCXorPR+sch\r\nQisKA7OSQ1MjcFN0d7tbrceWFNbzgL2csJVQeogOBGSe/KZEIZw6gXLKeFe7mupn\r\nNYJROi2iC11+HuF79iAttMc32Cv6UOxixY/3ZV+LzpLnklFq98XORgwkIJL1HuvP\r\nha8yvb+W6JislZJL+HLFtidoxmI7Qm3ZyIV66W533DsGFimFJkz3y0GeHWuSVMbI\r\nlfsCAwEAAaOCAU8wggFLMB0GA1UdDgQWBBR435GQX+7erPbFdevVTFVT7yRKtjAf\r\nBgNVHSMEGDAWgBROIlQgGJXm427mD/r6uRLtBhePOTAOBgNVHQ8BAf8EBAMCAYYw\r\nHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMBIGA1UdEwEB/wQIMAYBAf8C\r\nAQAwNAYIKwYBBQUHAQEEKDAmMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdp\r\nY2VydC5jb20wQgYDVR0fBDswOTA3oDWgM4YxaHR0cDovL2NybDMuZGlnaWNlcnQu\r\nY29tL0RpZ2lDZXJ0R2xvYmFsUm9vdEcyLmNybDBMBgNVHSAERTBDMDcGCWCGSAGG\r\n/WwBAjAqMCgGCCsGAQUFBwIBFhxodHRwczovL3d3dy5kaWdpY2VydC5jb20vQ1BT\r\nMAgGBmeBDAECATANBgkqhkiG9w0BAQsFAAOCAQEAoBs1eCLKakLtVRPFRjBIJ9LJ\r\nL0s8ZWum8U8/1TMVkQMBn+CPb5xnCD0GSA6L/V0ZFrMNqBirrr5B241OesECvxIi\r\n98bZ90h9+q/X5eMyOD35f8YTaEMpdnQCnawIwiHx06/0BfiTj+b/XQih+mqt3ZXe\r\nxNCJqKexdiB2IWGSKcgahPacWkk/BAQFisKIFYEqHzV974S3FAz/8LIfD58xnsEN\r\nGfzyIDkH3JrwYZ8caPTf6ZX9M1GrISN8HnWTtdNCH2xEajRa/h9ZBXjUyFKQrGk2\r\nn2hcLrfZSbynEC/pSw/ET7H5nWwckjmAJ1l9fcnbqkU/pf6uMQmnfl0JQjJNSg==\r\n-----END CERTIFICATE-----\r\n","KeyFilePath":"-----BEGIN RSA PRIVATE KEY-----\r\nMIIEpAIBAAKCAQEAuMt7yArJtC5N3wHTubHVU+r4ORngCbdVWLh0GjOL/ju7/y4/\r\nfmGkOLl68J9Hl2brPcqDITU0t93wfAUoU+gPoJGoySPQh22vz2uy+5/MZLEPUz3V\r\nObQib+MUbEV8KrxTKatD2gzL59oFnt9awjC1gMmuTeoOhD2RTIgmLsDaDxt5Yw01\r\nbzSVKfTpOBuGlzccv1gYFQyKGUrLVPDeVyp+H25rcDImh/8ITMwL/1Dtffyt1COd\r\namyTf6Z+Za9vzZfh+p1xhRjdQ+8je8R0gO6D3pP2ZxXE7taF+E9clq3/30ikgeUb\r\nC2xtI0IZHCK2D2O+rVrwM9UGXxYxcWlfuK3tLwIDAQABAoIBADKfQ3RkSCb8vEZH\r\n+NkYtyi66G60AOSIfW/Fb8CxuDrX4EMiRzB2zQbhAKWj7He0MOMHc+6H5b4spTFE\r\nNGzv2EATRtp63igLSysWBLzbZKF8w4b5HE0nZHukCDc0vbPzTRDQItE7yivkVL/V\r\nzcX+k+oI1Z/wazOcrg36CgOqvtF+LDUSZvFfBwYWMv9s/7FyY9YrWpCb5h6hM8L4\r\nZ3OAfKZJyI4LQGM/MR2yM0reKCZvodso0lqHIDe28s5U9CAFlcxoBPFa+iEOkXcx\r\ngVrnnBrsqAYLW6qyarFUhc/gznKc/bGWQRoRDI0dJDDaFQexv5fMr00geMid9CXQ\r\nxHDIiAkCgYEA9V/IW2aKvBK5bPctrlW1JVmwQx9eEdgErDCEHf4GuJnepcrAWxM4\r\nRlezTQmhgMTqhqgslNSnKwp+Pfs5UlMSznt9+XwAFKPw5iek0gP56gDER0oNA0Ol\r\nM7/K0sMAbijEQWRnqvNhHy999zyC5EblFE/XfWMLGHUuQJB7afY79MUCgYEAwMwe\r\nc2XDjVtuogD2Eh1JmEACBtb9El0W6tJm7XAc0zbJRLb43lB2q98BFvQufY4NoOwE\r\nEtjMSOO1WxO5fAwy1IblnQFE/s93mCaFsjI44As7JN9MsVOcIhlFrkEKCYArCHWo\r\nLYpcLOHYrQRz2/e2BD/DqEqNst0wuXvjY9z7gWMCgYEAonkozrSoWn4/f5JwEga4\r\naqHnn5pujHwwI+AAK2fgBW4FSlxlYCjq01EQ7YraAMziygUjd+0TP+Gz09C8lPFi\r\n+4wzzvW3i/M9+Pf5IAL4AWU+kkHLOgUZp5lwcHwklcyynuYf6sc64Q5uZz4PIZ57\r\n4yY29tSVLT2yQZgpz/D2pE0CgYEAmfBhP/K5OV0P8Pqjf8MyyAYTmDKAzvDVI5nr\r\n5Rnnah4MhnkyLRBCMOlWjWVa2ZVa/Gp2tjW0p15pp9cP+yQiUd+D5uDknjA5ZZsc\r\nlTPUhc9x4fcJ3WzM9c0H/iKgSQW1VoPbTMF058FMqJktiS04gBwJtY4RpgdGqFNr\r\nNIl1p9ECgYAP8frFBdcARGVDrKwgKRed3lxqMpDJKxeDjHMDgNPy2TCfUMKmbFc4\r\njHgk+AfTVJ3GzMGdkB6BV63vDsRe7yFTdGSaHNJuxYP5TWkabj22Rsc6+/aW7dYA\r\nU0eL3+sTq2fgEzfNLIRqy4m93Tru9RLUOcrLp2+AlUXpQU6tqBdkOg==\r\n-----END RSA PRIVATE KEY-----\r\n","NoStore":false,"IsClose":false,"AutoHttps":false,"Flow":{"ExportFlow":0,"InletFlow":0,"FlowLimit":0},"Client":{"Cnf":{"U":"","P":"","Compress":false,"Crypt":false},"Id":2,"VerifyKey":"a82kjhdyllqnlruk","Addr":"127.0.0.1","Remark":"","Status":true,"IsConnect":false,"RateLimit":0,"Flow":{"ExportFlow":551,"InletFlow":551,"FlowLimit":0},"Rate":{"NowRate":0},"NoStore":false,"NoDisplay":false,"MaxConn":0,"NowConn":0,"WebUserName":"","WebPassword":"","ConfigConnAllow":true,"MaxTunnelNum":0,"Version":"0.26.19","BlackIpList":[""],"LastOnlineTime":"2024-05-28 15:08:39"},"Target":{"TargetStr":"127.0.0.1:9090","TargetArr":null,"LocalProxy":false}} -*#* \ No newline at end of file diff --git a/go.mod b/go.mod index 8988ae3d..c5701d13 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,9 @@ require ( github.com/astaxie/beego v1.12.0 github.com/c4milo/unpackit v0.0.0-20170704181138-4ed373e9ef1c github.com/ccding/go-stun v0.0.0-20180726100737-be486d185f3d + github.com/fatih/color v1.18.0 github.com/golang/snappy v0.0.3 + github.com/google/uuid v1.6.0 github.com/kardianos/service v1.2.0 github.com/panjf2000/ants/v2 v2.4.2 github.com/pkg/errors v0.9.1 @@ -35,7 +37,8 @@ require ( github.com/klauspost/pgzip v1.2.1 // indirect github.com/klauspost/reedsolomon v1.9.12 // indirect github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect - github.com/pires/go-proxyproto v0.7.0 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect github.com/shiena/ansicolor v0.0.0-20151119151921-a422bbe96644 // indirect @@ -53,7 +56,7 @@ require ( github.com/yusufpapurcu/wmi v1.2.3 // indirect golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 // indirect golang.org/x/image v0.0.0-20200430140353-33d19683fad8 // indirect - golang.org/x/sys v0.13.0 // indirect + golang.org/x/sys v0.25.0 // indirect golang.org/x/text v0.3.3 // indirect gopkg.in/yaml.v2 v2.2.8 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index 367e20fa..ce8c5eb5 100644 --- a/go.sum +++ b/go.sum @@ -38,6 +38,8 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/exfly/beego v1.12.0-export-init h1:VQNYKdXhAwZGUaFmQv8Aj921O3rQJZRIF8xeGrhsjrI= github.com/exfly/beego v1.12.0-export-init/go.mod h1:fysx+LZNZKnvh4GED/xND7jWtjCR6HzydR2Hh2Im57o= +github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= +github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= github.com/fredbi/uri v0.0.0-20181227131451-3dcfdacbaaf3 h1:FDqhDm7pcsLhhWl1QtD8vlzI4mm59llRvNzrFg6/LAA= github.com/fredbi/uri v0.0.0-20181227131451-3dcfdacbaaf3/go.mod h1:CzM2G82Q9BDUvMTGHnXf/6OExw/Dz2ivDj48nVg7Lg8= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= @@ -80,6 +82,8 @@ github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/hooklift/assert v0.0.0-20170704181755-9d1defd6d214 h1:WgfvpuKg42WVLkxNwzfFraXkTXPK36bMqXvMFN67clI= github.com/hooklift/assert v0.0.0-20170704181755-9d1defd6d214/go.mod h1:kj6hFWqfwSjFjLnYW5PK1DoxZ4O0uapwHRmd9jhln4E= github.com/jackmordaunt/icns v0.0.0-20181231085925-4f16af745526/go.mod h1:UQkeMHVoNcyXYq9otUupF7/h/2tmHlhrS2zw7ZVvUqc= @@ -105,6 +109,11 @@ github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lucor/goinfo v0.0.0-20200401173949-526b5363a13a/go.mod h1:ORP3/rB5IsulLEBwQZCJyyV6niqmI7P4EWSmkug+1Ng= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= @@ -112,8 +121,6 @@ github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLA github.com/panjf2000/ants/v2 v2.4.2 h1:kesjjo8JipN3vNNg1XaiXaeSs6xJweBTgenkBtsrHf8= github.com/panjf2000/ants/v2 v2.4.2/go.mod h1:f6F0NZVFsGCp5A7QW/Zj/m92atWwOkY0OIhFxRNFr4A= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/pires/go-proxyproto v0.7.0 h1:IukmRewDQFWC7kfnb66CSomk2q/seBuilHBYFwyq0Hs= -github.com/pires/go-proxyproto v0.7.0/go.mod h1:Vz/1JPY/OACxWGQNIRY2BeyDmpoaWmEP40O9LbuiFR4= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -216,10 +223,13 @@ golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= +golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= diff --git a/image/new/cmd.png b/image/new/cmd.png new file mode 100644 index 00000000..9b679ee5 Binary files /dev/null and b/image/new/cmd.png differ diff --git a/lib/crypt/crypt.go b/lib/crypt/crypt.go index dbe312ff..278854e5 100644 --- a/lib/crypt/crypt.go +++ b/lib/crypt/crypt.go @@ -5,9 +5,12 @@ import ( "crypto/aes" "crypto/cipher" "crypto/md5" + "encoding/base64" "encoding/hex" "errors" + "github.com/google/uuid" "math/rand" + "strings" "time" ) @@ -74,3 +77,32 @@ func GetRandomString(l int) string { } return string(result) } + +func GetVkey() string { + // 生成UUID + u, _ := uuid.NewRandom() + // 将UUID转换为字符串 + uuidStr := u.String() + uuidStr = strings.ReplaceAll(uuidStr, "-", "") + // 截取前10位 + return uuidStr[:10] +} + +func Base64Decoding(encodedString string) (string, error) { + decodedBytes, err := base64.StdEncoding.DecodeString(encodedString) + if err != nil { + // 处理解码错误 + return "", err + } + + // 将解码后的字节数组转换为字符串 + decodedString := string(decodedBytes) + + // 如果startCmd开头不包含"nps ",则报错 + if len(decodedString) < 4 || !(decodedString[0:4] == "nps ") { + return "", errors.New("快捷启动命令错误,请检查") + } + + return decodedString[4:], nil + +} diff --git a/lib/file/db.go b/lib/file/db.go index 8701ecd9..c126b202 100644 --- a/lib/file/db.go +++ b/lib/file/db.go @@ -22,7 +22,7 @@ var ( once sync.Once ) -//init csv from file +// init csv from file func GetDb() *DbUtils { once.Do(func() { jsonDb := NewJsonDb(common.GetRunPath()) @@ -128,7 +128,7 @@ func (s *DbUtils) DelTask(id int) error { return nil } -//md5 password +// md5 password func (s *DbUtils) GetTaskByMd5Password(p string) (t *Tunnel) { s.JsonDb.Tasks.Range(func(key, value interface{}) bool { if crypt.Md5(value.(*Tunnel).Password) == p { @@ -218,7 +218,7 @@ func (s *DbUtils) NewClient(c *Client) error { reset: if c.VerifyKey == "" || isNotSet { isNotSet = true - c.VerifyKey = crypt.GetRandomString(16) + c.VerifyKey = crypt.GetVkey() } if c.RateLimit == 0 { c.Rate = rate.NewRate(int64(2 << 23)) @@ -326,7 +326,7 @@ func (s *DbUtils) GetHostById(id int) (h *Host, err error) { return } -//get key by host from x +// get key by host from x func (s *DbUtils) GetInfoByHost(host string, r *http.Request) (h *Host, err error) { var hosts []*Host //Handling Ported Access diff --git a/lib/version/version.go b/lib/version/version.go index d4bbcf6a..7bf60853 100644 --- a/lib/version/version.go +++ b/lib/version/version.go @@ -1,6 +1,6 @@ package version -const VERSION = "0.26.20" +const VERSION = "0.26.21" // Compulsory minimum version, Minimum downward compatibility to this version func GetVersion() string { diff --git a/web/static/css/toastr.min.css b/web/static/css/toastr.min.css new file mode 100644 index 00000000..064afd07 --- /dev/null +++ b/web/static/css/toastr.min.css @@ -0,0 +1 @@ +.toast-title{font-weight:700}.toast-message{-ms-word-wrap:break-word;word-wrap:break-word}.toast-message a,.toast-message label{color:#FFF}.toast-message a:hover{color:#CCC;text-decoration:none}.toast-close-button{position:relative;right:-.3em;top:-.3em;float:right;font-size:20px;font-weight:700;color:#FFF;-webkit-text-shadow:0 1px 0 #fff;text-shadow:0 1px 0 #fff;opacity:.8;-ms-filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=80);filter:alpha(opacity=80);line-height:1}.toast-close-button:focus,.toast-close-button:hover{color:#000;text-decoration:none;cursor:pointer;opacity:.4;-ms-filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=40);filter:alpha(opacity=40)}.rtl .toast-close-button{left:-.3em;float:left;right:.3em}button.toast-close-button{padding:0;cursor:pointer;background:0 0;border:0;-webkit-appearance:none}.toast-top-center{top:0;right:0;width:100%}.toast-bottom-center{bottom:0;right:0;width:100%}.toast-top-full-width{top:0;right:0;width:100%}.toast-bottom-full-width{bottom:0;right:0;width:100%}.toast-top-left{top:12px;left:12px}.toast-top-right{top:12px;right:12px}.toast-bottom-right{right:12px;bottom:12px}.toast-bottom-left{bottom:12px;left:12px}#toast-container{position:fixed;z-index:999999;pointer-events:none}#toast-container *{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box}#toast-container>div{position:relative;pointer-events:auto;overflow:hidden;margin:0 0 6px;padding:15px 15px 15px 50px;width:300px;-moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px;background-position:15px center;background-repeat:no-repeat;-moz-box-shadow:0 0 12px #999;-webkit-box-shadow:0 0 12px #999;box-shadow:0 0 12px #999;color:#FFF;opacity:.8;-ms-filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=80);filter:alpha(opacity=80)}#toast-container>div.rtl{direction:rtl;padding:15px 50px 15px 15px;background-position:right 15px center}#toast-container>div:hover{-moz-box-shadow:0 0 12px #000;-webkit-box-shadow:0 0 12px #000;box-shadow:0 0 12px #000;opacity:1;-ms-filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=100);filter:alpha(opacity=100);cursor:pointer}#toast-container>.toast-info{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGwSURBVEhLtZa9SgNBEMc9sUxxRcoUKSzSWIhXpFMhhYWFhaBg4yPYiWCXZxBLERsLRS3EQkEfwCKdjWJAwSKCgoKCcudv4O5YLrt7EzgXhiU3/4+b2ckmwVjJSpKkQ6wAi4gwhT+z3wRBcEz0yjSseUTrcRyfsHsXmD0AmbHOC9Ii8VImnuXBPglHpQ5wwSVM7sNnTG7Za4JwDdCjxyAiH3nyA2mtaTJufiDZ5dCaqlItILh1NHatfN5skvjx9Z38m69CgzuXmZgVrPIGE763Jx9qKsRozWYw6xOHdER+nn2KkO+Bb+UV5CBN6WC6QtBgbRVozrahAbmm6HtUsgtPC19tFdxXZYBOfkbmFJ1VaHA1VAHjd0pp70oTZzvR+EVrx2Ygfdsq6eu55BHYR8hlcki+n+kERUFG8BrA0BwjeAv2M8WLQBtcy+SD6fNsmnB3AlBLrgTtVW1c2QN4bVWLATaIS60J2Du5y1TiJgjSBvFVZgTmwCU+dAZFoPxGEEs8nyHC9Bwe2GvEJv2WXZb0vjdyFT4Cxk3e/kIqlOGoVLwwPevpYHT+00T+hWwXDf4AJAOUqWcDhbwAAAAASUVORK5CYII=)!important}#toast-container>.toast-error{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHOSURBVEhLrZa/SgNBEMZzh0WKCClSCKaIYOED+AAKeQQLG8HWztLCImBrYadgIdY+gIKNYkBFSwu7CAoqCgkkoGBI/E28PdbLZmeDLgzZzcx83/zZ2SSXC1j9fr+I1Hq93g2yxH4iwM1vkoBWAdxCmpzTxfkN2RcyZNaHFIkSo10+8kgxkXIURV5HGxTmFuc75B2RfQkpxHG8aAgaAFa0tAHqYFfQ7Iwe2yhODk8+J4C7yAoRTWI3w/4klGRgR4lO7Rpn9+gvMyWp+uxFh8+H+ARlgN1nJuJuQAYvNkEnwGFck18Er4q3egEc/oO+mhLdKgRyhdNFiacC0rlOCbhNVz4H9FnAYgDBvU3QIioZlJFLJtsoHYRDfiZoUyIxqCtRpVlANq0EU4dApjrtgezPFad5S19Wgjkc0hNVnuF4HjVA6C7QrSIbylB+oZe3aHgBsqlNqKYH48jXyJKMuAbiyVJ8KzaB3eRc0pg9VwQ4niFryI68qiOi3AbjwdsfnAtk0bCjTLJKr6mrD9g8iq/S/B81hguOMlQTnVyG40wAcjnmgsCNESDrjme7wfftP4P7SP4N3CJZdvzoNyGq2c/HWOXJGsvVg+RA/k2MC/wN6I2YA2Pt8GkAAAAASUVORK5CYII=)!important}#toast-container>.toast-success{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAADsSURBVEhLY2AYBfQMgf///3P8+/evAIgvA/FsIF+BavYDDWMBGroaSMMBiE8VC7AZDrIFaMFnii3AZTjUgsUUWUDA8OdAH6iQbQEhw4HyGsPEcKBXBIC4ARhex4G4BsjmweU1soIFaGg/WtoFZRIZdEvIMhxkCCjXIVsATV6gFGACs4Rsw0EGgIIH3QJYJgHSARQZDrWAB+jawzgs+Q2UO49D7jnRSRGoEFRILcdmEMWGI0cm0JJ2QpYA1RDvcmzJEWhABhD/pqrL0S0CWuABKgnRki9lLseS7g2AlqwHWQSKH4oKLrILpRGhEQCw2LiRUIa4lwAAAABJRU5ErkJggg==)!important}#toast-container>.toast-warning{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGYSURBVEhL5ZSvTsNQFMbXZGICMYGYmJhAQIJAICYQPAACiSDB8AiICQQJT4CqQEwgJvYASAQCiZiYmJhAIBATCARJy+9rTsldd8sKu1M0+dLb057v6/lbq/2rK0mS/TRNj9cWNAKPYIJII7gIxCcQ51cvqID+GIEX8ASG4B1bK5gIZFeQfoJdEXOfgX4QAQg7kH2A65yQ87lyxb27sggkAzAuFhbbg1K2kgCkB1bVwyIR9m2L7PRPIhDUIXgGtyKw575yz3lTNs6X4JXnjV+LKM/m3MydnTbtOKIjtz6VhCBq4vSm3ncdrD2lk0VgUXSVKjVDJXJzijW1RQdsU7F77He8u68koNZTz8Oz5yGa6J3H3lZ0xYgXBK2QymlWWA+RWnYhskLBv2vmE+hBMCtbA7KX5drWyRT/2JsqZ2IvfB9Y4bWDNMFbJRFmC9E74SoS0CqulwjkC0+5bpcV1CZ8NMej4pjy0U+doDQsGyo1hzVJttIjhQ7GnBtRFN1UarUlH8F3xict+HY07rEzoUGPlWcjRFRr4/gChZgc3ZL2d8oAAAAASUVORK5CYII=)!important}#toast-container.toast-bottom-center>div,#toast-container.toast-top-center>div{width:300px;margin-left:auto;margin-right:auto}#toast-container.toast-bottom-full-width>div,#toast-container.toast-top-full-width>div{width:96%;margin-left:auto;margin-right:auto}.toast{background-color:#030303}.toast-success{background-color:#51A351}.toast-error{background-color:#BD362F}.toast-info{background-color:#2F96B4}.toast-warning{background-color:#F89406}.toast-progress{position:absolute;left:0;bottom:0;height:4px;background-color:#000;opacity:.4;-ms-filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=40);filter:alpha(opacity=40)}@media all and (max-width:240px){#toast-container>div{padding:8px 8px 8px 50px;width:11em}#toast-container>div.rtl{padding:8px 50px 8px 8px}#toast-container .toast-close-button{right:-.2em;top:-.2em}#toast-container .rtl .toast-close-button{left:-.2em;right:.2em}}@media all and (min-width:241px) and (max-width:480px){#toast-container>div{padding:8px 8px 8px 50px;width:18em}#toast-container>div.rtl{padding:8px 50px 8px 8px}#toast-container .toast-close-button{right:-.2em;top:-.2em}#toast-container .rtl .toast-close-button{left:-.2em;right:.2em}}@media all and (min-width:481px) and (max-width:768px){#toast-container>div{padding:15px 15px 15px 50px;width:25em}#toast-container>div.rtl{padding:15px 50px 15px 15px}} \ No newline at end of file diff --git a/web/static/js/clipboard.min.js b/web/static/js/clipboard.min.js new file mode 100644 index 00000000..28650f3c --- /dev/null +++ b/web/static/js/clipboard.min.js @@ -0,0 +1,7 @@ +/*! + * clipboard.js v2.0.6 + * https://clipboardjs.com/ + * + * Licensed MIT © Zeno Rocha + */ +!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.ClipboardJS=e():t.ClipboardJS=e()}(this,function(){return o={},r.m=n=[function(t,e){t.exports=function(t){var e;if("SELECT"===t.nodeName)t.focus(),e=t.value;else if("INPUT"===t.nodeName||"TEXTAREA"===t.nodeName){var n=t.hasAttribute("readonly");n||t.setAttribute("readonly",""),t.select(),t.setSelectionRange(0,t.value.length),n||t.removeAttribute("readonly"),e=t.value}else{t.hasAttribute("contenteditable")&&t.focus();var o=window.getSelection(),r=document.createRange();r.selectNodeContents(t),o.removeAllRanges(),o.addRange(r),e=o.toString()}return e}},function(t,e){function n(){}n.prototype={on:function(t,e,n){var o=this.e||(this.e={});return(o[t]||(o[t]=[])).push({fn:e,ctx:n}),this},once:function(t,e,n){var o=this;function r(){o.off(t,r),e.apply(n,arguments)}return r._=e,this.on(t,r,n)},emit:function(t){for(var e=[].slice.call(arguments,1),n=((this.e||(this.e={}))[t]||[]).slice(),o=0,r=n.length;o=0;o--)u(e(n[o]),t)}function u(t,n,o){var s=!(!o||!o.force)&&o.force;return!(!t||!s&&0!==e(":focus",t).length)&&(t[n.hideMethod]({duration:n.hideDuration,easing:n.hideEasing,complete:function(){h(t)}}),!0)}function d(t){return v=e("
").attr("id",t.containerId).addClass(t.positionClass),v.appendTo(e(t.target)),v}function p(){return{tapToDismiss:!0,toastClass:"toast",containerId:"toast-container",debug:!1,showMethod:"fadeIn",showDuration:300,showEasing:"swing",onShown:void 0,hideMethod:"fadeOut",hideDuration:1e3,hideEasing:"swing",onHidden:void 0,closeMethod:!1,closeDuration:!1,closeEasing:!1,closeOnHover:!0,extendedTimeOut:1e3,iconClasses:{error:"toast-error",info:"toast-info",success:"toast-success",warning:"toast-warning"},iconClass:"toast-info",positionClass:"toast-top-right",timeOut:5e3,titleClass:"toast-title",messageClass:"toast-message",escapeHtml:!1,target:"body",closeHtml:'',closeClass:"toast-close-button",newestOnTop:!0,preventDuplicates:!1,progressBar:!1,progressClass:"toast-progress",rtl:!1}}function f(e){C&&C(e)}function g(t){function o(e){return null==e&&(e=""),e.replace(/&/g,"&").replace(/"/g,""").replace(/'/g,"'").replace(//g,">")}function s(){c(),u(),d(),p(),g(),C(),l(),i()}function i(){var e="";switch(t.iconClass){case"toast-success":case"toast-info":e="polite";break;default:e="assertive"}I.attr("aria-live",e)}function a(){E.closeOnHover&&I.hover(H,D),!E.onclick&&E.tapToDismiss&&I.click(b),E.closeButton&&j&&j.click(function(e){e.stopPropagation?e.stopPropagation():void 0!==e.cancelBubble&&e.cancelBubble!==!0&&(e.cancelBubble=!0),E.onCloseClick&&E.onCloseClick(e),b(!0)}),E.onclick&&I.click(function(e){E.onclick(e),b()})}function r(){I.hide(),I[E.showMethod]({duration:E.showDuration,easing:E.showEasing,complete:E.onShown}),E.timeOut>0&&(k=setTimeout(b,E.timeOut),F.maxHideTime=parseFloat(E.timeOut),F.hideEta=(new Date).getTime()+F.maxHideTime,E.progressBar&&(F.intervalId=setInterval(x,10)))}function c(){t.iconClass&&I.addClass(E.toastClass).addClass(y)}function l(){E.newestOnTop?v.prepend(I):v.append(I)}function u(){if(t.title){var e=t.title;E.escapeHtml&&(e=o(t.title)),M.append(e).addClass(E.titleClass),I.append(M)}}function d(){if(t.message){var e=t.message;E.escapeHtml&&(e=o(t.message)),B.append(e).addClass(E.messageClass),I.append(B)}}function p(){E.closeButton&&(j.addClass(E.closeClass).attr("role","button"),I.prepend(j))}function g(){E.progressBar&&(q.addClass(E.progressClass),I.prepend(q))}function C(){E.rtl&&I.addClass("rtl")}function O(e,t){if(e.preventDuplicates){if(t.message===w)return!0;w=t.message}return!1}function b(t){var n=t&&E.closeMethod!==!1?E.closeMethod:E.hideMethod,o=t&&E.closeDuration!==!1?E.closeDuration:E.hideDuration,s=t&&E.closeEasing!==!1?E.closeEasing:E.hideEasing;if(!e(":focus",I).length||t)return clearTimeout(F.intervalId),I[n]({duration:o,easing:s,complete:function(){h(I),clearTimeout(k),E.onHidden&&"hidden"!==P.state&&E.onHidden(),P.state="hidden",P.endTime=new Date,f(P)}})}function D(){(E.timeOut>0||E.extendedTimeOut>0)&&(k=setTimeout(b,E.extendedTimeOut),F.maxHideTime=parseFloat(E.extendedTimeOut),F.hideEta=(new Date).getTime()+F.maxHideTime)}function H(){clearTimeout(k),F.hideEta=0,I.stop(!0,!0)[E.showMethod]({duration:E.showDuration,easing:E.showEasing})}function x(){var e=(F.hideEta-(new Date).getTime())/F.maxHideTime*100;q.width(e+"%")}var E=m(),y=t.iconClass||E.iconClass;if("undefined"!=typeof t.optionsOverride&&(E=e.extend(E,t.optionsOverride),y=t.optionsOverride.iconClass||y),!O(E,t)){T++,v=n(E,!0);var k=null,I=e("
"),M=e("
"),B=e("
"),q=e("
"),j=e(E.closeHtml),F={intervalId:null,hideEta:null,maxHideTime:null},P={toastId:T,state:"visible",startTime:new Date,options:E,map:t};return s(),r(),a(),f(P),E.debug&&console&&console.log(P),I}}function m(){return e.extend({},p(),b.options)}function h(e){v||(v=n()),e.is(":visible")||(e.remove(),e=null,0===v.children().length&&(v.remove(),w=void 0))}var v,C,w,T=0,O={error:"error",info:"info",success:"success",warning:"warning"},b={clear:r,remove:c,error:t,getContainer:n,info:o,options:{},subscribe:s,success:i,version:"2.1.3",warning:a};return b}()})}("function"==typeof define&&define.amd?define:function(e,t){"undefined"!=typeof module&&module.exports?module.exports=t(require("jquery")):window.toastr=t(window.jQuery)}); +//# sourceMappingURL=toastr.js.map diff --git a/web/static/page/languages.xml b/web/static/page/languages.xml index fdc80ae8..ed048e98 100644 --- a/web/static/page/languages.xml +++ b/web/static/page/languages.xml @@ -189,6 +189,12 @@ 访问端命令 Access command + + + 快捷启动命令 + Quickly command + + 客户端命令 Command diff --git a/web/views/client/list.html b/web/views/client/list.html index 25cb9de8..88d4a4ae 100644 --- a/web/views/client/list.html +++ b/web/views/client/list.html @@ -73,8 +73,9 @@
+ ': ' + row.BlackIpList + ' 

' + ': ' + row.CreateTime + ' 

' + ': ' + row.LastOnlineTime + ' 

' - + ': ' + "./npc{{.win}} -server={{.ip}}:{{.p}} -vkey=" + row.VerifyKey + " -type=" +{{.bridgeType}} +"
" - + ': ' + "./npc{{.win}} -server={{.ip}}:{{.tls_p}} -vkey=" + row.VerifyKey + " -tls_enable=true" + + ': ' + encodeToBase64('{{.ip}}:{{.p}} ' + row.VerifyKey) + '
' + + ': ' + "./npc{{.win}} -server={{.ip}}:{{.p}} -vkey=" + row.VerifyKey + " -type=" +{{.bridgeType}} +"
" + + ': ' + "./npc{{.win}} -server={{.ip}}:{{.tls_p}} -vkey=" + row.VerifyKey + " -tls_enable=true" }, //表格的列 columns: [ @@ -213,4 +214,26 @@
} ] }); + + function encodeToBase64(str){ + return btoa("nps "+ str); + } + + function copyCommand(data) { + data.setAttribute("data-clipboard-text", data.previousElementSibling.innerHTML) + } + + var clipboard = new ClipboardJS('.copy'); + clipboard.on('success', function (e) { + // alert("复制成功"+e.text) + toastr.success('复制成功'); + e.clearSelection(); + }); + + clipboard.on('error', function (e) { + // alert("复制失败") + toastr.error('复制失败', '提示'); + }); + + diff --git a/web/views/public/layout.html b/web/views/public/layout.html index 2696f917..2b419530 100644 --- a/web/views/public/layout.html +++ b/web/views/public/layout.html @@ -11,6 +11,7 @@ + @@ -19,6 +20,8 @@ + + @@ -28,6 +31,7 @@ +