From 17662e32e6c4717941be5162ef642d2561914dc1 Mon Sep 17 00:00:00 2001 From: ysicing Date: Thu, 5 Oct 2023 20:43:49 +0800 Subject: [PATCH] feat(nnr): add nnr support add nnr support Signed-off-by: ysicing --- cmd/nnr/nnr.go | 121 ++++++++++++++++++++++++++++++++++++++ cmd/root.go | 5 ++ internal/pkg/nnr/nnr.go | 53 +++++++++++++++++ internal/pkg/nnr/types.go | 44 ++++++++++++++ internal/util/util.go | 26 ++++++++ 5 files changed, 249 insertions(+) create mode 100644 cmd/nnr/nnr.go create mode 100644 internal/pkg/nnr/nnr.go create mode 100644 internal/pkg/nnr/types.go create mode 100644 internal/util/util.go diff --git a/cmd/nnr/nnr.go b/cmd/nnr/nnr.go new file mode 100644 index 0000000..c284b50 --- /dev/null +++ b/cmd/nnr/nnr.go @@ -0,0 +1,121 @@ +// Copyright (c) 2023 ysicing(ysicing.me, ysicing@12306.work) All rights reserved. +// Use of this source code is covered by the following dual licenses: +// (1) Y PUBLIC LICENSE 1.0 (YPL 1.0) +// (2) Affero General Public License 3.0 (AGPL 3.0) +// License that can be found in the LICENSE file. + +package nnr + +import ( + "fmt" + "os" + "sort" + "strings" + + "github.com/ergoapi/util/output" + "github.com/gosuri/uitable" + "github.com/spf13/cobra" + "github.com/ysicing/tiga/internal/pkg/nnr" + "github.com/ysicing/tiga/internal/util" + "github.com/ysicing/tiga/pkg/factory" +) + +var token string + +func NewCmdNNR(f factory.Factory) *cobra.Command { + cmd := &cobra.Command{ + Use: "nnr", + Short: "nnr tools", + Version: "2023.10.0519", + } + cmd.AddCommand(listServers(f)) + cmd.AddCommand(listRules(f)) + cmd.AddCommand(addRule(f)) + cmd.AddCommand(delRule(f)) + cmd.AddCommand(updateRule(f)) + cmd.PersistentFlags().StringVarP(&token, "token", "t", os.Getenv("NNR_TOKEN"), "token") + return cmd +} + +func listServers(f factory.Factory) *cobra.Command { + cmd := &cobra.Command{ + Use: "nodes", + Aliases: []string{"servers"}, + Short: "list servers", + RunE: func(cmd *cobra.Command, args []string) error { + api := nnr.New(token) + s, err := api.ListServers() + if err != nil { + return err + } + if len(s) == 0 { + f.GetLog().Infof("no servers found") + return nil + } + f.GetLog().Infof("found %d servers", len(s)) + sort.Slice(s, func(i, j int) bool { + return s[i].Mf >= s[j].Mf + }) + table := uitable.New() + table.AddRow("标识", "节点", "IP", "倍率", "支持TCP+UDP", "描述") + for _, index := range s { + table.AddRow(index.Sid, index.Name, index.Host, index.Mf, len(index.Types) == 3, strings.ReplaceAll(index.Detail, "\n", " ")) + } + return output.EncodeTable(os.Stdout, table) + }, + } + return cmd +} + +func listRules(f factory.Factory) *cobra.Command { + cmd := &cobra.Command{ + Use: "rules", + Short: "list rules", + RunE: func(cmd *cobra.Command, args []string) error { + api := nnr.New(token) + s, err := api.ListRules() + if err != nil { + return err + } + if len(s) == 0 { + f.GetLog().Infof("no rules found") + return nil + } + f.GetLog().Infof("found %d rules", len(s)) + sort.Slice(s, func(i, j int) bool { + return s[i].Traffic >= s[j].Traffic + }) + table := uitable.New() + table.AddRow("规则标识", "节点标识", "转发地址", "远程地址", "类型", "流量") + for _, index := range s { + table.AddRow(index.Rid, index.Sid, fmt.Sprintf("%v:%v", index.Host, index.Port), fmt.Sprintf("%v:%v", index.Remote, index.RPort), index.Type, util.Traffic(index.Traffic)) + } + return output.EncodeTable(os.Stdout, table) + }, + } + return cmd +} + +func addRule(f factory.Factory) *cobra.Command { + cmd := &cobra.Command{ + Use: "add", + Short: "add rule", + } + return cmd +} + +func delRule(f factory.Factory) *cobra.Command { + cmd := &cobra.Command{ + Use: "del", + Short: "del rule", + } + return cmd +} + +func updateRule(f factory.Factory) *cobra.Command { + cmd := &cobra.Command{ + Use: "update", + Short: "update rule", + } + return cmd +} diff --git a/cmd/root.go b/cmd/root.go index 772566c..34a948e 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -19,6 +19,7 @@ import ( "github.com/spf13/cobra" "github.com/ysicing/tiga/cmd/clash" "github.com/ysicing/tiga/cmd/flags" + "github.com/ysicing/tiga/cmd/nnr" "github.com/ysicing/tiga/cmd/xray" "github.com/ysicing/tiga/common" "github.com/ysicing/tiga/pkg/factory" @@ -68,8 +69,12 @@ func BuildRoot(f factory.Factory) *cobra.Command { if zos.IsLinux() { rootCmd.AddCommand(newCmdApp(f)) rootCmd.AddCommand(newCmdRepo(f)) + } + + if zos.IsUnix() { rootCmd.AddCommand(xray.NewCmdXray(f)) rootCmd.AddCommand(clash.NewCmdClash(f)) + rootCmd.AddCommand(nnr.NewCmdNNR(f)) } rootCmd.AddCommand(newManCmd()) diff --git a/internal/pkg/nnr/nnr.go b/internal/pkg/nnr/nnr.go new file mode 100644 index 0000000..1ade582 --- /dev/null +++ b/internal/pkg/nnr/nnr.go @@ -0,0 +1,53 @@ +// Copyright (c) 2023 ysicing(ysicing.me, ysicing@12306.work) All rights reserved. +// Use of this source code is covered by the following dual licenses: +// (1) Y PUBLIC LICENSE 1.0 (YPL 1.0) +// (2) Affero General Public License 3.0 (AGPL 3.0) +// License that can be found in the LICENSE file. + +package nnr + +import ( + "github.com/cockroachdb/errors" + "github.com/imroc/req/v3" + "github.com/ysicing/tiga/common" +) + +type Option struct { + *req.Request +} + +func New(token string) *Option { + reqClient := req.C(). + SetUserAgent(common.GetUG()). + R(). + SetHeader("accept", "application/json"). + SetHeaders(map[string]string{ + "Content-Type": "application/json", + "Token": token, + }) + return &Option{reqClient} +} + +func (o *Option) ListServers() ([]Server, error) { + var serversResp ServersResp + _, err := o.SetSuccessResult(&serversResp).Post("https://nnr.moe/api/servers") + if err != nil { + return nil, err + } + if serversResp.Status != 1 { + return nil, errors.New("list servers failed") + } + return serversResp.Data, nil +} + +func (o *Option) ListRules() ([]Rule, error) { + var rulesResp RulesResp + _, err := o.SetSuccessResult(&rulesResp).Post("https://nnr.moe/api/rules") + if err != nil { + return nil, err + } + if rulesResp.Status != 1 { + return nil, errors.New("list rules failed") + } + return rulesResp.Data, nil +} diff --git a/internal/pkg/nnr/types.go b/internal/pkg/nnr/types.go new file mode 100644 index 0000000..d7f2da5 --- /dev/null +++ b/internal/pkg/nnr/types.go @@ -0,0 +1,44 @@ +// Copyright (c) 2023 ysicing(ysicing.me, ysicing@12306.work) All rights reserved. +// Use of this source code is covered by the following dual licenses: +// (1) Y PUBLIC LICENSE 1.0 (YPL 1.0) +// (2) Affero General Public License 3.0 (AGPL 3.0) +// License that can be found in the LICENSE file. + +package nnr + +type Server struct { + Sid string `json:"sid,omitempty"` + Name string `json:"name,omitempty"` + Host string `json:"host,omitempty"` + Mf int `json:"mf,omitempty"` + Level int `json:"level,omitempty"` + Top int `json:"top,omitempty"` + Status int `json:"status,omitempty"` + Detail string `json:"detail,omitempty"` + Types []string `json:"types,omitempty"` +} + +type ServersResp struct { + Status int `json:"status,omitempty"` + Data []Server `json:"data,omitempty"` +} + +type Rule struct { + Rid string `json:"rid,omitempty"` + Uid string `json:"uid,omitempty"` + Sid string `json:"sid,omitempty"` + Host string `json:"host,omitempty"` + Port int `json:"port,omitempty"` + Remote string `json:"remote,omitempty"` + RPort int `json:"rport,omitempty"` + Type string `json:"type,omitempty"` + Status int `json:"status,omitempty"` + Name string `json:"name,omitempty"` + Traffic int64 `json:"traffic,omitempty"` + Date int64 `json:"date,omitempty"` +} + +type RulesResp struct { + Status int `json:"status,omitempty"` + Data []Rule `json:"data,omitempty"` +} diff --git a/internal/util/util.go b/internal/util/util.go new file mode 100644 index 0000000..a9578d7 --- /dev/null +++ b/internal/util/util.go @@ -0,0 +1,26 @@ +// Copyright (c) 2023 ysicing(ysicing.me, ysicing@12306.work) All rights reserved. +// Use of this source code is covered by the following dual licenses: +// (1) Y PUBLIC LICENSE 1.0 (YPL 1.0) +// (2) Affero General Public License 3.0 (AGPL 3.0) +// License that can be found in the LICENSE file. + +package util + +import ( + "fmt" + "math" +) + +func Traffic(k int64) string { + t := math.Round(float64(k)/1024.0/1024.0*100) / 100 + tunit := "MB" + if t >= 1024.0 { + t = t / 1024.0 + tunit = "GB" + } + if t >= 1024.0 { + t = t / 1024.0 + tunit = "TB" + } + return fmt.Sprintf("%v%v", t, tunit) +}