diff --git a/README.md b/README.md index eca3c32..9a45d33 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ A V2board node server based on multi core, modified from XrayR. | 在线人数统计 | √ | √ | √ | √ | | 审计规则 | √ | √ | √ | | | 自定义DNS | √ | √ | √ | √ | -| 在线IP数限制 | √ | √ | √ | | +| 在线IP数限制 | √ | √ | √ | √ | | 连接数限制 | √ | √ | √ | | | 跨节点IP数限制 | √ | √ | √ | | | 按照用户限速 | √ | √ | √ | | @@ -43,6 +43,7 @@ A V2board node server based on multi core, modified from XrayR. - [x] 集成基本操作Command(Start, Stop, Restart, Status, Uninstall) - [ ] 完善Hysteria内核支持 - [ ] 完善使用文档 +- [ ] 尽可能统一日志输出格式 ## 软件安装 diff --git a/api/panel/node.go b/api/panel/node.go index e731d42..4d7dac1 100644 --- a/api/panel/node.go +++ b/api/panel/node.go @@ -2,6 +2,7 @@ package panel import ( "fmt" + "github.com/Yuzuki616/V2bX/conf" "github.com/goccy/go-json" "reflect" "regexp" @@ -41,11 +42,6 @@ type ShadowsocksNodeRsp struct { ServerKey string `json:"server_key"` } -type TrojanNodeRsp struct { - Host string `json:"host"` - ServerName string `json:"server_name"` -} - type HysteriaNodeRsp struct { UpMbps int `json:"up_mbps"` DownMbps int `json:"down_mbps"` @@ -59,6 +55,7 @@ type NodeInfo struct { Host string Port int Network string + ExtraConfig V2rayExtraConfig NetworkSettings json.RawMessage Tls bool ServerName string @@ -71,13 +68,23 @@ type NodeInfo struct { PullInterval time.Duration } +type V2rayExtraConfig struct { + EnableVless bool `json:"EnableVless"` + VlessFlow string `json:"VlessFlow"` + EnableReality bool `json:"EnableReality"` + RealityConfig conf.RealityConfig `json:"RealityConfig"` +} + func (c *Client) GetNodeInfo() (node *NodeInfo, err error) { const path = "/api/v1/server/UniProxy/config" - r, err := c.client.R().Get(path) + r, err := c.client. + R(). + SetHeader("If-None-Match", c.etag). + Get(path) if err = c.checkResponse(r, path, err); err != nil { return } - if c.etag == r.Header().Get("ETag") { // node info not changed + if r.StatusCode() == 304 { return nil, nil } // parse common params @@ -90,17 +97,30 @@ func (c *Client) GetNodeInfo() (node *NodeInfo, err error) { if err != nil { return nil, fmt.Errorf("decode common params error: %s", err) } + var extra []byte for i := range common.Routes { // parse rules from routes - if common.Routes[i].Action == "block" { - var matchs []string - if _, ok := common.Routes[i].Match.(string); ok { - matchs = strings.Split(common.Routes[i].Match.(string), ",") - } else { - matchs = common.Routes[i].Match.([]string) + var matchs []string + if _, ok := common.Routes[i].Match.(string); ok { + matchs = strings.Split(common.Routes[i].Match.(string), ",") + } else if _, ok = common.Routes[i].Match.([]string); ok { + matchs = common.Routes[i].Match.([]string) + } else { + temp := common.Routes[i].Match.([]interface{}) + matchs = make([]string, len(temp)) + for i := range temp { + matchs[i] = temp[i].(string) } + } + switch common.Routes[i].Action { + case "block": for _, v := range matchs { node.Rules = append(node.Rules, regexp.MustCompile(v)) } + case "dns": + if matchs[0] != "extra" { + break + } + extra = []byte(strings.Join(matchs[1:], "")) } } node.ServerName = common.ServerName @@ -122,6 +142,12 @@ func (c *Client) GetNodeInfo() (node *NodeInfo, err error) { if rsp.Tls == 1 { node.Tls = true } + if len(extra) != 0 { + err = json.Unmarshal(extra, &node.ExtraConfig) + if err != nil { + return nil, fmt.Errorf("decode v2ray extra error: %s", err) + } + } case "shadowsocks": rsp := ShadowsocksNodeRsp{} err = json.Unmarshal(r.Body(), &rsp) @@ -131,11 +157,6 @@ func (c *Client) GetNodeInfo() (node *NodeInfo, err error) { node.ServerKey = rsp.ServerKey node.Cipher = rsp.Cipher case "trojan": - rsp := TrojanNodeRsp{} - err = json.Unmarshal(r.Body(), &rsp) - if err != nil { - return nil, fmt.Errorf("decode v2ray params error: %s", err) - } case "hysteria": rsp := HysteriaNodeRsp{} err = json.Unmarshal(r.Body(), &rsp) @@ -146,7 +167,7 @@ func (c *Client) GetNodeInfo() (node *NodeInfo, err error) { node.UpMbps = rsp.UpMbps node.HyObfs = rsp.Obfs } - c.etag = r.Header().Get("Etag") + c.etag = r.Header().Get("ETag") return } diff --git a/api/panel/node_test.go b/api/panel/node_test.go index dea16e8..570182f 100644 --- a/api/panel/node_test.go +++ b/api/panel/node_test.go @@ -23,6 +23,7 @@ func init() { func TestClient_GetNodeInfo(t *testing.T) { log.Println(client.GetNodeInfo()) + log.Println(client.GetNodeInfo()) } func TestClient_ReportUserTraffic(t *testing.T) { diff --git a/api/panel/utils.go b/api/panel/utils.go index 938bec9..74ef5b3 100644 --- a/api/panel/utils.go +++ b/api/panel/utils.go @@ -18,7 +18,7 @@ func (c *Client) checkResponse(res *resty.Response, path string, err error) erro if err != nil { return fmt.Errorf("request %s failed: %s", c.assembleURL(path), err) } - if res.StatusCode() != 200 { + if res.StatusCode() >= 400 { body := res.Body() return fmt.Errorf("request %s failed: %s", c.assembleURL(path), string(body)) } diff --git a/cmd/cmd.go b/cmd/cmd.go index 8bf7d47..26f9130 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -1,7 +1,7 @@ package cmd import ( - "log" + log "github.com/sirupsen/logrus" _ "github.com/Yuzuki616/V2bX/core/imports" "github.com/spf13/cobra" @@ -14,6 +14,6 @@ var command = &cobra.Command{ func Run() { err := command.Execute() if err != nil { - log.Println("execute failed, error:", err) + log.WithField("err", err).Error("Execute command failed") } } diff --git a/cmd/server.go b/cmd/server.go index 1542389..34ceeae 100644 --- a/cmd/server.go +++ b/cmd/server.go @@ -1,7 +1,7 @@ package cmd import ( - "log" + log "github.com/sirupsen/logrus" "os" "os/signal" "runtime" @@ -42,23 +42,26 @@ func serverHandle(_ *cobra.Command, _ []string) { c := conf.New() err := c.LoadFromPath(config) if err != nil { - log.Fatalf("can't unmarshal config file: %s \n", err) + log.WithField("err", err).Error("Load config file failed") + return } limiter.Init() - log.Println("Start V2bX...") + log.Info("Start V2bX...") vc, err := vCore.NewCore(&c.CoreConfig) if err != nil { - log.Fatalf("New core error: %s", err) + log.WithField("err", err).Error("new core failed") + return } err = vc.Start() if err != nil { - log.Fatalf("Start core error: %s", err) + log.WithField("err", err).Error("Start core failed") + return } defer vc.Close() nodes := node.New() err = nodes.Start(c.NodesConfig, vc) if err != nil { - log.Fatalf("Run nodes error: %s", err) + log.WithField("err", err).Error("Run nodes failed") return } if watch { @@ -66,24 +69,29 @@ func serverHandle(_ *cobra.Command, _ []string) { nodes.Close() err = vc.Close() if err != nil { - log.Fatalf("Failed to restart xray-core: %s", err) + log.WithField("err", err).Error("Restart node failed") + return } vc, err = vCore.NewCore(&c.CoreConfig) if err != nil { - log.Fatalf("New core error: %s", err) + log.WithField("err", err).Error("New core failed") + return } err = vc.Start() if err != nil { - log.Fatalf("Start core error: %s", err) + log.WithField("err", err).Error("Start core failed") + return } err = nodes.Start(c.NodesConfig, vc) if err != nil { - log.Fatalf("Run nodes error: %s", err) + log.WithField("err", err).Error("Run nodes failed") + return } runtime.GC() }) if err != nil { - log.Fatalf("Watch config file error: %s", err) + log.WithField("err", err).Error("start watch failed") + return } } // clear memory diff --git a/common/builder/inbound.go b/common/builder/inbound.go index 18e62ee..21e3c9f 100644 --- a/common/builder/inbound.go +++ b/common/builder/inbound.go @@ -90,6 +90,7 @@ func BuildInbound(config *conf.ControllerConfig, nodeInfo *panel.NodeInfo, tag s } default: // Normal tls + in.StreamSetting.Security = "tls" in.StreamSetting.TLSSettings = &coreConf.TLSConfig{ Certs: []*coreConf.TLSCertConfig{ { @@ -102,6 +103,25 @@ func BuildInbound(config *conf.ControllerConfig, nodeInfo *panel.NodeInfo, tag s } } } + // use remote reality replace local config + if nodeInfo.ExtraConfig.EnableReality { + rc := nodeInfo.ExtraConfig.RealityConfig + in.StreamSetting.Security = "reality" + d, err := json.Marshal(rc.Dest) + if err != nil { + return nil, fmt.Errorf("marshal reality dest error: %s", err) + } + in.StreamSetting.REALITYSettings = &coreConf.REALITYConfig{ + Dest: d, + Xver: rc.Xver, + ServerNames: rc.ServerNames, + PrivateKey: rc.PrivateKey, + MinClientVer: rc.MinClientVer, + MaxClientVer: rc.MaxClientVer, + MaxTimeDiff: rc.MaxTimeDiff, + ShortIds: rc.ShortIds, + } + } // Support ProxyProtocol for any transport protocol if *in.StreamSetting.Network != "tcp" && *in.StreamSetting.Network != "ws" && @@ -117,7 +137,8 @@ func BuildInbound(config *conf.ControllerConfig, nodeInfo *panel.NodeInfo, tag s } func buildV2ray(config *conf.ControllerConfig, nodeInfo *panel.NodeInfo, inbound *coreConf.InboundDetourConfig) error { - if config.XrayOptions.EnableVless { + if config.XrayOptions.EnableVless || + nodeInfo.ExtraConfig.EnableVless { //Set vless inbound.Protocol = "vless" if config.XrayOptions.EnableFallback { diff --git a/common/builder/user.go b/common/builder/user.go index a410a1d..bde1c86 100644 --- a/common/builder/user.go +++ b/common/builder/user.go @@ -14,8 +14,6 @@ import ( "strings" ) -const xtlsFLow = "xtls-rprx-vision" - func BuildVmessUsers(tag string, userInfo []panel.UserInfo) (users []*protocol.User) { users = make([]*protocol.User, len(userInfo)) for i, user := range userInfo { @@ -37,21 +35,19 @@ func BuildVmessUser(tag string, userInfo *panel.UserInfo) (user *protocol.User) } } -func BuildVlessUsers(tag string, userInfo []panel.UserInfo, xtls bool) (users []*protocol.User) { +func BuildVlessUsers(tag string, userInfo []panel.UserInfo, flow string) (users []*protocol.User) { users = make([]*protocol.User, len(userInfo)) for i := range userInfo { - users[i] = BuildVlessUser(tag, &(userInfo)[i], xtls) + users[i] = BuildVlessUser(tag, &(userInfo)[i], flow) } return users } -func BuildVlessUser(tag string, userInfo *panel.UserInfo, xtls bool) (user *protocol.User) { +func BuildVlessUser(tag string, userInfo *panel.UserInfo, flow string) (user *protocol.User) { vlessAccount := &vless.Account{ Id: userInfo.Uuid, } - if xtls { - vlessAccount.Flow = xtlsFLow - } + vlessAccount.Flow = flow return &protocol.User{ Level: 0, Email: BuildUserTag(tag, userInfo.Uuid), @@ -93,7 +89,7 @@ func BuildSSUser(tag string, userInfo *panel.UserInfo, cypher string, serverKey } return &protocol.User{ Level: 0, - Email: tag, + Email: BuildUserTag(tag, userInfo.Uuid), Account: serial.ToTypedMessage(ssAccount), } } else { diff --git a/common/task/task.go b/common/task/task.go new file mode 100644 index 0000000..f5a3234 --- /dev/null +++ b/common/task/task.go @@ -0,0 +1,78 @@ +package task + +import ( + "sync" + "time" +) + +// Task is a task that runs periodically. +type Task struct { + // Interval of the task being run + Interval time.Duration + // Execute is the task function + Execute func() error + + access sync.Mutex + timer *time.Timer + running bool +} + +func (t *Task) hasClosed() bool { + t.access.Lock() + defer t.access.Unlock() + + return !t.running +} + +func (t *Task) checkedExecute(first bool) error { + if t.hasClosed() { + return nil + } + + t.access.Lock() + defer t.access.Unlock() + if first { + if err := t.Execute(); err != nil { + t.running = false + return err + } + } + if !t.running { + return nil + } + t.timer = time.AfterFunc(t.Interval, func() { + t.checkedExecute(true) + }) + + return nil +} + +// Start implements common.Runnable. +func (t *Task) Start(first bool) error { + t.access.Lock() + if t.running { + t.access.Unlock() + return nil + } + t.running = true + t.access.Unlock() + if err := t.checkedExecute(first); err != nil { + t.access.Lock() + t.running = false + t.access.Unlock() + return err + } + return nil +} + +// Close implements common.Closable. +func (t *Task) Close() { + t.access.Lock() + defer t.access.Unlock() + + t.running = false + if t.timer != nil { + t.timer.Stop() + t.timer = nil + } +} diff --git a/common/task/task_test.go b/common/task/task_test.go new file mode 100644 index 0000000..dec33b1 --- /dev/null +++ b/common/task/task_test.go @@ -0,0 +1,15 @@ +package task + +import ( + "log" + "testing" + "time" +) + +func TestTask(t *testing.T) { + ts := Task{Execute: func() error { + log.Println("q") + return nil + }, Interval: time.Second} + ts.Start(false) +} diff --git a/conf/conf.go b/conf/conf.go index e54997c..4f2c7e8 100644 --- a/conf/conf.go +++ b/conf/conf.go @@ -2,14 +2,9 @@ package conf import ( "fmt" - "io" - "log" - "os" - "path" - "time" - - "github.com/fsnotify/fsnotify" "gopkg.in/yaml.v3" + "io" + "os" ) type Conf struct { @@ -56,44 +51,3 @@ func (p *Conf) LoadFromPath(filePath string) error { } return nil } - -func (p *Conf) Watch(filePath string, reload func()) error { - watcher, err := fsnotify.NewWatcher() - if err != nil { - return fmt.Errorf("new watcher error: %s", err) - } - go func() { - var pre time.Time - defer watcher.Close() - for { - select { - case e := <-watcher.Events: - if e.Has(fsnotify.Chmod) { - continue - } - if pre.Add(1 * time.Second).After(time.Now()) { - continue - } - time.Sleep(2 * time.Second) - pre = time.Now() - log.Println("config dir changed, reloading...") - *p = *New() - err := p.LoadFromPath(filePath) - if err != nil { - log.Printf("reload config error: %s", err) - } - reload() - log.Println("reload config success") - case err := <-watcher.Errors: - if err != nil { - log.Printf("File watcher error: %s", err) - } - } - } - }() - err = watcher.Add(path.Dir(filePath)) - if err != nil { - return fmt.Errorf("watch file error: %s", err) - } - return nil -} diff --git a/conf/node.go b/conf/node.go index 9304ef1..f8b38c9 100644 --- a/conf/node.go +++ b/conf/node.go @@ -15,28 +15,26 @@ type ApiConfig struct { } type ControllerConfig struct { - DisableUploadTraffic bool `yaml:"DisableUploadTraffic"` - DisableGetRule bool `yaml:"DisableGetRule"` - ListenIP string `yaml:"ListenIP"` - SendIP string `yaml:"SendIP"` - XrayOptions XrayOptions `yaml:"XrayOptions"` - HyOptions HyOptions `yaml:"HyOptions"` - LimitConfig LimitConfig `yaml:"LimitConfig"` - CertConfig *CertConfig `yaml:"CertConfig"` + ListenIP string `yaml:"ListenIP"` + SendIP string `yaml:"SendIP"` + EnableProxyProtocol bool `yaml:"EnableProxyProtocol"` + XrayOptions XrayOptions `yaml:"XrayOptions"` + HyOptions HyOptions `yaml:"HyOptions"` + LimitConfig LimitConfig `yaml:"LimitConfig"` + CertConfig *CertConfig `yaml:"CertConfig"` } type XrayOptions struct { - EnableProxyProtocol bool `yaml:"EnableProxyProtocol"` - EnableDNS bool `yaml:"EnableDNS"` - DNSType string `yaml:"DNSType"` - EnableVless bool `yaml:"EnableVless"` - EnableXtls bool `yaml:"EnableXtls"` - EnableUot bool `yaml:"EnableUot"` - EnableTFO bool `yaml:"EnableTFO"` - DisableIVCheck bool `yaml:"DisableIVCheck"` - DisableSniffing bool `yaml:"DisableSniffing"` - EnableFallback bool `yaml:"EnableFallback"` - FallBackConfigs []FallBackConfig `yaml:"FallBackConfigs"` + EnableDNS bool `yaml:"EnableDNS"` + DNSType string `yaml:"DNSType"` + EnableVless bool `yaml:"EnableVless"` + VlessFlow string `json:"VlessFlow"` + EnableUot bool `yaml:"EnableUot"` + EnableTFO bool `yaml:"EnableTFO"` + DisableIVCheck bool `yaml:"DisableIVCheck"` + DisableSniffing bool `yaml:"DisableSniffing"` + EnableFallback bool `yaml:"EnableFallback"` + FallBackConfigs []FallBackConfig `yaml:"FallBackConfigs"` } type HyOptions struct { @@ -105,12 +103,12 @@ type CertConfig struct { } type RealityConfig struct { - Dest interface{} `yaml:"Dest"` - Xver uint64 `yaml:"Xver"` - ServerNames []string `yaml:"ServerNames"` - PrivateKey string `yaml:"PrivateKey"` - MinClientVer string `yaml:"MinClientVer"` - MaxClientVer string `yaml:"MaxClientVer"` - MaxTimeDiff uint64 `yaml:"MaxTimeDiff"` - ShortIds []string `yaml:"ShortIds"` + Dest interface{} `yaml:"Dest" json:"Dest"` + Xver uint64 `yaml:"Xver" json:"Xver"` + ServerNames []string `yaml:"ServerNames" json:"ServerNames"` + PrivateKey string `yaml:"PrivateKey" json:"PrivateKey"` + MinClientVer string `yaml:"MinClientVer" json:"MinClientVer"` + MaxClientVer string `yaml:"MaxClientVer" json:"MaxClientVer"` + MaxTimeDiff uint64 `yaml:"MaxTimeDiff" json:"MaxTimeDiff"` + ShortIds []string `yaml:"ShortIds" json:"ShortIds"` } diff --git a/conf/watch.go b/conf/watch.go new file mode 100644 index 0000000..eacf0fa --- /dev/null +++ b/conf/watch.go @@ -0,0 +1,52 @@ +package conf + +import ( + "fmt" + "github.com/fsnotify/fsnotify" + "log" + "path" + "time" +) + +func (p *Conf) Watch(filePath string, reload func()) error { + watcher, err := fsnotify.NewWatcher() + if err != nil { + return fmt.Errorf("new watcher error: %s", err) + } + go func() { + var pre time.Time + defer watcher.Close() + for { + select { + case e := <-watcher.Events: + if e.Has(fsnotify.Chmod) { + continue + } + if pre.Add(10 * time.Second).After(time.Now()) { + continue + } + pre = time.Now() + go func() { + time.Sleep(10 * time.Second) + log.Println("config dir changed, reloading...") + *p = *New() + err := p.LoadFromPath(filePath) + if err != nil { + log.Printf("reload config error: %s", err) + } + reload() + log.Println("reload config success") + }() + case err := <-watcher.Errors: + if err != nil { + log.Printf("File watcher error: %s", err) + } + } + } + }() + err = watcher.Add(path.Dir(filePath)) + if err != nil { + return fmt.Errorf("watch file error: %s", err) + } + return nil +} diff --git a/core/core.go b/core/core.go index ad00877..825cac8 100644 --- a/core/core.go +++ b/core/core.go @@ -26,7 +26,7 @@ func NewCore(c *conf.CoreConfig) (Core, error) { } cs = append(cs, core1) } - return &Selecter{ + return &Selector{ cores: cs, }, nil } diff --git a/core/hy/node.go b/core/hy/node.go index a39908b..5cdf889 100644 --- a/core/hy/node.go +++ b/core/hy/node.go @@ -5,7 +5,7 @@ import ( "fmt" "github.com/Yuzuki616/V2bX/api/panel" "github.com/Yuzuki616/V2bX/conf" - "github.com/apernet/hysteria/core/cs" + "github.com/Yuzuki616/V2bX/limiter" ) func (h *Hy) AddNode(tag string, info *panel.NodeInfo, c *conf.ControllerConfig) error { @@ -16,8 +16,12 @@ func (h *Hy) AddNode(tag string, info *panel.NodeInfo, c *conf.ControllerConfig) case "reality", "none", "": return errors.New("hysteria need normal tls cert") } - s := NewServer(tag) - err := s.runServer(info, c) + l, err := limiter.GetLimiter(tag) + if err != nil { + return fmt.Errorf("get limiter error: %s", err) + } + s := NewServer(tag, l) + err = s.runServer(info, c) if err != nil { return fmt.Errorf("run hy server error: %s", err) } @@ -27,7 +31,7 @@ func (h *Hy) AddNode(tag string, info *panel.NodeInfo, c *conf.ControllerConfig) func (h *Hy) DelNode(tag string) error { if s, e := h.servers.Load(tag); e { - err := s.(*cs.Server).Close() + err := s.(*Server).Close() if err != nil { return err } diff --git a/core/hy/server.go b/core/hy/server.go index fda7f6c..6e34831 100644 --- a/core/hy/server.go +++ b/core/hy/server.go @@ -5,6 +5,7 @@ import ( "fmt" "github.com/Yuzuki616/V2bX/api/panel" "github.com/Yuzuki616/V2bX/conf" + "github.com/Yuzuki616/V2bX/limiter" "github.com/apernet/hysteria/core/sockopt" "io" "net" @@ -32,15 +33,17 @@ var serverPacketConnFuncFactoryMap = map[string]pktconns.ServerPacketConnFuncFac type Server struct { tag string + l *limiter.Limiter counter *UserTrafficCounter users sync.Map running atomic.Bool *cs.Server } -func NewServer(tag string) *Server { +func NewServer(tag string, l *limiter.Limiter) *Server { return &Server{ tag: tag, + l: l, } } @@ -173,6 +176,9 @@ func (s *Server) runServer(node *panel.NodeInfo, c *conf.ControllerConfig) error } func (s *Server) authByUser(addr net.Addr, auth []byte, sSend uint64, sRecv uint64) (bool, string) { + if _, r := s.l.CheckLimit(string(auth), addr.String(), false); r { + return false, "device limited" + } if _, ok := s.users.Load(string(auth)); ok { return true, "Done" } @@ -180,6 +186,7 @@ func (s *Server) authByUser(addr net.Addr, auth []byte, sSend uint64, sRecv uint } func (s *Server) connectFunc(addr net.Addr, auth []byte, sSend uint64, sRecv uint64) (bool, string) { + s.l.ConnLimiter.AddConnCount(addr.String(), string(auth), false) ok, msg := s.authByUser(addr, auth, sSend, sRecv) if !ok { logrus.WithFields(logrus.Fields{ @@ -196,6 +203,7 @@ func (s *Server) connectFunc(addr net.Addr, auth []byte, sSend uint64, sRecv uin } func (s *Server) disconnectFunc(addr net.Addr, auth []byte, err error) { + s.l.ConnLimiter.DelConnCount(addr.String(), string(auth)) logrus.WithFields(logrus.Fields{ "src": defaultIPMasker.Mask(addr.String()), "error": err, diff --git a/core/hy/server_test.go b/core/hy/server_test.go index 95c8f9a..db16d4f 100644 --- a/core/hy/server_test.go +++ b/core/hy/server_test.go @@ -1,17 +1,23 @@ package hy import ( + "encoding/base64" "github.com/Yuzuki616/V2bX/api/panel" "github.com/Yuzuki616/V2bX/conf" + "github.com/Yuzuki616/V2bX/limiter" "github.com/sirupsen/logrus" + "log" "testing" + "time" ) func TestServer(t *testing.T) { logrus.SetLevel(logrus.DebugLevel) - s := NewServer("test") + limiter.Init() + l := limiter.AddLimiter("test", &conf.LimitConfig{}, nil) + s := NewServer("test", l) t.Log(s.runServer(&panel.NodeInfo{ - Port: 11415, + Port: 1145, UpMbps: 100, DownMbps: 100, HyObfs: "atresssdaaaadd", @@ -24,5 +30,13 @@ func TestServer(t *testing.T) { }, })) s.users.Store("test1111", struct{}{}) + go func() { + for { + time.Sleep(10 * time.Second) + auth := base64.StdEncoding.EncodeToString([]byte("test1111")) + log.Println(auth) + log.Println(s.counter.getCounters(auth).UpCounter.Load()) + } + }() select {} } diff --git a/core/hy/user.go b/core/hy/user.go index 30cb651..a286dcc 100644 --- a/core/hy/user.go +++ b/core/hy/user.go @@ -3,6 +3,7 @@ package hy import ( "encoding/base64" "errors" + "github.com/Yuzuki616/V2bX/api/panel" "github.com/Yuzuki616/V2bX/core" ) @@ -23,22 +24,22 @@ func (h *Hy) GetUserTraffic(tag, uuid string, reset bool) (up int64, down int64) s := v.(*Server) auth := base64.StdEncoding.EncodeToString([]byte(uuid)) up = s.counter.getCounters(auth).UpCounter.Load() - down = s.counter.getCounters(uuid).DownCounter.Load() + down = s.counter.getCounters(auth).DownCounter.Load() if reset { - s.counter.Reset(uuid) + s.counter.Reset(auth) } return } -func (h *Hy) DelUsers(users []string, tag string) error { +func (h *Hy) DelUsers(users []panel.UserInfo, tag string) error { v, e := h.servers.Load(tag) if !e { return errors.New("the node is not have") } s := v.(*Server) for i := range users { - s.users.Delete(users[i]) - s.counter.Delete(users[i]) + s.users.Delete(users[i].Uuid) + s.counter.Delete(base64.StdEncoding.EncodeToString([]byte(users[i].Uuid))) } return nil } diff --git a/core/interface.go b/core/interface.go index 0af41fa..af29058 100644 --- a/core/interface.go +++ b/core/interface.go @@ -18,6 +18,6 @@ type Core interface { DelNode(tag string) error AddUsers(p *AddUsersParams) (added int, err error) GetUserTraffic(tag, uuid string, reset bool) (up int64, down int64) - DelUsers(users []string, tag string) error + DelUsers(users []panel.UserInfo, tag string) error Protocols() []string } diff --git a/core/selecter.go b/core/selector.go similarity index 78% rename from core/selecter.go rename to core/selector.go index 38127a9..997be8a 100644 --- a/core/selecter.go +++ b/core/selector.go @@ -9,12 +9,12 @@ import ( "github.com/hashicorp/go-multierror" ) -type Selecter struct { +type Selector struct { cores []Core nodes sync.Map } -func (s *Selecter) Start() error { +func (s *Selector) Start() error { for i := range s.cores { err := s.cores[i].Start() return err @@ -22,7 +22,7 @@ func (s *Selecter) Start() error { return nil } -func (s *Selecter) Close() error { +func (s *Selector) Close() error { var errs error for i := range s.cores { errs = multierror.Append(errs, s.cores[i].Close()) @@ -39,7 +39,7 @@ func isSupported(protocol string, protocols []string) bool { return false } -func (s *Selecter) AddNode(tag string, info *panel.NodeInfo, config *conf.ControllerConfig) error { +func (s *Selector) AddNode(tag string, info *panel.NodeInfo, config *conf.ControllerConfig) error { for i := range s.cores { if !isSupported(info.Type, s.cores[i].Protocols()) { continue @@ -53,7 +53,7 @@ func (s *Selecter) AddNode(tag string, info *panel.NodeInfo, config *conf.Contro return errors.New("the node type is not support") } -func (s *Selecter) DelNode(tag string) error { +func (s *Selector) DelNode(tag string) error { if t, e := s.nodes.Load(tag); e { err := s.cores[t.(int)].DelNode(tag) if err != nil { @@ -65,7 +65,7 @@ func (s *Selecter) DelNode(tag string) error { return errors.New("the node is not have") } -func (s *Selecter) AddUsers(p *AddUsersParams) (added int, err error) { +func (s *Selector) AddUsers(p *AddUsersParams) (added int, err error) { t, e := s.nodes.Load(p.Tag) if !e { return 0, errors.New("the node is not have") @@ -73,7 +73,7 @@ func (s *Selecter) AddUsers(p *AddUsersParams) (added int, err error) { return s.cores[t.(int)].AddUsers(p) } -func (s *Selecter) GetUserTraffic(tag, uuid string, reset bool) (up int64, down int64) { +func (s *Selector) GetUserTraffic(tag, uuid string, reset bool) (up int64, down int64) { t, e := s.nodes.Load(tag) if !e { return 0, 0 @@ -81,7 +81,7 @@ func (s *Selecter) GetUserTraffic(tag, uuid string, reset bool) (up int64, down return s.cores[t.(int)].GetUserTraffic(tag, uuid, reset) } -func (s *Selecter) DelUsers(users []string, tag string) error { +func (s *Selector) DelUsers(users []panel.UserInfo, tag string) error { t, e := s.nodes.Load(tag) if !e { return errors.New("the node is not have") @@ -89,7 +89,7 @@ func (s *Selecter) DelUsers(users []string, tag string) error { return s.cores[t.(int)].DelUsers(users, tag) } -func (s *Selecter) Protocols() []string { +func (s *Selector) Protocols() []string { protocols := make([]string, 0) for i := range s.cores { protocols = append(protocols, s.cores[i].Protocols()...) diff --git a/core/xray/distro/all/all.go b/core/xray/distro/all/all.go index 1a5f307..18057ee 100644 --- a/core/xray/distro/all/all.go +++ b/core/xray/distro/all/all.go @@ -21,7 +21,7 @@ import ( _ "github.com/xtls/xray-core/app/dns" _ "github.com/xtls/xray-core/app/dns/fakedns" _ "github.com/xtls/xray-core/app/log" - _ "github.com/xtls/xray-core/app/metrics" + //_ "github.com/xtls/xray-core/app/metrics" _ "github.com/xtls/xray-core/app/policy" _ "github.com/xtls/xray-core/app/reverse" _ "github.com/xtls/xray-core/app/router" diff --git a/core/xray/user.go b/core/xray/user.go index 43f2c63..b2a51e4 100644 --- a/core/xray/user.go +++ b/core/xray/user.go @@ -3,7 +3,7 @@ package xray import ( "context" "fmt" - + "github.com/Yuzuki616/V2bX/api/panel" "github.com/Yuzuki616/V2bX/common/builder" vCore "github.com/Yuzuki616/V2bX/core" "github.com/xtls/xray-core/common/protocol" @@ -26,16 +26,22 @@ func (c *Core) GetUserManager(tag string) (proxy.UserManager, error) { return userManager, nil } -func (c *Core) DelUsers(users []string, tag string) error { +func (c *Core) DelUsers(users []panel.UserInfo, tag string) error { userManager, err := c.GetUserManager(tag) if err != nil { return fmt.Errorf("get user manager error: %s", err) } - for _, email := range users { - err = userManager.RemoveUser(context.Background(), email) + var up, down, user string + for i := range users { + user = builder.BuildUserTag(tag, users[i].Uuid) + err = userManager.RemoveUser(context.Background(), user) if err != nil { return err } + up = "user>>>" + user + ">>>traffic>>>uplink" + down = "user>>>" + user + ">>>traffic>>>downlink" + c.shm.UnregisterCounter(up) + c.shm.UnregisterCounter(down) } return nil } @@ -67,11 +73,22 @@ func (c *Core) AddUsers(p *vCore.AddUsersParams) (added int, err error) { users := make([]*protocol.User, 0, len(p.UserInfo)) switch p.NodeInfo.Type { case "v2ray": - if p.Config.XrayOptions.EnableXtls { - users = builder.BuildVlessUsers(p.Tag, p.UserInfo, true) + if p.Config.XrayOptions.EnableVless || + p.NodeInfo.ExtraConfig.EnableVless { + if p.Config.XrayOptions.VlessFlow != "" { + if p.Config.XrayOptions.VlessFlow == p.NodeInfo.ExtraConfig.VlessFlow { + users = builder.BuildVlessUsers(p.Tag, p.UserInfo, p.Config.XrayOptions.VlessFlow) + } else { + users = builder.BuildVlessUsers(p.Tag, p.UserInfo, p.NodeInfo.ExtraConfig.VlessFlow) + } + + } else { + users = builder.BuildVlessUsers(p.Tag, p.UserInfo, p.NodeInfo.ExtraConfig.VlessFlow) + } } else { users = builder.BuildVmessUsers(p.Tag, p.UserInfo) } + case "trojan": users = builder.BuildTrojanUsers(p.Tag, p.UserInfo) case "shadowsocks": diff --git a/core/xray/xray.go b/core/xray/xray.go index 280eb10..2520e46 100644 --- a/core/xray/xray.go +++ b/core/xray/xray.go @@ -1,7 +1,6 @@ package xray import ( - "log" "os" "sync" @@ -10,6 +9,7 @@ import ( "github.com/Yuzuki616/V2bX/core/xray/app/dispatcher" _ "github.com/Yuzuki616/V2bX/core/xray/distro/all" "github.com/goccy/go-json" + log "github.com/sirupsen/logrus" "github.com/xtls/xray-core/app/proxyman" "github.com/xtls/xray-core/app/stats" "github.com/xtls/xray-core/common/serial" @@ -63,40 +63,40 @@ func getCore(c *conf.XrayConfig) *core.Instance { coreDnsConfig := &coreConf.DNSConfig{} if c.DnsConfigPath != "" { if f, err := os.Open(c.DnsConfigPath); err != nil { - log.Panicf("Failed to read DNS config file at: %s", c.DnsConfigPath) + log.WithField("err", err).Panic("Failed to read DNS config file") } else { if err = json.NewDecoder(f).Decode(coreDnsConfig); err != nil { - log.Panicf("Failed to unmarshal DNS config: %s", c.DnsConfigPath) + log.WithField("err", err).Panic("Failed to unmarshal DNS config") } } } dnsConfig, err := coreDnsConfig.Build() if err != nil { - log.Panicf("Failed to understand DNS config, Please check: https://xtls.github.io/config/dns.html for help: %s", err) + log.WithField("err", err).Panic("Failed to understand DNS config, Please check: https://xtls.github.io/config/dns.html for help") } // Routing config coreRouterConfig := &coreConf.RouterConfig{} if c.RouteConfigPath != "" { if f, err := os.Open(c.RouteConfigPath); err != nil { - log.Panicf("Failed to read Routing config file at: %s", c.RouteConfigPath) + log.WithField("err", err).Panic("Failed to read Routing config file") } else { if err = json.NewDecoder(f).Decode(coreRouterConfig); err != nil { - log.Panicf("Failed to unmarshal Routing config: %s", c.RouteConfigPath) + log.WithField("err", err).Panic("Failed to unmarshal Routing config") } } } routeConfig, err := coreRouterConfig.Build() if err != nil { - log.Panicf("Failed to understand Routing config Please check: https://xtls.github.io/config/routing.html for help: %s", err) + log.WithField("err", err).Panic("Failed to understand Routing config Please check: https://xtls.github.io/config/routing.html") } // Custom Inbound config var coreCustomInboundConfig []coreConf.InboundDetourConfig if c.InboundConfigPath != "" { if f, err := os.Open(c.InboundConfigPath); err != nil { - log.Panicf("Failed to read Custom Inbound config file at: %s", c.OutboundConfigPath) + log.WithField("err", err).Panic("Failed to read Custom Inbound config file") } else { if err = json.NewDecoder(f).Decode(&coreCustomInboundConfig); err != nil { - log.Panicf("Failed to unmarshal Custom Inbound config: %s", c.OutboundConfigPath) + log.WithField("err", err).Panic("Failed to unmarshal Custom Inbound config") } } } @@ -104,7 +104,7 @@ func getCore(c *conf.XrayConfig) *core.Instance { for _, config := range coreCustomInboundConfig { oc, err := config.Build() if err != nil { - log.Panicf("Failed to understand Inbound config, Please check: https://xtls.github.io/config/inbound.html for help: %s", err) + log.WithField("err", err).Panic("Failed to understand Inbound config, Please check: https://xtls.github.io/config/inbound.html for help") } inBoundConfig = append(inBoundConfig, oc) } @@ -112,10 +112,10 @@ func getCore(c *conf.XrayConfig) *core.Instance { var coreCustomOutboundConfig []coreConf.OutboundDetourConfig if c.OutboundConfigPath != "" { if f, err := os.Open(c.OutboundConfigPath); err != nil { - log.Panicf("Failed to read Custom Outbound config file at: %s", c.OutboundConfigPath) + log.WithField("err", err).Panic("Failed to read Custom Outbound config file") } else { if err = json.NewDecoder(f).Decode(&coreCustomOutboundConfig); err != nil { - log.Panicf("Failed to unmarshal Custom Outbound config: %s", c.OutboundConfigPath) + log.WithField("err", err).Panic("Failed to unmarshal Custom Outbound config") } } } @@ -123,7 +123,7 @@ func getCore(c *conf.XrayConfig) *core.Instance { for _, config := range coreCustomOutboundConfig { oc, err := config.Build() if err != nil { - log.Panicf("Failed to understand Outbound config, Please check: https://xtls.github.io/config/outbound.html for help: %s", err) + log.WithField("err", err).Panic("Failed to understand Outbound config, Please check: https://xtls.github.io/config/outbound.html for help") } outBoundConfig = append(outBoundConfig, oc) } @@ -149,10 +149,9 @@ func getCore(c *conf.XrayConfig) *core.Instance { } server, err := core.New(config) if err != nil { - log.Panicf("failed to create instance: %s", err) + log.WithField("err", err).Panic("failed to create instance") } - log.Printf("Core Version: %s", core.Version()) - + log.Info("Xray Core Version: ", core.Version()) return server } diff --git a/example/config.yml.example b/example/config.yml.example index b27f79e..697ca57 100644 --- a/example/config.yml.example +++ b/example/config.yml.example @@ -1,5 +1,5 @@ CoreConfig: - Type: "xray" # Core type. if you need many cores, use " " to split + Type: "xray" # Core type, default support "xray" and "hy". If you need many cores, use " " to split XrayConfig: Log: Level: warning # Log level: none, error, warning, info, debug @@ -31,7 +31,7 @@ Nodes: DNSType: AsIs # AsIs, UseIP, UseIPv4, UseIPv6, DNS strategy EnableTFO: false # Enable TCP Fast Open EnableVless: false # Enable Vless for V2ray Type - EnableXtls: false # Enable xtls-rprx-vision, only vless + VlessFlow: "xtls-rprx-vision" # flow for vless, "xtls-rprx-vision" or "none" or "" EnableProxyProtocol: false # Only works for WebSocket and TCP EnableFallback: false # Only support for Trojan and Vless FallBackConfigs: # Support multiple fallbacks @@ -39,7 +39,7 @@ Nodes: Alpn: # Alpn, Empty for any Path: # HTTP PATH, Empty for any Dest: 80 # Required, Destination of fallback, check https://xtls.github.io/config/features/fallback.html for details. - ProxyProtocolVer: 0 # Send PROXY protocol version, 0 for dsable + ProxyProtocolVer: 0 # Send PROXY protocol version, 0 for disable HyOptions: Resolver: "udp://1.1.1.1:53" # DNS resolver address ResolvePreference: 64 # DNS IPv4/IPv6 preference. Available options: "64" (IPv6 first, fallback to IPv4), "46" (IPv4 first, fallback to IPv6), "6" (IPv6 only), "4" (IPv4 only) diff --git a/go.mod b/go.mod index c945c7c..d663de9 100644 --- a/go.mod +++ b/go.mod @@ -3,23 +3,23 @@ module github.com/Yuzuki616/V2bX go 1.20 require ( - github.com/apernet/hysteria/core v1.3.4 - github.com/beevik/ntp v1.0.0 + github.com/apernet/hysteria/core v1.3.5 + github.com/beevik/ntp v1.1.1 github.com/folbricht/routedns v0.1.21-0.20230220022436-4ae86ce30d53 github.com/fsnotify/fsnotify v1.6.0 - github.com/go-acme/lego/v4 v4.12.1 + github.com/go-acme/lego/v4 v4.12.3 github.com/go-redis/redis/v8 v8.11.5 github.com/go-resty/resty/v2 v2.7.0 github.com/goccy/go-json v0.10.2 github.com/hashicorp/go-multierror v1.1.1 github.com/juju/ratelimit v1.0.2 - github.com/oschwald/geoip2-golang v1.8.0 - github.com/quic-go/quic-go v0.33.0 + github.com/oschwald/geoip2-golang v1.9.0 + github.com/quic-go/quic-go v0.34.0 github.com/sagernet/sing-box v1.2.7 github.com/sirupsen/logrus v1.9.3 github.com/spf13/cobra v1.7.0 github.com/xtls/xray-core v1.8.1 - golang.org/x/crypto v0.9.0 + golang.org/x/crypto v0.10.0 google.golang.org/protobuf v1.30.0 gopkg.in/yaml.v3 v3.0.1 ) @@ -121,7 +121,7 @@ require ( github.com/nrdcg/porkbun v0.1.1 // indirect github.com/onsi/ginkgo/v2 v2.9.2 // indirect github.com/oracle/oci-go-sdk v24.3.0+incompatible // indirect - github.com/oschwald/maxminddb-golang v1.10.0 // indirect + github.com/oschwald/maxminddb-golang v1.11.0 // indirect github.com/ovh/go-ovh v1.1.0 // indirect github.com/patrickmn/go-cache v2.1.0+incompatible // indirect github.com/pion/dtls/v2 v2.2.4 // indirect @@ -158,7 +158,7 @@ require ( github.com/spf13/cast v1.5.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/stretchr/objx v0.5.0 // indirect - github.com/stretchr/testify v1.8.3 // indirect + github.com/stretchr/testify v1.8.4 // indirect github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.490 // indirect github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.490 // indirect github.com/transip/gotransip/v6 v6.17.0 // indirect @@ -181,8 +181,8 @@ require ( golang.org/x/mod v0.10.0 // indirect golang.org/x/net v0.10.0 // indirect golang.org/x/oauth2 v0.6.0 // indirect - golang.org/x/sys v0.8.0 // indirect - golang.org/x/text v0.9.0 // indirect + golang.org/x/sys v0.9.0 // indirect + golang.org/x/text v0.10.0 // indirect golang.org/x/time v0.3.0 // indirect golang.org/x/tools v0.8.0 // indirect google.golang.org/api v0.114.0 // indirect diff --git a/go.sum b/go.sum index 91a8636..49d4698 100644 --- a/go.sum +++ b/go.sum @@ -67,8 +67,8 @@ github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/ github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -github.com/apernet/hysteria/core v1.3.4 h1:i5EqGIf7FZqwsOrT7PagLQeh+P5gHfLkDZWn7/pXUjo= -github.com/apernet/hysteria/core v1.3.4/go.mod h1:WxFihvoDBhatefn4w8Lipzy97CWjK6NF1/duT0kGUG0= +github.com/apernet/hysteria/core v1.3.5 h1:Pg7w7rcdXhkGUJoG3ALpT9waz+ZmWpIpD5QqJe8MbuI= +github.com/apernet/hysteria/core v1.3.5/go.mod h1:7jmeIvXeTaRDuXvK6suvxYq65E4erdi3nEkKy9Qusj4= github.com/apernet/quic-go v0.34.1-0.20230507231629-ec008b7e8473 h1:3KFetJ/lUFn0m9xTFg+rMmz2nyHg+D2boJX0Rp4OF6c= github.com/apernet/quic-go v0.34.1-0.20230507231629-ec008b7e8473/go.mod h1:+4CVgVppm0FNjpG3UcX8Joi/frKOH7/ciD5yGcwOO1g= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= @@ -76,8 +76,8 @@ github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmV github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/aws/aws-sdk-go v1.39.0 h1:74BBwkEmiqBbi2CGflEh34l0YNtIibTjZsibGarkNjo= github.com/aws/aws-sdk-go v1.39.0/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= -github.com/beevik/ntp v1.0.0 h1:d0Lgy1xbNNqVyGfvg2Z96ItKcfyn3lzgus/oRoj9vnk= -github.com/beevik/ntp v1.0.0/go.mod h1:JN7/74B0Z4GUGO/1aUeRI2adARlfJGUeaJb0y0Wvnf4= +github.com/beevik/ntp v1.1.1 h1:ah1SrQMwiyL8eRpb7+Erm7l4PvXnZ/Dnh/gjGZXPvp0= +github.com/beevik/ntp v1.1.1/go.mod h1:br1ZC24XiJQWuIUdrmQT3Bf5jQXIOLaPT+MoAK9Vlk0= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= @@ -176,8 +176,8 @@ github.com/ghodss/yaml v1.0.1-0.20220118164431-d8423dcdf344/go.mod h1:GIjDIg/heH github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.7.4/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY= github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= -github.com/go-acme/lego/v4 v4.12.1 h1:Cy3FS7wADLNBqCLpz2wdfdNrThW9rZy8RCAfnUrL2uE= -github.com/go-acme/lego/v4 v4.12.1/go.mod h1:UZoOlhVmUYP/N0z4tEbfUjoCNHRZNObzqWZtT76DIsc= +github.com/go-acme/lego/v4 v4.12.3 h1:aWPYhBopAZXWBASPgvi1LnWGrr5YiXOsrpVaFaVJipo= +github.com/go-acme/lego/v4 v4.12.3/go.mod h1:UZoOlhVmUYP/N0z4tEbfUjoCNHRZNObzqWZtT76DIsc= github.com/go-chi/chi/v5 v5.0.0/go.mod h1:BBug9lr0cqtdAhsu6R4AAdvufI0/XBzAQSsUqJpoZOs= github.com/go-cmd/cmd v1.0.5/go.mod h1:y8q8qlK5wQibcw63djSl/ntiHUHXHGdCkPk0j4QeW4s= github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w= @@ -519,10 +519,10 @@ github.com/onsi/gomega v1.27.4 h1:Z2AnStgsdSayCMDiCU42qIz+HLqEPcgiOCXjAU/w+8E= github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= github.com/oracle/oci-go-sdk v24.3.0+incompatible h1:x4mcfb4agelf1O4/1/auGlZ1lr97jXRSSN5MxTgG/zU= github.com/oracle/oci-go-sdk v24.3.0+incompatible/go.mod h1:VQb79nF8Z2cwLkLS35ukwStZIg5F66tcBccjip/j888= -github.com/oschwald/geoip2-golang v1.8.0 h1:KfjYB8ojCEn/QLqsDU0AzrJ3R5Qa9vFlx3z6SLNcKTs= -github.com/oschwald/geoip2-golang v1.8.0/go.mod h1:R7bRvYjOeaoenAp9sKRS8GX5bJWcZ0laWO5+DauEktw= -github.com/oschwald/maxminddb-golang v1.10.0 h1:Xp1u0ZhqkSuopaKmk1WwHtjF0H9Hd9181uj2MQ5Vndg= -github.com/oschwald/maxminddb-golang v1.10.0/go.mod h1:Y2ELenReaLAZ0b400URyGwvYxHV1dLIxBuyOsyYjHK0= +github.com/oschwald/geoip2-golang v1.9.0 h1:uvD3O6fXAXs+usU+UGExshpdP13GAqp4GBrzN7IgKZc= +github.com/oschwald/geoip2-golang v1.9.0/go.mod h1:BHK6TvDyATVQhKNbQBdrj9eAvuwOMi2zSFXizL3K81Y= +github.com/oschwald/maxminddb-golang v1.11.0 h1:aSXMqYR/EPNjGE8epgqwDay+P30hCBZIveY0WZbAWh0= +github.com/oschwald/maxminddb-golang v1.11.0/go.mod h1:YmVI+H0zh3ySFR3w+oz8PCfglAFj3PuCmui13+P9zDg= github.com/ovh/go-ovh v1.1.0 h1:bHXZmw8nTgZin4Nv7JuaLs0KG5x54EQR7migYTd1zrk= github.com/ovh/go-ovh v1.1.0/go.mod h1:AxitLZ5HBRPyUd+Zl60Ajaag+rNTdVXWIkzfrVuTXWA= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= @@ -697,9 +697,8 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= -github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.490 h1:mmz27tVi2r70JYnm5y0Zk8w0Qzsx+vfUw3oqSyrEfP8= @@ -788,8 +787,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20211202192323-5770296d904e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU= -golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g= -golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= +golang.org/x/crypto v0.10.0 h1:LKqV2xt9+kDzSTfOhx4FrkEBcMrAgHSYgzywV9zcGmM= +golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -857,7 +856,6 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -937,9 +935,9 @@ golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s= +golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -947,8 +945,8 @@ golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuX golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= -golang.org/x/term v0.8.0 h1:n5xxQn2i3PC0yLAbjTpNT85q/Kgzcr2gIoX9OrJUols= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.9.0 h1:GRRCnKYhdQrD8kfRAdQ6Zcw1P0OcELxGLKJvtjVMZ28= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= @@ -960,8 +958,9 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.10.0 h1:UpjohKhiEgNc0CSauXmwYftY1+LlaC75SJwh0SgCX58= +golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= diff --git a/limiter/clear.go b/limiter/clear.go index 47db76f..a926854 100644 --- a/limiter/clear.go +++ b/limiter/clear.go @@ -1,14 +1,16 @@ package limiter -import "log" +import log "github.com/sirupsen/logrus" func ClearOnlineIP() error { - log.Println("Limiter: Clear online ip...") + log.WithField("Type", "Limiter"). + Debug("Clear online ip...") limitLock.RLock() for _, l := range limiter { l.ConnLimiter.ClearOnlineIP() } limitLock.RUnlock() - log.Println("Limiter: Clear online ip done") + log.WithField("Type", "Limiter"). + Debug("Clear online ip done") return nil } diff --git a/limiter/dynamic.go b/limiter/dynamic.go index 7e7d8c5..a74599e 100644 --- a/limiter/dynamic.go +++ b/limiter/dynamic.go @@ -1,8 +1,8 @@ package limiter import ( - "fmt" "github.com/Yuzuki616/V2bX/api/panel" + "github.com/Yuzuki616/V2bX/common/builder" "time" ) @@ -11,7 +11,7 @@ func (l *Limiter) AddDynamicSpeedLimit(tag string, userInfo *panel.UserInfo, lim DynamicSpeedLimit: limitNum, ExpireTime: time.Now().Add(time.Duration(expire) * time.Second).Unix(), } - l.UserLimitInfo.Store(fmt.Sprintf("%s|%s|%d", tag, userInfo.Uuid, userInfo.Id), userLimit) + l.UserLimitInfo.Store(builder.BuildUserTag(tag, userInfo.Uuid), userLimit) return nil } diff --git a/limiter/limiter.go b/limiter/limiter.go index ff45148..45c4583 100644 --- a/limiter/limiter.go +++ b/limiter/limiter.go @@ -7,8 +7,8 @@ import ( "github.com/Yuzuki616/V2bX/common/builder" "github.com/Yuzuki616/V2bX/conf" "github.com/juju/ratelimit" + log "github.com/sirupsen/logrus" "github.com/xtls/xray-core/common/task" - "log" "regexp" "sync" "time" @@ -24,7 +24,8 @@ func Init() { Execute: ClearOnlineIP, } go func() { - log.Println("Limiter: ClearOnlineIP started") + log.WithField("Type", "Limiter"). + Debug("ClearOnlineIP started") time.Sleep(time.Minute * 2) _ = c.Start() }() @@ -85,10 +86,7 @@ func UpdateLimiter(tag string, added []panel.UserInfo, deleted []panel.UserInfo) return fmt.Errorf("get limit error: %s", err) } for i := range deleted { - l.UserLimitInfo.Delete(fmt.Sprintf("%s|%s|%d", - tag, - deleted[i].Uuid, - deleted[i].Id)) + l.UserLimitInfo.Delete(builder.BuildUserTag(tag, deleted[i].Uuid)) } for i := range added { if added[i].SpeedLimit != 0 { @@ -97,10 +95,7 @@ func UpdateLimiter(tag string, added []panel.UserInfo, deleted []panel.UserInfo) SpeedLimit: added[i].SpeedLimit, ExpireTime: 0, } - l.UserLimitInfo.Store(fmt.Sprintf("%s|%s|%d", - tag, - added[i].Uuid, - added[i].Id), userLimit) + l.UserLimitInfo.Store(builder.BuildUserTag(tag, added[i].Uuid), userLimit) } } return nil diff --git a/node/cert.go b/node/cert.go index d80857c..500c631 100644 --- a/node/cert.go +++ b/node/cert.go @@ -4,8 +4,22 @@ import ( "fmt" "github.com/Yuzuki616/V2bX/common/file" "github.com/Yuzuki616/V2bX/node/lego" + "log" ) +func (c *Controller) renewCertTask() { + l, err := lego.New(c.CertConfig) + if err != nil { + log.Print("new lego error: ", err) + return + } + err = l.RenewCert() + if err != nil { + log.Print("renew cert error: ", err) + return + } +} + func (c *Controller) requestCert() error { if c.CertConfig.CertFile == "" || c.CertConfig.KeyFile == "" { return fmt.Errorf("cert file path or key file path not exist") diff --git a/node/controller.go b/node/controller.go index d9c1bb1..725b2ad 100644 --- a/node/controller.go +++ b/node/controller.go @@ -3,14 +3,13 @@ package node import ( "errors" "fmt" - "log" - "github.com/Yuzuki616/V2bX/api/iprecoder" "github.com/Yuzuki616/V2bX/api/panel" + "github.com/Yuzuki616/V2bX/common/task" "github.com/Yuzuki616/V2bX/conf" vCore "github.com/Yuzuki616/V2bX/core" "github.com/Yuzuki616/V2bX/limiter" - "github.com/xtls/xray-core/common/task" + log "github.com/sirupsen/logrus" ) type Controller struct { @@ -20,11 +19,11 @@ type Controller struct { Tag string userList []panel.UserInfo ipRecorder iprecoder.IpRecorder - nodeInfoMonitorPeriodic *task.Periodic - userReportPeriodic *task.Periodic - renewCertPeriodic *task.Periodic - dynamicSpeedLimitPeriodic *task.Periodic - onlineIpReportPeriodic *task.Periodic + nodeInfoMonitorPeriodic *task.Task + userReportPeriodic *task.Task + renewCertPeriodic *task.Task + dynamicSpeedLimitPeriodic *task.Task + onlineIpReportPeriodic *task.Task *conf.ControllerConfig } @@ -59,12 +58,10 @@ func (c *Controller) Start() error { // add limiter l := limiter.AddLimiter(c.Tag, &c.LimitConfig, c.userList) // add rule limiter - if !c.DisableGetRule { - if err = l.UpdateRule(c.nodeInfo.Rules); err != nil { - return fmt.Errorf("update rule error: %s", err) - } + if err = l.UpdateRule(c.nodeInfo.Rules); err != nil { + return fmt.Errorf("update rule error: %s", err) } - if c.nodeInfo.Tls { + if c.nodeInfo.Tls || c.nodeInfo.Type == "hysteria" { err = c.requestCert() if err != nil { return fmt.Errorf("request cert error: %s", err) @@ -84,7 +81,7 @@ func (c *Controller) Start() error { if err != nil { return fmt.Errorf("add users error: %s", err) } - log.Printf("[%s: %d] Added %d new users", c.nodeInfo.Type, c.nodeInfo.Id, added) + log.WithField("tag", c.Tag).Infof("Added %d new users", added) c.initTask() return nil } @@ -93,38 +90,23 @@ func (c *Controller) Start() error { func (c *Controller) Close() error { limiter.DeleteLimiter(c.Tag) if c.nodeInfoMonitorPeriodic != nil { - err := c.nodeInfoMonitorPeriodic.Close() - if err != nil { - return fmt.Errorf("node info periodic close error: %s", err) - } + c.nodeInfoMonitorPeriodic.Close() } - if c.nodeInfoMonitorPeriodic != nil { - err := c.userReportPeriodic.Close() - if err != nil { - return fmt.Errorf("user report periodic close error: %s", err) - } + if c.userReportPeriodic != nil { + c.userReportPeriodic.Close() } if c.renewCertPeriodic != nil { - err := c.renewCertPeriodic.Close() - if err != nil { - return fmt.Errorf("renew cert periodic close error: %s", err) - } + c.renewCertPeriodic.Close() } if c.dynamicSpeedLimitPeriodic != nil { - err := c.dynamicSpeedLimitPeriodic.Close() - if err != nil { - return fmt.Errorf("dynamic speed limit periodic close error: %s", err) - } + c.dynamicSpeedLimitPeriodic.Close() } if c.onlineIpReportPeriodic != nil { - err := c.onlineIpReportPeriodic.Close() - if err != nil { - return fmt.Errorf("online ip report periodic close error: %s", err) - } + c.onlineIpReportPeriodic.Close() } return nil } func (c *Controller) buildNodeTag() string { - return fmt.Sprintf("%s_%s_%d", c.nodeInfo.Type, c.ListenIP, c.nodeInfo.Id) + return fmt.Sprintf("%s-%s-%d", c.apiClient.APIHost, c.nodeInfo.Type, c.nodeInfo.Id) } diff --git a/node/lego/lego.go b/node/lego/lego.go index 374acd4..4a29c9c 100644 --- a/node/lego/lego.go +++ b/node/lego/lego.go @@ -16,7 +16,8 @@ type Lego struct { } func New(config *conf.CertConfig) (*Lego, error) { - user, err := NewUser(path.Join("/etc/V2bX/cert/user/", + user, err := NewUser(path.Join(path.Dir(config.CertFile), + "user", fmt.Sprintf("user-%s.json", config.Email)), config.Email) if err != nil { diff --git a/node/task.go b/node/task.go index b029ecc..d87b380 100644 --- a/node/task.go +++ b/node/task.go @@ -1,101 +1,100 @@ package node import ( - "fmt" - "log" - "runtime" - "time" - + "github.com/Yuzuki616/V2bX/common/task" vCore "github.com/Yuzuki616/V2bX/core" - - "github.com/Yuzuki616/V2bX/api/panel" "github.com/Yuzuki616/V2bX/limiter" - "github.com/Yuzuki616/V2bX/node/lego" - "github.com/xtls/xray-core/common/task" + log "github.com/sirupsen/logrus" + "time" ) func (c *Controller) initTask() { // fetch node info task - c.nodeInfoMonitorPeriodic = &task.Periodic{ + c.nodeInfoMonitorPeriodic = &task.Task{ Interval: c.nodeInfo.PullInterval, Execute: c.nodeInfoMonitor, } // fetch user list task - c.userReportPeriodic = &task.Periodic{ + c.userReportPeriodic = &task.Task{ Interval: c.nodeInfo.PushInterval, - Execute: c.reportUserTraffic, + Execute: c.reportUserTrafficTask, } - log.Printf("[%s: %d] Start monitor node status", c.nodeInfo.Type, c.nodeInfo.Id) + log.WithField("tag", c.Tag).Info("Start monitor node status") // delay to start nodeInfoMonitor - go func() { - time.Sleep(c.nodeInfo.PullInterval) - _ = c.nodeInfoMonitorPeriodic.Start() - }() - log.Printf("[%s: %d] Start report node status", c.nodeInfo.Type, c.nodeInfo.Id) - // delay to start userReport - go func() { - time.Sleep(c.nodeInfo.PullInterval) - _ = c.userReportPeriodic.Start() - }() - if c.nodeInfo.Tls && c.CertConfig.CertMode != "none" && - (c.CertConfig.CertMode == "dns" || c.CertConfig.CertMode == "http") { - c.renewCertPeriodic = &task.Periodic{ - Interval: time.Hour * 24, - Execute: c.reportUserTraffic, + _ = c.nodeInfoMonitorPeriodic.Start(false) + log.WithField("tag", c.Tag).Info("Start report node status") + _ = c.userReportPeriodic.Start(false) + if c.nodeInfo.Tls { + switch c.CertConfig.CertMode { + case "reality", "none", "": + default: + c.renewCertPeriodic = &task.Task{ + Interval: time.Hour * 24, + Execute: c.reportUserTrafficTask, + } + log.WithField("tag", c.Tag).Info("Start renew cert") + // delay to start renewCert + _ = c.renewCertPeriodic.Start(true) } - log.Printf("[%s: %d] Start renew cert", c.nodeInfo.Type, c.nodeInfo.Id) - // delay to start renewCert - go func() { - _ = c.renewCertPeriodic.Start() - }() } } func (c *Controller) nodeInfoMonitor() (err error) { - // First fetch Node Info + // get node info newNodeInfo, err := c.apiClient.GetNodeInfo() if err != nil { - log.Print(err) + log.WithFields(log.Fields{ + "tag": c.Tag, + "err": err, + }).Error("Get node info failed") + return nil + } + // get user info + newUserInfo, err := c.apiClient.GetUserList() + if err != nil { + log.WithFields(log.Fields{ + "tag": c.Tag, + "err": err, + }).Error("Get user list failed") return nil } - var nodeInfoChanged = false - // If nodeInfo changed if newNodeInfo != nil { + // nodeInfo changed // Remove old tag - oldTag := c.Tag - err := c.server.DelNode(oldTag) + log.WithField("tag", c.Tag).Info("Node changed, reload") + err = c.server.DelNode(c.Tag) if err != nil { - log.Print(err) + log.WithFields(log.Fields{ + "tag": c.Tag, + "err": err, + }).Error("Delete node failed") return nil } // Remove Old limiter - limiter.DeleteLimiter(oldTag) - // Add new tag - c.nodeInfo = newNodeInfo + limiter.DeleteLimiter(c.Tag) + // Add new Limiter c.Tag = c.buildNodeTag() - err = c.server.AddNode(c.Tag, newNodeInfo, c.ControllerConfig) - if err != nil { - log.Print(err) - return nil - } - if newNodeInfo.Tls { + l := limiter.AddLimiter(c.Tag, &c.LimitConfig, newUserInfo) + // check cert + if newNodeInfo.Tls || newNodeInfo.Type == "hysteria" { err = c.requestCert() if err != nil { - return fmt.Errorf("request cert error: %s", err) + log.WithFields(log.Fields{ + "tag": c.Tag, + "err": err, + }).Error("Request cert failed") + return nil } } - nodeInfoChanged = true - } - // Update User - newUserInfo, err := c.apiClient.GetUserList() - if err != nil { - log.Print(err) - return nil - } - if nodeInfoChanged { - c.userList = newUserInfo - // Add new Limiter - l := limiter.AddLimiter(c.Tag, &c.LimitConfig, newUserInfo) + // add new node + err = c.server.AddNode(c.Tag, newNodeInfo, c.ControllerConfig) + if err != nil { + log.WithFields(log.Fields{ + "tag": c.Tag, + "err": err, + }).Error("Add node failed") + return nil + } _, err = c.server.AddUsers(&vCore.AddUsersParams{ Tag: c.Tag, Config: c.ControllerConfig, @@ -103,109 +102,82 @@ func (c *Controller) nodeInfoMonitor() (err error) { NodeInfo: newNodeInfo, }) if err != nil { - log.Print(err) + log.WithFields(log.Fields{ + "tag": c.Tag, + "err": err, + }).Error("Add users failed") return nil } err = l.UpdateRule(newNodeInfo.Rules) if err != nil { - log.Printf("Update Rule error: %s", err) + log.WithFields(log.Fields{ + "tag": c.Tag, + "err": err, + }).Error("Update Rule failed") + return nil } // Check interval if c.nodeInfoMonitorPeriodic.Interval != newNodeInfo.PullInterval && newNodeInfo.PullInterval != 0 { c.nodeInfoMonitorPeriodic.Interval = newNodeInfo.PullInterval - _ = c.nodeInfoMonitorPeriodic.Close() - go func() { - time.Sleep(c.nodeInfoMonitorPeriodic.Interval) - _ = c.nodeInfoMonitorPeriodic.Start() - }() + c.nodeInfoMonitorPeriodic.Close() + _ = c.nodeInfoMonitorPeriodic.Start(false) } if c.userReportPeriodic.Interval != newNodeInfo.PushInterval && newNodeInfo.PushInterval != 0 { c.userReportPeriodic.Interval = newNodeInfo.PullInterval - _ = c.userReportPeriodic.Close() - go func() { - time.Sleep(c.userReportPeriodic.Interval) - _ = c.userReportPeriodic.Start() - }() + c.userReportPeriodic.Close() + _ = c.userReportPeriodic.Start(false) } - } else { - deleted, added := compareUserList(c.userList, newUserInfo) - if len(deleted) > 0 { - deletedEmail := make([]string, len(deleted)) - for i := range deleted { - deletedEmail[i] = fmt.Sprintf("%s|%s|%d", - c.Tag, - (deleted)[i].Uuid, - (deleted)[i].Id) - } - err := c.server.DelUsers(deletedEmail, c.Tag) - if err != nil { - log.Print(err) - } - } - if len(added) > 0 { - _, err := c.server.AddUsers(&vCore.AddUsersParams{ - Tag: c.Tag, - Config: c.ControllerConfig, - UserInfo: added, - NodeInfo: c.nodeInfo, - }) - if err != nil { - log.Print(err) - } - } - if len(added) > 0 || len(deleted) > 0 { - // Update Limiter - err = limiter.UpdateLimiter(c.Tag, added, deleted) - if err != nil { - log.Print("update limiter:", err) - } - } - log.Printf("[%s: %d] %d user deleted, %d user added", c.nodeInfo.Type, c.nodeInfo.Id, - len(deleted), len(added)) + c.nodeInfo = newNodeInfo c.userList = newUserInfo + log.WithField("tag", c.Tag).Infof("Added %d new users", len(newUserInfo)) + // exit + return nil } - return nil -} -func (c *Controller) reportUserTraffic() (err error) { - // Get User traffic - userTraffic := make([]panel.UserTraffic, 0) - for i := range c.userList { - up, down := c.server.GetUserTraffic(c.Tag, c.userList[i].Uuid, true) - if up > 0 || down > 0 { - if c.LimitConfig.EnableDynamicSpeedLimit { - c.userList[i].Traffic += up + down - } - userTraffic = append(userTraffic, panel.UserTraffic{ - UID: (c.userList)[i].Id, - Upload: up, - Download: down}) - } - } - if len(userTraffic) > 0 && !c.DisableUploadTraffic { - err = c.apiClient.ReportUserTraffic(userTraffic) + // node no changed, check users + deleted, added := compareUserList(c.userList, newUserInfo) + if len(deleted) > 0 { + // have deleted users + err = c.server.DelUsers(deleted, c.Tag) if err != nil { - log.Printf("Report user traffic faild: %s", err) - } else { - log.Printf("[%s: %d] Report %d online users", c.nodeInfo.Type, c.nodeInfo.Id, len(userTraffic)) + log.WithFields(log.Fields{ + "tag": c.Tag, + "err": err, + }).Error("Delete users failed") + return nil } } - userTraffic = nil - runtime.GC() + if len(added) > 0 { + // have added users + _, err = c.server.AddUsers(&vCore.AddUsersParams{ + Tag: c.Tag, + Config: c.ControllerConfig, + UserInfo: added, + NodeInfo: c.nodeInfo, + }) + if err != nil { + log.WithFields(log.Fields{ + "tag": c.Tag, + "err": err, + }).Error("Add users failed") + return nil + } + } + if len(added) > 0 || len(deleted) > 0 { + // update Limiter + err = limiter.UpdateLimiter(c.Tag, added, deleted) + if err != nil { + log.WithFields(log.Fields{ + "tag": c.Tag, + "err": err, + }).Error("limiter users failed") + return nil + } + } + c.userList = newUserInfo + log.WithField("tag", c.Tag). + Errorf("%d user deleted, %d user added", len(deleted), len(added)) return nil } - -func (c *Controller) RenewCert() { - l, err := lego.New(c.CertConfig) - if err != nil { - log.Print("new lego error: ", err) - return - } - err = l.RenewCert() - if err != nil { - log.Print("renew cert error: ", err) - return - } -} diff --git a/node/user.go b/node/user.go index 31857f0..b8e95cc 100644 --- a/node/user.go +++ b/node/user.go @@ -2,9 +2,42 @@ package node import ( "github.com/Yuzuki616/V2bX/api/panel" + log "github.com/sirupsen/logrus" + "runtime" "strconv" ) +func (c *Controller) reportUserTrafficTask() (err error) { + // Get User traffic + userTraffic := make([]panel.UserTraffic, 0) + for i := range c.userList { + up, down := c.server.GetUserTraffic(c.Tag, c.userList[i].Uuid, true) + if up > 0 || down > 0 { + if c.LimitConfig.EnableDynamicSpeedLimit { + c.userList[i].Traffic += up + down + } + userTraffic = append(userTraffic, panel.UserTraffic{ + UID: (c.userList)[i].Id, + Upload: up, + Download: down}) + } + } + if len(userTraffic) > 0 { + err = c.apiClient.ReportUserTraffic(userTraffic) + if err != nil { + log.WithFields(log.Fields{ + "tag": c.Tag, + "err": err, + }).Error("Report user traffic faild") + } else { + log.WithField("tag", err).Errorf("Report %d online users", len(userTraffic)) + } + } + userTraffic = nil + runtime.GC() + return nil +} + func compareUserList(old, new []panel.UserInfo) (deleted, added []panel.UserInfo) { tmp := map[string]struct{}{} tmp2 := map[string]struct{}{}