diff --git a/cmd/verysimple/cli.go b/cmd/verysimple/cli.go index 46bb125..f82ac40 100644 --- a/cmd/verysimple/cli.go +++ b/cmd/verysimple/cli.go @@ -66,6 +66,8 @@ func init() { sc := mainM.GetStandardConfFromCurrentState() interactively_generate_share(&sc) }, + }, &CliCmd{ + "【导出标准配置文件】<-当前全部配置", interactively_exportVsConf, }, &CliCmd{ "【交互生成配置】,超级强大", func() { generateConfigFileInteractively(mainM) }, }, &CliCmd{ @@ -96,29 +98,6 @@ func runCli_func() { loadPreferences() - /* - langList := []string{"简体中文", "English"} - utils.PrintStr("Welcome to Interactive Mode, please choose a Language \n") - Select := promptui.Select{ - Label: "Select Language", - Items: langList, - } - - _, result, err := Select.Run() - - if err != nil { - fmt.Printf("Prompt failed %v\n", err) - return - } - - fmt.Printf("You choose %q\n", result) - - if result != langList[0] { - utils.PrintStr("Sorry, language not supported yet \n") - return - } - */ - searcher := func(input string, index int) bool { pepper := cliCmdList[index] name := strings.Replace(strings.ToLower(pepper.Name), " ", "", -1) diff --git a/cmd/verysimple/cli_generateConfigs.go b/cmd/verysimple/cli_generateConfigs.go index 12df159..eeb931c 100644 --- a/cmd/verysimple/cli_generateConfigs.go +++ b/cmd/verysimple/cli_generateConfigs.go @@ -5,6 +5,7 @@ package main import ( "errors" "fmt" + "os" "strconv" "strings" @@ -15,6 +16,43 @@ import ( "github.com/manifoldco/promptui" ) +func interactively_exportVsConf() { + vc := mainM.GetVSConfFromCurrentState() + + bs, e := utils.GetPurgedTomlBytes(vc) + if e != nil { + utils.PrintStr("转换格式错误\n") + utils.PrintStr(e.Error()) + utils.PrintStr("\n") + return + } + + utils.PrintStr("请输入生成配置文件的名称(不含.toml后缀)\n") + + promptDomain := promptui.Prompt{ + Label: "文件名", + } + + result, err := promptDomain.Run() + if err != nil { + fmt.Println("Prompt failed ", err, result) + return + } + + fmt.Printf("你输入了 %s\n", result) + + e = os.WriteFile(result+".toml", bs, 0666) + + if e != nil { + utils.PrintStr("写入文件错误\n") + utils.PrintStr(e.Error()) + utils.PrintStr("\n") + return + } + + utils.PrintStr("导出成功!\n") +} + func interactively_generate_share(conf *proxy.StandardConf) { all := []*CliCmd{ @@ -188,11 +226,12 @@ func interactively_generateConf(confClient, confServer *proxy.StandardConf) { "vless", "vmess", "trojan", + "shadowsocks", }, } i3, result, err := select3.Run() - if err != nil || i3 != 0 { + if err != nil { fmt.Println("Prompt failed ", err, i3) return } @@ -232,7 +271,7 @@ func interactively_generateConf(confClient, confServer *proxy.StandardConf) { i4, result, err := select4.Run() if err != nil { - fmt.Println("Prompt failed ", err, i3) + fmt.Println("Prompt failed ", err, i4) return } @@ -286,8 +325,7 @@ func interactively_generateConf(confClient, confServer *proxy.StandardConf) { utils.PrintStr("请输入你服务端的域名\n") promptDomain := promptui.Prompt{ - Label: "域名", - Validate: func(s string) error { return nil }, //允许不设域名 + Label: "域名", } result, err = promptDomain.Run() @@ -310,7 +348,7 @@ func interactively_generateConf(confClient, confServer *proxy.StandardConf) { i5, result, err := select5.Run() if err != nil { - fmt.Println("Prompt failed ", err, i3) + fmt.Println("Prompt failed ", err, i5) return } if i5 == 0 { @@ -358,7 +396,7 @@ func interactively_generateConf(confClient, confServer *proxy.StandardConf) { i6, result, err := select6.Run() if err != nil { - fmt.Println("Prompt failed ", err, i3) + fmt.Println("Prompt failed ", err, i6) return } if i6 == 0 { diff --git a/cmd/verysimple/gui.go b/cmd/verysimple/gui.go index aa484a8..5f3f096 100644 --- a/cmd/verysimple/gui.go +++ b/cmd/verysimple/gui.go @@ -278,6 +278,31 @@ func makeBasicControlsPage() ui.Control { return } + vc := mainM.GetVSConfFromCurrentState() + + bs, e := utils.GetPurgedTomlBytes(vc) + if e != nil { + if ce := utils.CanLogErr("转换格式错误"); ce != nil { + ce.Write(zap.Error(e)) + } + + return + } + filename += ".toml" + e = os.WriteFile(filename, bs, 0666) + + if e != nil { + if ce := utils.CanLogErr("写入文件错误"); ce != nil { + ce.Write(zap.Error(e)) + } + + return + } + + if ce := utils.CanLogInfo("导出成功"); ce != nil { + ce.Write(zap.String("filename", filename)) + } + }) fgrid.Append(button, 1, 1, 1, 1, diff --git a/cmd/verysimple/main.go b/cmd/verysimple/main.go index 6a0a510..86f176b 100644 --- a/cmd/verysimple/main.go +++ b/cmd/verysimple/main.go @@ -391,5 +391,5 @@ func NoFuture(m *machine.M) bool { } func NothingRunning(m *machine.M) bool { - return !m.HasProxyRunning() && !(interactive_mode || gui_mode || m.ApiServerRunning) + return !m.HasProxyRunning() && !(interactive_mode || gui_mode || m.IsApiServerRunning()) } diff --git a/examples/doco_udpclient.go b/examples/doko_udpclient.go similarity index 100% rename from examples/doco_udpclient.go rename to examples/doko_udpclient.go diff --git a/examples/multi.server.toml b/examples/multi.server.toml index 62be671..f62e38b 100644 --- a/examples/multi.server.toml +++ b/examples/multi.server.toml @@ -4,6 +4,15 @@ default_uuid = "a684455c-b14f-11ea-bf0d-42010aaa0003" # 这个除了作为默 # 每个listen 可以只配置users来配置用户列表,然后 这个default_uuid 会作为 每个listen 的uuid 的值。 # 这样作为管理员,每个listen自己都能用自己的 账户连上,不必再额外对每一个listen 添加一遍 自己的账户。 +# [apiServer] # v1.2.5开始, apiServer配置单独在一项中配置 +# enable = true # 默认为false +# plain = false # 是否使用明文http, 默认false +# admin_pass = "i_love_verysimple" +# addr = "127.0.0.1:8080" +# key = "/home/vs/key" # 若不用明文http, 可配置tls证书, 若不给出, vs会自动生成随机证书 +# cert = "/home/vs/cert" +# prefix = "/myapi" + [[listen]] tag = "my_vlesss1" protocol = "vlesss" diff --git a/machine/apiServer.go b/machine/apiServer.go index 0e91091..444757b 100644 --- a/machine/apiServer.go +++ b/machine/apiServer.go @@ -85,7 +85,7 @@ func (asc *ApiServerConf) SetUnDefault(acref *ApiServerConf) { // 非阻塞,如果运行成功则 apiServerRunning 会被设为 true func (m *M) TryRunApiServer() { - m.ApiServerRunning = true + m.apiServerRunning = true go m.runApiServer() @@ -279,7 +279,7 @@ func (m *M) runApiServer() { srv.ListenAndServeTLS(m.CertFile, m.KeyFile) } - m.ApiServerRunning = false + m.apiServerRunning = false } type auth struct { diff --git a/machine/conf.go b/machine/conf.go index 212459d..5518980 100644 --- a/machine/conf.go +++ b/machine/conf.go @@ -145,13 +145,13 @@ func (m *M) LoadConfigByTomlBytes(bs []byte) (err error) { m.AppConf.Setup() } if vsConf.ApiServerConf != nil { - m.TomlApiServerConf = *vsConf.ApiServerConf + m.tomlApiServerConf = *vsConf.ApiServerConf } return nil } -// 先检查configFileName是否存在,存在就尝试加载文件到 standardConf , 否则尝试通过 listenURL, dialURL 参数 创建urlConf +// 先检查configFileName是否存在,存在就尝试加载文件到 standardConf , 否则尝试通过 listenURL, dialURL 参数 创建urlConf. 若使用url, 自动加载进机器; 若为toml, 需要手动调用 SetupListenAndRoute 和 SetupDial func (m *M) LoadConfig(configFileName, listenURL, dialURL string) (confMode int, err error) { fpath := utils.GetFilePath(configFileName) @@ -218,7 +218,7 @@ func (m *M) SetupListenAndRoute() { m.ParseFallbacksAtSymbol(m.standardConf.Fallbacks) } - m.RoutingEnv = proxy.LoadEnvFromStandardConf(&m.standardConf, myCountryISO_3166) + m.routingEnv = proxy.LoadEnvFromStandardConf(&m.standardConf, myCountryISO_3166) } func (m *M) SetupDial() { diff --git a/machine/load.go b/machine/load.go index 6d2f316..89fbdcd 100644 --- a/machine/load.go +++ b/machine/load.go @@ -30,7 +30,7 @@ func (m *M) LoadDialConf(conf []*proxy.DialConf) (ok bool) { m.allClients = append(m.allClients, outClient) if tag := outClient.GetTag(); tag != "" { - m.RoutingEnv.SetClient(tag, outClient) + m.routingEnv.SetClient(tag, outClient) } } @@ -69,7 +69,7 @@ func (m *M) LoadListenConf(conf []*proxy.ListenConf, hot bool) (ok bool) { } if hot { - lis := v2ray_simple.ListenSer(inServer, m.DefaultOutClient, &m.RoutingEnv, &m.GlobalInfo) + lis := v2ray_simple.ListenSer(inServer, m.DefaultOutClient, &m.routingEnv, &m.GlobalInfo) if lis != nil { m.listenCloserList = append(m.listenCloserList, lis) m.allServers = append(m.allServers, inServer) @@ -92,7 +92,7 @@ func (m *M) HotDeleteClient(index int) { } doomedClient := m.allClients[index] - m.RoutingEnv.DelClient(doomedClient.GetTag()) + m.routingEnv.DelClient(doomedClient.GetTag()) doomedClient.Stop() m.allClients = utils.TrimSlice(m.allClients, index) } @@ -123,7 +123,7 @@ func (m *M) loadUrlConf(hot bool) (result int) { } if hot { - lis := v2ray_simple.ListenSer(ser, cli, &m.RoutingEnv, &m.GlobalInfo) + lis := v2ray_simple.ListenSer(ser, cli, &m.routingEnv, &m.GlobalInfo) if lis != nil { m.listenCloserList = append(m.listenCloserList, lis) } else { @@ -168,6 +168,14 @@ func (m *M) loadUrlClient(urlConf proxy.UrlConf) (result int, client proxy.Clien return } +func (m *M) GetVSConfFromCurrentState() (vc VSConf) { + vc.StandardConf = m.GetStandardConfFromCurrentState() + vc.ApiServerConf = &m.ApiServerConf + vc.AppConf = &m.AppConf + + return +} + func (m *M) GetStandardConfFromCurrentState() (sc proxy.StandardConf) { for i := range m.allClients { sc.Dial = append(sc.Dial, m.getDialConfFromCurrentState(i)) diff --git a/machine/machine.go b/machine/machine.go index a55b3bf..3a9f3be 100644 --- a/machine/machine.go +++ b/machine/machine.go @@ -18,44 +18,42 @@ import ( "github.com/e1732a364fed/v2ray_simple/httpLayer" "github.com/e1732a364fed/v2ray_simple/proxy" "github.com/e1732a364fed/v2ray_simple/utils" + "go.uber.org/zap" ) type M struct { - ApiServerConf - - TomlApiServerConf ApiServerConf - CmdApiServerConf ApiServerConf + sync.RWMutex + v2ray_simple.GlobalInfo AppConf + ApiServerConf + + tomlApiServerConf ApiServerConf + CmdApiServerConf ApiServerConf + standardConf proxy.StandardConf urlConf proxy.UrlConf - //appConf *AppConf - v2ray_simple.GlobalInfo - sync.RWMutex - - //DefaultUUID string - - ApiServerRunning bool + running bool + apiServerRunning bool DefaultOutClient proxy.Client - RoutingEnv proxy.RoutingEnv - - callbacks + routingEnv proxy.RoutingEnv allServers []proxy.Server allClients []proxy.Client listenCloserList []io.Closer - running bool + + callbacks } func New() *M { m := new(M) m.allClients = make([]proxy.Client, 0, 8) m.allServers = make([]proxy.Server, 0, 8) - m.RoutingEnv.ClientsTagMap = make(map[string]proxy.Client) + m.routingEnv.ClientsTagMap = make(map[string]proxy.Client) directClient, _ := proxy.ClientFromURL(proxy.DirectURL) m.DefaultOutClient = directClient return m @@ -83,6 +81,10 @@ func (m *M) IsRunning() bool { return m.running } +func (m *M) IsApiServerRunning() bool { + return m.apiServerRunning +} + // 运行配置 以及 apiServer func (m *M) Start() { if (m.DefaultOutClient != nil) && (len(m.allServers) > 0) { @@ -91,20 +93,20 @@ func (m *M) Start() { m.running = true m.callToggleFallback(1) for _, inServer := range m.allServers { - lis := v2ray_simple.ListenSer(inServer, m.DefaultOutClient, &m.RoutingEnv, &m.GlobalInfo) + lis := v2ray_simple.ListenSer(inServer, m.DefaultOutClient, &m.routingEnv, &m.GlobalInfo) if lis != nil { m.listenCloserList = append(m.listenCloserList, lis) } } - if dm := m.RoutingEnv.DnsMachine; dm != nil { + if dm := m.routingEnv.DnsMachine; dm != nil { dm.StartListen() } m.Unlock() } - if !m.ApiServerRunning && m.EnableApiServer { + if !m.apiServerRunning && m.EnableApiServer { m.TryRunApiServer() } @@ -114,10 +116,11 @@ func (m *M) Start() { func (m *M) SetupApiConf() { m.ApiServerConf = NewApiServerConf() - m.ApiServerConf.SetUnDefault(&m.TomlApiServerConf) + m.ApiServerConf.SetUnDefault(&m.tomlApiServerConf) m.ApiServerConf.SetUnDefault(&m.CmdApiServerConf) } +// Stop不会停止ApiServer func (m *M) Stop() { utils.Info("Stopping...") @@ -135,7 +138,7 @@ func (m *M) Stop() { listener.Close() } } - if dm := m.RoutingEnv.DnsMachine; dm != nil { + if dm := m.routingEnv.DnsMachine; dm != nil { dm.Stop() } m.Unlock() @@ -145,7 +148,7 @@ func (m *M) SetDefaultDirectClient() { m.allClients = append(m.allClients, v2ray_simple.DirectClient) m.DefaultOutClient = v2ray_simple.DirectClient - m.RoutingEnv.SetClient("direct", v2ray_simple.DirectClient) + m.routingEnv.SetClient("direct", v2ray_simple.DirectClient) } // 将fallback配置中的@转化成实际对应的server的地址 @@ -157,7 +160,10 @@ func (m *M) ParseFallbacksAtSymbol(fs []*httpLayer.FallbackConf) { if deststr, ok := fbConf.Dest.(string); ok && strings.HasPrefix(deststr, "@") { for _, s := range m.allServers { if s.GetTag() == deststr[1:] { - //log.Println("got tag fallback dest, will set to ", s.AddrStr()) + + if ce := utils.CanLogDebug("got @tag fallback dest"); ce != nil { + ce.Write(zap.String("will set to ", s.AddrStr()), zap.String("tag", deststr[1:])) + } fbConf.Dest = s.AddrStr() } } diff --git a/utils/strings.go b/utils/strings.go index f096cf3..7e4a703 100644 --- a/utils/strings.go +++ b/utils/strings.go @@ -1,6 +1,7 @@ package utils import ( + "bytes" "math/rand" "os" "strings" @@ -67,3 +68,24 @@ func GetPurgedTomlStr(v any) (string, error) { return sb.String(), nil } + +// mimic GetPurgedTomlStr +func GetPurgedTomlBytes(v any) ([]byte, error) { + buf := GetBuf() + defer PutBuf(buf) + if err := toml.NewEncoder(buf).Encode(v); err != nil { + return nil, err + } + lines := bytes.Split(buf.Bytes(), []byte{'\n'}) + var sb bytes.Buffer + + for _, l := range lines { + if !bytes.HasSuffix(l, []byte(` = ""`)) && !bytes.HasSuffix(l, []byte(` = false`)) && !bytes.HasSuffix(l, []byte(` = 0`)) { + + sb.Write(l) + sb.WriteByte('\n') + } + } + return sb.Bytes(), nil + +}