Merge remote-tracking branch 'origin/dev' into dev

# Conflicts:
#	conf/node.go
This commit is contained in:
Yuzuki616 2023-07-08 22:45:29 +08:00
commit 251702c06a
34 changed files with 583 additions and 397 deletions

View File

@ -30,7 +30,7 @@ A V2board node server based on multi core, modified from XrayR.
| 在线人数统计 | √ | √ | √ | √ | | 在线人数统计 | √ | √ | √ | √ |
| 审计规则 | √ | √ | √ | | | 审计规则 | √ | √ | √ | |
| 自定义DNS | √ | √ | √ | √ | | 自定义DNS | √ | √ | √ | √ |
| 在线IP数限制 | √ | √ | √ | | | 在线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) - [x] 集成基本操作Command(Start, Stop, Restart, Status, Uninstall)
- [ ] 完善Hysteria内核支持 - [ ] 完善Hysteria内核支持
- [ ] 完善使用文档 - [ ] 完善使用文档
- [ ] 尽可能统一日志输出格式
## 软件安装 ## 软件安装

View File

@ -2,6 +2,7 @@ package panel
import ( import (
"fmt" "fmt"
"github.com/Yuzuki616/V2bX/conf"
"github.com/goccy/go-json" "github.com/goccy/go-json"
"reflect" "reflect"
"regexp" "regexp"
@ -41,11 +42,6 @@ type ShadowsocksNodeRsp struct {
ServerKey string `json:"server_key"` ServerKey string `json:"server_key"`
} }
type TrojanNodeRsp struct {
Host string `json:"host"`
ServerName string `json:"server_name"`
}
type HysteriaNodeRsp struct { type HysteriaNodeRsp struct {
UpMbps int `json:"up_mbps"` UpMbps int `json:"up_mbps"`
DownMbps int `json:"down_mbps"` DownMbps int `json:"down_mbps"`
@ -59,6 +55,7 @@ type NodeInfo struct {
Host string Host string
Port int Port int
Network string Network string
ExtraConfig V2rayExtraConfig
NetworkSettings json.RawMessage NetworkSettings json.RawMessage
Tls bool Tls bool
ServerName string ServerName string
@ -71,13 +68,23 @@ type NodeInfo struct {
PullInterval time.Duration 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) { func (c *Client) GetNodeInfo() (node *NodeInfo, err error) {
const path = "/api/v1/server/UniProxy/config" 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 { if err = c.checkResponse(r, path, err); err != nil {
return return
} }
if c.etag == r.Header().Get("ETag") { // node info not changed if r.StatusCode() == 304 {
return nil, nil return nil, nil
} }
// parse common params // parse common params
@ -90,17 +97,30 @@ func (c *Client) GetNodeInfo() (node *NodeInfo, err error) {
if err != nil { if err != nil {
return nil, fmt.Errorf("decode common params error: %s", err) return nil, fmt.Errorf("decode common params error: %s", err)
} }
var extra []byte
for i := range common.Routes { // parse rules from routes for i := range common.Routes { // parse rules from routes
if common.Routes[i].Action == "block" { var matchs []string
var matchs []string if _, ok := common.Routes[i].Match.(string); ok {
if _, ok := common.Routes[i].Match.(string); ok { matchs = strings.Split(common.Routes[i].Match.(string), ",")
matchs = strings.Split(common.Routes[i].Match.(string), ",") } else if _, ok = common.Routes[i].Match.([]string); ok {
} else { matchs = common.Routes[i].Match.([]string)
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 { for _, v := range matchs {
node.Rules = append(node.Rules, regexp.MustCompile(v)) 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 node.ServerName = common.ServerName
@ -122,6 +142,12 @@ func (c *Client) GetNodeInfo() (node *NodeInfo, err error) {
if rsp.Tls == 1 { if rsp.Tls == 1 {
node.Tls = true 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": case "shadowsocks":
rsp := ShadowsocksNodeRsp{} rsp := ShadowsocksNodeRsp{}
err = json.Unmarshal(r.Body(), &rsp) err = json.Unmarshal(r.Body(), &rsp)
@ -131,11 +157,6 @@ func (c *Client) GetNodeInfo() (node *NodeInfo, err error) {
node.ServerKey = rsp.ServerKey node.ServerKey = rsp.ServerKey
node.Cipher = rsp.Cipher node.Cipher = rsp.Cipher
case "trojan": 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": case "hysteria":
rsp := HysteriaNodeRsp{} rsp := HysteriaNodeRsp{}
err = json.Unmarshal(r.Body(), &rsp) err = json.Unmarshal(r.Body(), &rsp)
@ -146,7 +167,7 @@ func (c *Client) GetNodeInfo() (node *NodeInfo, err error) {
node.UpMbps = rsp.UpMbps node.UpMbps = rsp.UpMbps
node.HyObfs = rsp.Obfs node.HyObfs = rsp.Obfs
} }
c.etag = r.Header().Get("Etag") c.etag = r.Header().Get("ETag")
return return
} }

View File

@ -23,6 +23,7 @@ func init() {
func TestClient_GetNodeInfo(t *testing.T) { func TestClient_GetNodeInfo(t *testing.T) {
log.Println(client.GetNodeInfo()) log.Println(client.GetNodeInfo())
log.Println(client.GetNodeInfo())
} }
func TestClient_ReportUserTraffic(t *testing.T) { func TestClient_ReportUserTraffic(t *testing.T) {

View File

@ -18,7 +18,7 @@ func (c *Client) checkResponse(res *resty.Response, path string, err error) erro
if err != nil { if err != nil {
return fmt.Errorf("request %s failed: %s", c.assembleURL(path), err) return fmt.Errorf("request %s failed: %s", c.assembleURL(path), err)
} }
if res.StatusCode() != 200 { if res.StatusCode() >= 400 {
body := res.Body() body := res.Body()
return fmt.Errorf("request %s failed: %s", c.assembleURL(path), string(body)) return fmt.Errorf("request %s failed: %s", c.assembleURL(path), string(body))
} }

View File

@ -1,7 +1,7 @@
package cmd package cmd
import ( import (
"log" log "github.com/sirupsen/logrus"
_ "github.com/Yuzuki616/V2bX/core/imports" _ "github.com/Yuzuki616/V2bX/core/imports"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@ -14,6 +14,6 @@ var command = &cobra.Command{
func Run() { func Run() {
err := command.Execute() err := command.Execute()
if err != nil { if err != nil {
log.Println("execute failed, error:", err) log.WithField("err", err).Error("Execute command failed")
} }
} }

View File

@ -1,7 +1,7 @@
package cmd package cmd
import ( import (
"log" log "github.com/sirupsen/logrus"
"os" "os"
"os/signal" "os/signal"
"runtime" "runtime"
@ -42,23 +42,26 @@ func serverHandle(_ *cobra.Command, _ []string) {
c := conf.New() c := conf.New()
err := c.LoadFromPath(config) err := c.LoadFromPath(config)
if err != nil { 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() limiter.Init()
log.Println("Start V2bX...") log.Info("Start V2bX...")
vc, err := vCore.NewCore(&c.CoreConfig) vc, err := vCore.NewCore(&c.CoreConfig)
if err != nil { if err != nil {
log.Fatalf("New core error: %s", err) log.WithField("err", err).Error("new core failed")
return
} }
err = vc.Start() err = vc.Start()
if err != nil { if err != nil {
log.Fatalf("Start core error: %s", err) log.WithField("err", err).Error("Start core failed")
return
} }
defer vc.Close() defer vc.Close()
nodes := node.New() nodes := node.New()
err = nodes.Start(c.NodesConfig, vc) err = nodes.Start(c.NodesConfig, vc)
if err != nil { if err != nil {
log.Fatalf("Run nodes error: %s", err) log.WithField("err", err).Error("Run nodes failed")
return return
} }
if watch { if watch {
@ -66,24 +69,29 @@ func serverHandle(_ *cobra.Command, _ []string) {
nodes.Close() nodes.Close()
err = vc.Close() err = vc.Close()
if err != nil { 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) vc, err = vCore.NewCore(&c.CoreConfig)
if err != nil { if err != nil {
log.Fatalf("New core error: %s", err) log.WithField("err", err).Error("New core failed")
return
} }
err = vc.Start() err = vc.Start()
if err != nil { 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) err = nodes.Start(c.NodesConfig, vc)
if err != nil { if err != nil {
log.Fatalf("Run nodes error: %s", err) log.WithField("err", err).Error("Run nodes failed")
return
} }
runtime.GC() runtime.GC()
}) })
if err != nil { if err != nil {
log.Fatalf("Watch config file error: %s", err) log.WithField("err", err).Error("start watch failed")
return
} }
} }
// clear memory // clear memory

View File

@ -90,6 +90,7 @@ func BuildInbound(config *conf.ControllerConfig, nodeInfo *panel.NodeInfo, tag s
} }
default: default:
// Normal tls // Normal tls
in.StreamSetting.Security = "tls"
in.StreamSetting.TLSSettings = &coreConf.TLSConfig{ in.StreamSetting.TLSSettings = &coreConf.TLSConfig{
Certs: []*coreConf.TLSCertConfig{ 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 // Support ProxyProtocol for any transport protocol
if *in.StreamSetting.Network != "tcp" && if *in.StreamSetting.Network != "tcp" &&
*in.StreamSetting.Network != "ws" && *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 { 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 //Set vless
inbound.Protocol = "vless" inbound.Protocol = "vless"
if config.XrayOptions.EnableFallback { if config.XrayOptions.EnableFallback {

View File

@ -14,8 +14,6 @@ import (
"strings" "strings"
) )
const xtlsFLow = "xtls-rprx-vision"
func BuildVmessUsers(tag string, userInfo []panel.UserInfo) (users []*protocol.User) { func BuildVmessUsers(tag string, userInfo []panel.UserInfo) (users []*protocol.User) {
users = make([]*protocol.User, len(userInfo)) users = make([]*protocol.User, len(userInfo))
for i, user := range 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)) users = make([]*protocol.User, len(userInfo))
for i := range userInfo { for i := range userInfo {
users[i] = BuildVlessUser(tag, &(userInfo)[i], xtls) users[i] = BuildVlessUser(tag, &(userInfo)[i], flow)
} }
return users 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{ vlessAccount := &vless.Account{
Id: userInfo.Uuid, Id: userInfo.Uuid,
} }
if xtls { vlessAccount.Flow = flow
vlessAccount.Flow = xtlsFLow
}
return &protocol.User{ return &protocol.User{
Level: 0, Level: 0,
Email: BuildUserTag(tag, userInfo.Uuid), Email: BuildUserTag(tag, userInfo.Uuid),
@ -93,7 +89,7 @@ func BuildSSUser(tag string, userInfo *panel.UserInfo, cypher string, serverKey
} }
return &protocol.User{ return &protocol.User{
Level: 0, Level: 0,
Email: tag, Email: BuildUserTag(tag, userInfo.Uuid),
Account: serial.ToTypedMessage(ssAccount), Account: serial.ToTypedMessage(ssAccount),
} }
} else { } else {

78
common/task/task.go Normal file
View File

@ -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
}
}

15
common/task/task_test.go Normal file
View File

@ -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)
}

View File

@ -2,14 +2,9 @@ package conf
import ( import (
"fmt" "fmt"
"io"
"log"
"os"
"path"
"time"
"github.com/fsnotify/fsnotify"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
"io"
"os"
) )
type Conf struct { type Conf struct {
@ -56,44 +51,3 @@ func (p *Conf) LoadFromPath(filePath string) error {
} }
return nil 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
}

View File

@ -15,28 +15,26 @@ type ApiConfig struct {
} }
type ControllerConfig struct { type ControllerConfig struct {
DisableUploadTraffic bool `yaml:"DisableUploadTraffic"` ListenIP string `yaml:"ListenIP"`
DisableGetRule bool `yaml:"DisableGetRule"` SendIP string `yaml:"SendIP"`
ListenIP string `yaml:"ListenIP"` EnableProxyProtocol bool `yaml:"EnableProxyProtocol"`
SendIP string `yaml:"SendIP"` XrayOptions XrayOptions `yaml:"XrayOptions"`
XrayOptions XrayOptions `yaml:"XrayOptions"` HyOptions HyOptions `yaml:"HyOptions"`
HyOptions HyOptions `yaml:"HyOptions"` LimitConfig LimitConfig `yaml:"LimitConfig"`
LimitConfig LimitConfig `yaml:"LimitConfig"` CertConfig *CertConfig `yaml:"CertConfig"`
CertConfig *CertConfig `yaml:"CertConfig"`
} }
type XrayOptions struct { type XrayOptions struct {
EnableProxyProtocol bool `yaml:"EnableProxyProtocol"` EnableDNS bool `yaml:"EnableDNS"`
EnableDNS bool `yaml:"EnableDNS"` DNSType string `yaml:"DNSType"`
DNSType string `yaml:"DNSType"` EnableVless bool `yaml:"EnableVless"`
EnableVless bool `yaml:"EnableVless"` VlessFlow string `json:"VlessFlow"`
EnableXtls bool `yaml:"EnableXtls"` EnableUot bool `yaml:"EnableUot"`
EnableUot bool `yaml:"EnableUot"` EnableTFO bool `yaml:"EnableTFO"`
EnableTFO bool `yaml:"EnableTFO"` DisableIVCheck bool `yaml:"DisableIVCheck"`
DisableIVCheck bool `yaml:"DisableIVCheck"` DisableSniffing bool `yaml:"DisableSniffing"`
DisableSniffing bool `yaml:"DisableSniffing"` EnableFallback bool `yaml:"EnableFallback"`
EnableFallback bool `yaml:"EnableFallback"` FallBackConfigs []FallBackConfig `yaml:"FallBackConfigs"`
FallBackConfigs []FallBackConfig `yaml:"FallBackConfigs"`
} }
type HyOptions struct { type HyOptions struct {
@ -105,12 +103,12 @@ type CertConfig struct {
} }
type RealityConfig struct { type RealityConfig struct {
Dest interface{} `yaml:"Dest"` Dest interface{} `yaml:"Dest" json:"Dest"`
Xver uint64 `yaml:"Xver"` Xver uint64 `yaml:"Xver" json:"Xver"`
ServerNames []string `yaml:"ServerNames"` ServerNames []string `yaml:"ServerNames" json:"ServerNames"`
PrivateKey string `yaml:"PrivateKey"` PrivateKey string `yaml:"PrivateKey" json:"PrivateKey"`
MinClientVer string `yaml:"MinClientVer"` MinClientVer string `yaml:"MinClientVer" json:"MinClientVer"`
MaxClientVer string `yaml:"MaxClientVer"` MaxClientVer string `yaml:"MaxClientVer" json:"MaxClientVer"`
MaxTimeDiff uint64 `yaml:"MaxTimeDiff"` MaxTimeDiff uint64 `yaml:"MaxTimeDiff" json:"MaxTimeDiff"`
ShortIds []string `yaml:"ShortIds"` ShortIds []string `yaml:"ShortIds" json:"ShortIds"`
} }

52
conf/watch.go Normal file
View File

@ -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
}

View File

@ -26,7 +26,7 @@ func NewCore(c *conf.CoreConfig) (Core, error) {
} }
cs = append(cs, core1) cs = append(cs, core1)
} }
return &Selecter{ return &Selector{
cores: cs, cores: cs,
}, nil }, nil
} }

View File

@ -5,7 +5,7 @@ import (
"fmt" "fmt"
"github.com/Yuzuki616/V2bX/api/panel" "github.com/Yuzuki616/V2bX/api/panel"
"github.com/Yuzuki616/V2bX/conf" "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 { 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", "": case "reality", "none", "":
return errors.New("hysteria need normal tls cert") return errors.New("hysteria need normal tls cert")
} }
s := NewServer(tag) l, err := limiter.GetLimiter(tag)
err := s.runServer(info, c) if err != nil {
return fmt.Errorf("get limiter error: %s", err)
}
s := NewServer(tag, l)
err = s.runServer(info, c)
if err != nil { if err != nil {
return fmt.Errorf("run hy server error: %s", err) 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 { func (h *Hy) DelNode(tag string) error {
if s, e := h.servers.Load(tag); e { if s, e := h.servers.Load(tag); e {
err := s.(*cs.Server).Close() err := s.(*Server).Close()
if err != nil { if err != nil {
return err return err
} }

View File

@ -5,6 +5,7 @@ import (
"fmt" "fmt"
"github.com/Yuzuki616/V2bX/api/panel" "github.com/Yuzuki616/V2bX/api/panel"
"github.com/Yuzuki616/V2bX/conf" "github.com/Yuzuki616/V2bX/conf"
"github.com/Yuzuki616/V2bX/limiter"
"github.com/apernet/hysteria/core/sockopt" "github.com/apernet/hysteria/core/sockopt"
"io" "io"
"net" "net"
@ -32,15 +33,17 @@ var serverPacketConnFuncFactoryMap = map[string]pktconns.ServerPacketConnFuncFac
type Server struct { type Server struct {
tag string tag string
l *limiter.Limiter
counter *UserTrafficCounter counter *UserTrafficCounter
users sync.Map users sync.Map
running atomic.Bool running atomic.Bool
*cs.Server *cs.Server
} }
func NewServer(tag string) *Server { func NewServer(tag string, l *limiter.Limiter) *Server {
return &Server{ return &Server{
tag: tag, 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) { 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 { if _, ok := s.users.Load(string(auth)); ok {
return true, "Done" 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) { 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) ok, msg := s.authByUser(addr, auth, sSend, sRecv)
if !ok { if !ok {
logrus.WithFields(logrus.Fields{ 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) { func (s *Server) disconnectFunc(addr net.Addr, auth []byte, err error) {
s.l.ConnLimiter.DelConnCount(addr.String(), string(auth))
logrus.WithFields(logrus.Fields{ logrus.WithFields(logrus.Fields{
"src": defaultIPMasker.Mask(addr.String()), "src": defaultIPMasker.Mask(addr.String()),
"error": err, "error": err,

View File

@ -1,17 +1,23 @@
package hy package hy
import ( import (
"encoding/base64"
"github.com/Yuzuki616/V2bX/api/panel" "github.com/Yuzuki616/V2bX/api/panel"
"github.com/Yuzuki616/V2bX/conf" "github.com/Yuzuki616/V2bX/conf"
"github.com/Yuzuki616/V2bX/limiter"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"log"
"testing" "testing"
"time"
) )
func TestServer(t *testing.T) { func TestServer(t *testing.T) {
logrus.SetLevel(logrus.DebugLevel) 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{ t.Log(s.runServer(&panel.NodeInfo{
Port: 11415, Port: 1145,
UpMbps: 100, UpMbps: 100,
DownMbps: 100, DownMbps: 100,
HyObfs: "atresssdaaaadd", HyObfs: "atresssdaaaadd",
@ -24,5 +30,13 @@ func TestServer(t *testing.T) {
}, },
})) }))
s.users.Store("test1111", struct{}{}) 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 {} select {}
} }

View File

@ -3,6 +3,7 @@ package hy
import ( import (
"encoding/base64" "encoding/base64"
"errors" "errors"
"github.com/Yuzuki616/V2bX/api/panel"
"github.com/Yuzuki616/V2bX/core" "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) s := v.(*Server)
auth := base64.StdEncoding.EncodeToString([]byte(uuid)) auth := base64.StdEncoding.EncodeToString([]byte(uuid))
up = s.counter.getCounters(auth).UpCounter.Load() up = s.counter.getCounters(auth).UpCounter.Load()
down = s.counter.getCounters(uuid).DownCounter.Load() down = s.counter.getCounters(auth).DownCounter.Load()
if reset { if reset {
s.counter.Reset(uuid) s.counter.Reset(auth)
} }
return 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) v, e := h.servers.Load(tag)
if !e { if !e {
return errors.New("the node is not have") return errors.New("the node is not have")
} }
s := v.(*Server) s := v.(*Server)
for i := range users { for i := range users {
s.users.Delete(users[i]) s.users.Delete(users[i].Uuid)
s.counter.Delete(users[i]) s.counter.Delete(base64.StdEncoding.EncodeToString([]byte(users[i].Uuid)))
} }
return nil return nil
} }

View File

@ -18,6 +18,6 @@ type Core interface {
DelNode(tag string) error DelNode(tag string) error
AddUsers(p *AddUsersParams) (added int, err error) AddUsers(p *AddUsersParams) (added int, err error)
GetUserTraffic(tag, uuid string, reset bool) (up int64, down int64) 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 Protocols() []string
} }

View File

@ -9,12 +9,12 @@ import (
"github.com/hashicorp/go-multierror" "github.com/hashicorp/go-multierror"
) )
type Selecter struct { type Selector struct {
cores []Core cores []Core
nodes sync.Map nodes sync.Map
} }
func (s *Selecter) Start() error { func (s *Selector) Start() error {
for i := range s.cores { for i := range s.cores {
err := s.cores[i].Start() err := s.cores[i].Start()
return err return err
@ -22,7 +22,7 @@ func (s *Selecter) Start() error {
return nil return nil
} }
func (s *Selecter) Close() error { func (s *Selector) Close() error {
var errs error var errs error
for i := range s.cores { for i := range s.cores {
errs = multierror.Append(errs, s.cores[i].Close()) errs = multierror.Append(errs, s.cores[i].Close())
@ -39,7 +39,7 @@ func isSupported(protocol string, protocols []string) bool {
return false 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 { for i := range s.cores {
if !isSupported(info.Type, s.cores[i].Protocols()) { if !isSupported(info.Type, s.cores[i].Protocols()) {
continue 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") 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 { if t, e := s.nodes.Load(tag); e {
err := s.cores[t.(int)].DelNode(tag) err := s.cores[t.(int)].DelNode(tag)
if err != nil { if err != nil {
@ -65,7 +65,7 @@ func (s *Selecter) DelNode(tag string) error {
return errors.New("the node is not have") 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) t, e := s.nodes.Load(p.Tag)
if !e { if !e {
return 0, errors.New("the node is not have") 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) 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) t, e := s.nodes.Load(tag)
if !e { if !e {
return 0, 0 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) 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) t, e := s.nodes.Load(tag)
if !e { if !e {
return errors.New("the node is not have") 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) return s.cores[t.(int)].DelUsers(users, tag)
} }
func (s *Selecter) Protocols() []string { func (s *Selector) Protocols() []string {
protocols := make([]string, 0) protocols := make([]string, 0)
for i := range s.cores { for i := range s.cores {
protocols = append(protocols, s.cores[i].Protocols()...) protocols = append(protocols, s.cores[i].Protocols()...)

View File

@ -21,7 +21,7 @@ import (
_ "github.com/xtls/xray-core/app/dns" _ "github.com/xtls/xray-core/app/dns"
_ "github.com/xtls/xray-core/app/dns/fakedns" _ "github.com/xtls/xray-core/app/dns/fakedns"
_ "github.com/xtls/xray-core/app/log" _ "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/policy"
_ "github.com/xtls/xray-core/app/reverse" _ "github.com/xtls/xray-core/app/reverse"
_ "github.com/xtls/xray-core/app/router" _ "github.com/xtls/xray-core/app/router"

View File

@ -3,7 +3,7 @@ package xray
import ( import (
"context" "context"
"fmt" "fmt"
"github.com/Yuzuki616/V2bX/api/panel"
"github.com/Yuzuki616/V2bX/common/builder" "github.com/Yuzuki616/V2bX/common/builder"
vCore "github.com/Yuzuki616/V2bX/core" vCore "github.com/Yuzuki616/V2bX/core"
"github.com/xtls/xray-core/common/protocol" "github.com/xtls/xray-core/common/protocol"
@ -26,16 +26,22 @@ func (c *Core) GetUserManager(tag string) (proxy.UserManager, error) {
return userManager, nil 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) userManager, err := c.GetUserManager(tag)
if err != nil { if err != nil {
return fmt.Errorf("get user manager error: %s", err) return fmt.Errorf("get user manager error: %s", err)
} }
for _, email := range users { var up, down, user string
err = userManager.RemoveUser(context.Background(), email) for i := range users {
user = builder.BuildUserTag(tag, users[i].Uuid)
err = userManager.RemoveUser(context.Background(), user)
if err != nil { if err != nil {
return err return err
} }
up = "user>>>" + user + ">>>traffic>>>uplink"
down = "user>>>" + user + ">>>traffic>>>downlink"
c.shm.UnregisterCounter(up)
c.shm.UnregisterCounter(down)
} }
return nil 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)) users := make([]*protocol.User, 0, len(p.UserInfo))
switch p.NodeInfo.Type { switch p.NodeInfo.Type {
case "v2ray": case "v2ray":
if p.Config.XrayOptions.EnableXtls { if p.Config.XrayOptions.EnableVless ||
users = builder.BuildVlessUsers(p.Tag, p.UserInfo, true) 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 { } else {
users = builder.BuildVmessUsers(p.Tag, p.UserInfo) users = builder.BuildVmessUsers(p.Tag, p.UserInfo)
} }
case "trojan": case "trojan":
users = builder.BuildTrojanUsers(p.Tag, p.UserInfo) users = builder.BuildTrojanUsers(p.Tag, p.UserInfo)
case "shadowsocks": case "shadowsocks":

View File

@ -1,7 +1,6 @@
package xray package xray
import ( import (
"log"
"os" "os"
"sync" "sync"
@ -10,6 +9,7 @@ import (
"github.com/Yuzuki616/V2bX/core/xray/app/dispatcher" "github.com/Yuzuki616/V2bX/core/xray/app/dispatcher"
_ "github.com/Yuzuki616/V2bX/core/xray/distro/all" _ "github.com/Yuzuki616/V2bX/core/xray/distro/all"
"github.com/goccy/go-json" "github.com/goccy/go-json"
log "github.com/sirupsen/logrus"
"github.com/xtls/xray-core/app/proxyman" "github.com/xtls/xray-core/app/proxyman"
"github.com/xtls/xray-core/app/stats" "github.com/xtls/xray-core/app/stats"
"github.com/xtls/xray-core/common/serial" "github.com/xtls/xray-core/common/serial"
@ -63,40 +63,40 @@ func getCore(c *conf.XrayConfig) *core.Instance {
coreDnsConfig := &coreConf.DNSConfig{} coreDnsConfig := &coreConf.DNSConfig{}
if c.DnsConfigPath != "" { if c.DnsConfigPath != "" {
if f, err := os.Open(c.DnsConfigPath); err != nil { 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 { } else {
if err = json.NewDecoder(f).Decode(coreDnsConfig); err != nil { 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() dnsConfig, err := coreDnsConfig.Build()
if err != nil { 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 // Routing config
coreRouterConfig := &coreConf.RouterConfig{} coreRouterConfig := &coreConf.RouterConfig{}
if c.RouteConfigPath != "" { if c.RouteConfigPath != "" {
if f, err := os.Open(c.RouteConfigPath); err != nil { 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 { } else {
if err = json.NewDecoder(f).Decode(coreRouterConfig); err != nil { 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() routeConfig, err := coreRouterConfig.Build()
if err != nil { 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 // Custom Inbound config
var coreCustomInboundConfig []coreConf.InboundDetourConfig var coreCustomInboundConfig []coreConf.InboundDetourConfig
if c.InboundConfigPath != "" { if c.InboundConfigPath != "" {
if f, err := os.Open(c.InboundConfigPath); err != nil { 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 { } else {
if err = json.NewDecoder(f).Decode(&coreCustomInboundConfig); err != nil { 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 { for _, config := range coreCustomInboundConfig {
oc, err := config.Build() oc, err := config.Build()
if err != nil { 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) inBoundConfig = append(inBoundConfig, oc)
} }
@ -112,10 +112,10 @@ func getCore(c *conf.XrayConfig) *core.Instance {
var coreCustomOutboundConfig []coreConf.OutboundDetourConfig var coreCustomOutboundConfig []coreConf.OutboundDetourConfig
if c.OutboundConfigPath != "" { if c.OutboundConfigPath != "" {
if f, err := os.Open(c.OutboundConfigPath); err != nil { 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 { } else {
if err = json.NewDecoder(f).Decode(&coreCustomOutboundConfig); err != nil { 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 { for _, config := range coreCustomOutboundConfig {
oc, err := config.Build() oc, err := config.Build()
if err != nil { 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) outBoundConfig = append(outBoundConfig, oc)
} }
@ -149,10 +149,9 @@ func getCore(c *conf.XrayConfig) *core.Instance {
} }
server, err := core.New(config) server, err := core.New(config)
if err != nil { 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 return server
} }

View File

@ -1,5 +1,5 @@
CoreConfig: 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: XrayConfig:
Log: Log:
Level: warning # Log level: none, error, warning, info, debug Level: warning # Log level: none, error, warning, info, debug
@ -31,7 +31,7 @@ Nodes:
DNSType: AsIs # AsIs, UseIP, UseIPv4, UseIPv6, DNS strategy DNSType: AsIs # AsIs, UseIP, UseIPv4, UseIPv6, DNS strategy
EnableTFO: false # Enable TCP Fast Open EnableTFO: false # Enable TCP Fast Open
EnableVless: false # Enable Vless for V2ray Type 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 EnableProxyProtocol: false # Only works for WebSocket and TCP
EnableFallback: false # Only support for Trojan and Vless EnableFallback: false # Only support for Trojan and Vless
FallBackConfigs: # Support multiple fallbacks FallBackConfigs: # Support multiple fallbacks
@ -39,7 +39,7 @@ Nodes:
Alpn: # Alpn, Empty for any Alpn: # Alpn, Empty for any
Path: # HTTP PATH, 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. 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: HyOptions:
Resolver: "udp://1.1.1.1:53" # DNS resolver address 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) 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)

20
go.mod
View File

@ -3,23 +3,23 @@ module github.com/Yuzuki616/V2bX
go 1.20 go 1.20
require ( require (
github.com/apernet/hysteria/core v1.3.4 github.com/apernet/hysteria/core v1.3.5
github.com/beevik/ntp v1.0.0 github.com/beevik/ntp v1.1.1
github.com/folbricht/routedns v0.1.21-0.20230220022436-4ae86ce30d53 github.com/folbricht/routedns v0.1.21-0.20230220022436-4ae86ce30d53
github.com/fsnotify/fsnotify v1.6.0 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-redis/redis/v8 v8.11.5
github.com/go-resty/resty/v2 v2.7.0 github.com/go-resty/resty/v2 v2.7.0
github.com/goccy/go-json v0.10.2 github.com/goccy/go-json v0.10.2
github.com/hashicorp/go-multierror v1.1.1 github.com/hashicorp/go-multierror v1.1.1
github.com/juju/ratelimit v1.0.2 github.com/juju/ratelimit v1.0.2
github.com/oschwald/geoip2-golang v1.8.0 github.com/oschwald/geoip2-golang v1.9.0
github.com/quic-go/quic-go v0.33.0 github.com/quic-go/quic-go v0.34.0
github.com/sagernet/sing-box v1.2.7 github.com/sagernet/sing-box v1.2.7
github.com/sirupsen/logrus v1.9.3 github.com/sirupsen/logrus v1.9.3
github.com/spf13/cobra v1.7.0 github.com/spf13/cobra v1.7.0
github.com/xtls/xray-core v1.8.1 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 google.golang.org/protobuf v1.30.0
gopkg.in/yaml.v3 v3.0.1 gopkg.in/yaml.v3 v3.0.1
) )
@ -121,7 +121,7 @@ require (
github.com/nrdcg/porkbun v0.1.1 // indirect github.com/nrdcg/porkbun v0.1.1 // indirect
github.com/onsi/ginkgo/v2 v2.9.2 // indirect github.com/onsi/ginkgo/v2 v2.9.2 // indirect
github.com/oracle/oci-go-sdk v24.3.0+incompatible // 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/ovh/go-ovh v1.1.0 // indirect
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
github.com/pion/dtls/v2 v2.2.4 // 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/cast v1.5.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect github.com/spf13/pflag v1.0.5 // indirect
github.com/stretchr/objx v0.5.0 // 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/common v1.0.490 // indirect
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod 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 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/mod v0.10.0 // indirect
golang.org/x/net v0.10.0 // indirect golang.org/x/net v0.10.0 // indirect
golang.org/x/oauth2 v0.6.0 // indirect golang.org/x/oauth2 v0.6.0 // indirect
golang.org/x/sys v0.8.0 // indirect golang.org/x/sys v0.9.0 // indirect
golang.org/x/text v0.9.0 // indirect golang.org/x/text v0.10.0 // indirect
golang.org/x/time v0.3.0 // indirect golang.org/x/time v0.3.0 // indirect
golang.org/x/tools v0.8.0 // indirect golang.org/x/tools v0.8.0 // indirect
google.golang.org/api v0.114.0 // indirect google.golang.org/api v0.114.0 // indirect

41
go.sum
View File

@ -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/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/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/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.5 h1:Pg7w7rcdXhkGUJoG3ALpT9waz+ZmWpIpD5QqJe8MbuI=
github.com/apernet/hysteria/core v1.3.4/go.mod h1:WxFihvoDBhatefn4w8Lipzy97CWjK6NF1/duT0kGUG0= 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 h1:3KFetJ/lUFn0m9xTFg+rMmz2nyHg+D2boJX0Rp4OF6c=
github.com/apernet/quic-go v0.34.1-0.20230507231629-ec008b7e8473/go.mod h1:+4CVgVppm0FNjpG3UcX8Joi/frKOH7/ciD5yGcwOO1g= 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= 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/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 h1:74BBwkEmiqBbi2CGflEh34l0YNtIibTjZsibGarkNjo=
github.com/aws/aws-sdk-go v1.39.0/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= 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.1.1 h1:ah1SrQMwiyL8eRpb7+Erm7l4PvXnZ/Dnh/gjGZXPvp0=
github.com/beevik/ntp v1.0.0/go.mod h1:JN7/74B0Z4GUGO/1aUeRI2adARlfJGUeaJb0y0Wvnf4= 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 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.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= 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-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/gin-gonic/gin v1.7.4/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY=
github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= 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.3 h1:aWPYhBopAZXWBASPgvi1LnWGrr5YiXOsrpVaFaVJipo=
github.com/go-acme/lego/v4 v4.12.1/go.mod h1:UZoOlhVmUYP/N0z4tEbfUjoCNHRZNObzqWZtT76DIsc= 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-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-cmd/cmd v1.0.5/go.mod h1:y8q8qlK5wQibcw63djSl/ntiHUHXHGdCkPk0j4QeW4s=
github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w= 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/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 h1:x4mcfb4agelf1O4/1/auGlZ1lr97jXRSSN5MxTgG/zU=
github.com/oracle/oci-go-sdk v24.3.0+incompatible/go.mod h1:VQb79nF8Z2cwLkLS35ukwStZIg5F66tcBccjip/j888= 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.9.0 h1:uvD3O6fXAXs+usU+UGExshpdP13GAqp4GBrzN7IgKZc=
github.com/oschwald/geoip2-golang v1.8.0/go.mod h1:R7bRvYjOeaoenAp9sKRS8GX5bJWcZ0laWO5+DauEktw= github.com/oschwald/geoip2-golang v1.9.0/go.mod h1:BHK6TvDyATVQhKNbQBdrj9eAvuwOMi2zSFXizL3K81Y=
github.com/oschwald/maxminddb-golang v1.10.0 h1:Xp1u0ZhqkSuopaKmk1WwHtjF0H9Hd9181uj2MQ5Vndg= github.com/oschwald/maxminddb-golang v1.11.0 h1:aSXMqYR/EPNjGE8epgqwDay+P30hCBZIveY0WZbAWh0=
github.com/oschwald/maxminddb-golang v1.10.0/go.mod h1:Y2ELenReaLAZ0b400URyGwvYxHV1dLIxBuyOsyYjHK0= 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 h1:bHXZmw8nTgZin4Nv7JuaLs0KG5x54EQR7migYTd1zrk=
github.com/ovh/go-ovh v1.1.0/go.mod h1:AxitLZ5HBRPyUd+Zl60Ajaag+rNTdVXWIkzfrVuTXWA= 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= 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.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.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.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= 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/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= 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-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.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.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.10.0 h1:LKqV2xt9+kDzSTfOhx4FrkEBcMrAgHSYgzywV9zcGmM=
golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= 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-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-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= 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.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.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.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 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= 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= 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.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.4.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.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.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-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-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 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.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.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.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/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.8.0 h1:n5xxQn2i3PC0yLAbjTpNT85q/Kgzcr2gIoX9OrJUols= 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.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.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 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.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.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.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.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-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-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=

View File

@ -1,14 +1,16 @@
package limiter package limiter
import "log" import log "github.com/sirupsen/logrus"
func ClearOnlineIP() error { func ClearOnlineIP() error {
log.Println("Limiter: Clear online ip...") log.WithField("Type", "Limiter").
Debug("Clear online ip...")
limitLock.RLock() limitLock.RLock()
for _, l := range limiter { for _, l := range limiter {
l.ConnLimiter.ClearOnlineIP() l.ConnLimiter.ClearOnlineIP()
} }
limitLock.RUnlock() limitLock.RUnlock()
log.Println("Limiter: Clear online ip done") log.WithField("Type", "Limiter").
Debug("Clear online ip done")
return nil return nil
} }

View File

@ -1,8 +1,8 @@
package limiter package limiter
import ( import (
"fmt"
"github.com/Yuzuki616/V2bX/api/panel" "github.com/Yuzuki616/V2bX/api/panel"
"github.com/Yuzuki616/V2bX/common/builder"
"time" "time"
) )
@ -11,7 +11,7 @@ func (l *Limiter) AddDynamicSpeedLimit(tag string, userInfo *panel.UserInfo, lim
DynamicSpeedLimit: limitNum, DynamicSpeedLimit: limitNum,
ExpireTime: time.Now().Add(time.Duration(expire) * time.Second).Unix(), 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 return nil
} }

View File

@ -7,8 +7,8 @@ import (
"github.com/Yuzuki616/V2bX/common/builder" "github.com/Yuzuki616/V2bX/common/builder"
"github.com/Yuzuki616/V2bX/conf" "github.com/Yuzuki616/V2bX/conf"
"github.com/juju/ratelimit" "github.com/juju/ratelimit"
log "github.com/sirupsen/logrus"
"github.com/xtls/xray-core/common/task" "github.com/xtls/xray-core/common/task"
"log"
"regexp" "regexp"
"sync" "sync"
"time" "time"
@ -24,7 +24,8 @@ func Init() {
Execute: ClearOnlineIP, Execute: ClearOnlineIP,
} }
go func() { go func() {
log.Println("Limiter: ClearOnlineIP started") log.WithField("Type", "Limiter").
Debug("ClearOnlineIP started")
time.Sleep(time.Minute * 2) time.Sleep(time.Minute * 2)
_ = c.Start() _ = c.Start()
}() }()
@ -85,10 +86,7 @@ func UpdateLimiter(tag string, added []panel.UserInfo, deleted []panel.UserInfo)
return fmt.Errorf("get limit error: %s", err) return fmt.Errorf("get limit error: %s", err)
} }
for i := range deleted { for i := range deleted {
l.UserLimitInfo.Delete(fmt.Sprintf("%s|%s|%d", l.UserLimitInfo.Delete(builder.BuildUserTag(tag, deleted[i].Uuid))
tag,
deleted[i].Uuid,
deleted[i].Id))
} }
for i := range added { for i := range added {
if added[i].SpeedLimit != 0 { if added[i].SpeedLimit != 0 {
@ -97,10 +95,7 @@ func UpdateLimiter(tag string, added []panel.UserInfo, deleted []panel.UserInfo)
SpeedLimit: added[i].SpeedLimit, SpeedLimit: added[i].SpeedLimit,
ExpireTime: 0, ExpireTime: 0,
} }
l.UserLimitInfo.Store(fmt.Sprintf("%s|%s|%d", l.UserLimitInfo.Store(builder.BuildUserTag(tag, added[i].Uuid), userLimit)
tag,
added[i].Uuid,
added[i].Id), userLimit)
} }
} }
return nil return nil

View File

@ -4,8 +4,22 @@ import (
"fmt" "fmt"
"github.com/Yuzuki616/V2bX/common/file" "github.com/Yuzuki616/V2bX/common/file"
"github.com/Yuzuki616/V2bX/node/lego" "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 { func (c *Controller) requestCert() error {
if c.CertConfig.CertFile == "" || c.CertConfig.KeyFile == "" { if c.CertConfig.CertFile == "" || c.CertConfig.KeyFile == "" {
return fmt.Errorf("cert file path or key file path not exist") return fmt.Errorf("cert file path or key file path not exist")

View File

@ -3,14 +3,13 @@ package node
import ( import (
"errors" "errors"
"fmt" "fmt"
"log"
"github.com/Yuzuki616/V2bX/api/iprecoder" "github.com/Yuzuki616/V2bX/api/iprecoder"
"github.com/Yuzuki616/V2bX/api/panel" "github.com/Yuzuki616/V2bX/api/panel"
"github.com/Yuzuki616/V2bX/common/task"
"github.com/Yuzuki616/V2bX/conf" "github.com/Yuzuki616/V2bX/conf"
vCore "github.com/Yuzuki616/V2bX/core" vCore "github.com/Yuzuki616/V2bX/core"
"github.com/Yuzuki616/V2bX/limiter" "github.com/Yuzuki616/V2bX/limiter"
"github.com/xtls/xray-core/common/task" log "github.com/sirupsen/logrus"
) )
type Controller struct { type Controller struct {
@ -20,11 +19,11 @@ type Controller struct {
Tag string Tag string
userList []panel.UserInfo userList []panel.UserInfo
ipRecorder iprecoder.IpRecorder ipRecorder iprecoder.IpRecorder
nodeInfoMonitorPeriodic *task.Periodic nodeInfoMonitorPeriodic *task.Task
userReportPeriodic *task.Periodic userReportPeriodic *task.Task
renewCertPeriodic *task.Periodic renewCertPeriodic *task.Task
dynamicSpeedLimitPeriodic *task.Periodic dynamicSpeedLimitPeriodic *task.Task
onlineIpReportPeriodic *task.Periodic onlineIpReportPeriodic *task.Task
*conf.ControllerConfig *conf.ControllerConfig
} }
@ -59,12 +58,10 @@ func (c *Controller) Start() error {
// add limiter // add limiter
l := limiter.AddLimiter(c.Tag, &c.LimitConfig, c.userList) l := limiter.AddLimiter(c.Tag, &c.LimitConfig, c.userList)
// add rule limiter // add rule limiter
if !c.DisableGetRule { if err = l.UpdateRule(c.nodeInfo.Rules); err != nil {
if err = l.UpdateRule(c.nodeInfo.Rules); err != nil { return fmt.Errorf("update rule error: %s", err)
return fmt.Errorf("update rule error: %s", err)
}
} }
if c.nodeInfo.Tls { if c.nodeInfo.Tls || c.nodeInfo.Type == "hysteria" {
err = c.requestCert() err = c.requestCert()
if err != nil { if err != nil {
return fmt.Errorf("request cert error: %s", err) return fmt.Errorf("request cert error: %s", err)
@ -84,7 +81,7 @@ func (c *Controller) Start() error {
if err != nil { if err != nil {
return fmt.Errorf("add users error: %s", err) 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() c.initTask()
return nil return nil
} }
@ -93,38 +90,23 @@ func (c *Controller) Start() error {
func (c *Controller) Close() error { func (c *Controller) Close() error {
limiter.DeleteLimiter(c.Tag) limiter.DeleteLimiter(c.Tag)
if c.nodeInfoMonitorPeriodic != nil { if c.nodeInfoMonitorPeriodic != nil {
err := c.nodeInfoMonitorPeriodic.Close() c.nodeInfoMonitorPeriodic.Close()
if err != nil {
return fmt.Errorf("node info periodic close error: %s", err)
}
} }
if c.nodeInfoMonitorPeriodic != nil { if c.userReportPeriodic != nil {
err := c.userReportPeriodic.Close() c.userReportPeriodic.Close()
if err != nil {
return fmt.Errorf("user report periodic close error: %s", err)
}
} }
if c.renewCertPeriodic != nil { if c.renewCertPeriodic != nil {
err := c.renewCertPeriodic.Close() c.renewCertPeriodic.Close()
if err != nil {
return fmt.Errorf("renew cert periodic close error: %s", err)
}
} }
if c.dynamicSpeedLimitPeriodic != nil { if c.dynamicSpeedLimitPeriodic != nil {
err := c.dynamicSpeedLimitPeriodic.Close() c.dynamicSpeedLimitPeriodic.Close()
if err != nil {
return fmt.Errorf("dynamic speed limit periodic close error: %s", err)
}
} }
if c.onlineIpReportPeriodic != nil { if c.onlineIpReportPeriodic != nil {
err := c.onlineIpReportPeriodic.Close() c.onlineIpReportPeriodic.Close()
if err != nil {
return fmt.Errorf("online ip report periodic close error: %s", err)
}
} }
return nil return nil
} }
func (c *Controller) buildNodeTag() string { 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)
} }

View File

@ -16,7 +16,8 @@ type Lego struct {
} }
func New(config *conf.CertConfig) (*Lego, error) { 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)), fmt.Sprintf("user-%s.json", config.Email)),
config.Email) config.Email)
if err != nil { if err != nil {

View File

@ -1,101 +1,100 @@
package node package node
import ( import (
"fmt" "github.com/Yuzuki616/V2bX/common/task"
"log"
"runtime"
"time"
vCore "github.com/Yuzuki616/V2bX/core" vCore "github.com/Yuzuki616/V2bX/core"
"github.com/Yuzuki616/V2bX/api/panel"
"github.com/Yuzuki616/V2bX/limiter" "github.com/Yuzuki616/V2bX/limiter"
"github.com/Yuzuki616/V2bX/node/lego" log "github.com/sirupsen/logrus"
"github.com/xtls/xray-core/common/task" "time"
) )
func (c *Controller) initTask() { func (c *Controller) initTask() {
// fetch node info task // fetch node info task
c.nodeInfoMonitorPeriodic = &task.Periodic{ c.nodeInfoMonitorPeriodic = &task.Task{
Interval: c.nodeInfo.PullInterval, Interval: c.nodeInfo.PullInterval,
Execute: c.nodeInfoMonitor, Execute: c.nodeInfoMonitor,
} }
// fetch user list task // fetch user list task
c.userReportPeriodic = &task.Periodic{ c.userReportPeriodic = &task.Task{
Interval: c.nodeInfo.PushInterval, 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 // delay to start nodeInfoMonitor
go func() { _ = c.nodeInfoMonitorPeriodic.Start(false)
time.Sleep(c.nodeInfo.PullInterval) log.WithField("tag", c.Tag).Info("Start report node status")
_ = c.nodeInfoMonitorPeriodic.Start() _ = c.userReportPeriodic.Start(false)
}() if c.nodeInfo.Tls {
log.Printf("[%s: %d] Start report node status", c.nodeInfo.Type, c.nodeInfo.Id) switch c.CertConfig.CertMode {
// delay to start userReport case "reality", "none", "":
go func() { default:
time.Sleep(c.nodeInfo.PullInterval) c.renewCertPeriodic = &task.Task{
_ = c.userReportPeriodic.Start() Interval: time.Hour * 24,
}() Execute: c.reportUserTrafficTask,
if c.nodeInfo.Tls && c.CertConfig.CertMode != "none" && }
(c.CertConfig.CertMode == "dns" || c.CertConfig.CertMode == "http") { log.WithField("tag", c.Tag).Info("Start renew cert")
c.renewCertPeriodic = &task.Periodic{ // delay to start renewCert
Interval: time.Hour * 24, _ = c.renewCertPeriodic.Start(true)
Execute: c.reportUserTraffic,
} }
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) { func (c *Controller) nodeInfoMonitor() (err error) {
// First fetch Node Info // get node info
newNodeInfo, err := c.apiClient.GetNodeInfo() newNodeInfo, err := c.apiClient.GetNodeInfo()
if err != nil { 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 return nil
} }
var nodeInfoChanged = false
// If nodeInfo changed
if newNodeInfo != nil { if newNodeInfo != nil {
// nodeInfo changed
// Remove old tag // Remove old tag
oldTag := c.Tag log.WithField("tag", c.Tag).Info("Node changed, reload")
err := c.server.DelNode(oldTag) err = c.server.DelNode(c.Tag)
if err != nil { if err != nil {
log.Print(err) log.WithFields(log.Fields{
"tag": c.Tag,
"err": err,
}).Error("Delete node failed")
return nil return nil
} }
// Remove Old limiter // Remove Old limiter
limiter.DeleteLimiter(oldTag) limiter.DeleteLimiter(c.Tag)
// Add new tag // Add new Limiter
c.nodeInfo = newNodeInfo
c.Tag = c.buildNodeTag() c.Tag = c.buildNodeTag()
err = c.server.AddNode(c.Tag, newNodeInfo, c.ControllerConfig) l := limiter.AddLimiter(c.Tag, &c.LimitConfig, newUserInfo)
if err != nil { // check cert
log.Print(err) if newNodeInfo.Tls || newNodeInfo.Type == "hysteria" {
return nil
}
if newNodeInfo.Tls {
err = c.requestCert() err = c.requestCert()
if err != nil { 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 // add new node
} err = c.server.AddNode(c.Tag, newNodeInfo, c.ControllerConfig)
// Update User if err != nil {
newUserInfo, err := c.apiClient.GetUserList() log.WithFields(log.Fields{
if err != nil { "tag": c.Tag,
log.Print(err) "err": err,
return nil }).Error("Add node failed")
} return nil
if nodeInfoChanged { }
c.userList = newUserInfo
// Add new Limiter
l := limiter.AddLimiter(c.Tag, &c.LimitConfig, newUserInfo)
_, err = c.server.AddUsers(&vCore.AddUsersParams{ _, err = c.server.AddUsers(&vCore.AddUsersParams{
Tag: c.Tag, Tag: c.Tag,
Config: c.ControllerConfig, Config: c.ControllerConfig,
@ -103,109 +102,82 @@ func (c *Controller) nodeInfoMonitor() (err error) {
NodeInfo: newNodeInfo, NodeInfo: newNodeInfo,
}) })
if err != nil { if err != nil {
log.Print(err) log.WithFields(log.Fields{
"tag": c.Tag,
"err": err,
}).Error("Add users failed")
return nil return nil
} }
err = l.UpdateRule(newNodeInfo.Rules) err = l.UpdateRule(newNodeInfo.Rules)
if err != nil { 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 // Check interval
if c.nodeInfoMonitorPeriodic.Interval != newNodeInfo.PullInterval && if c.nodeInfoMonitorPeriodic.Interval != newNodeInfo.PullInterval &&
newNodeInfo.PullInterval != 0 { newNodeInfo.PullInterval != 0 {
c.nodeInfoMonitorPeriodic.Interval = newNodeInfo.PullInterval c.nodeInfoMonitorPeriodic.Interval = newNodeInfo.PullInterval
_ = c.nodeInfoMonitorPeriodic.Close() c.nodeInfoMonitorPeriodic.Close()
go func() { _ = c.nodeInfoMonitorPeriodic.Start(false)
time.Sleep(c.nodeInfoMonitorPeriodic.Interval)
_ = c.nodeInfoMonitorPeriodic.Start()
}()
} }
if c.userReportPeriodic.Interval != newNodeInfo.PushInterval && if c.userReportPeriodic.Interval != newNodeInfo.PushInterval &&
newNodeInfo.PushInterval != 0 { newNodeInfo.PushInterval != 0 {
c.userReportPeriodic.Interval = newNodeInfo.PullInterval c.userReportPeriodic.Interval = newNodeInfo.PullInterval
_ = c.userReportPeriodic.Close() c.userReportPeriodic.Close()
go func() { _ = c.userReportPeriodic.Start(false)
time.Sleep(c.userReportPeriodic.Interval)
_ = c.userReportPeriodic.Start()
}()
} }
} else { c.nodeInfo = newNodeInfo
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.userList = newUserInfo 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) { // node no changed, check users
// Get User traffic deleted, added := compareUserList(c.userList, newUserInfo)
userTraffic := make([]panel.UserTraffic, 0) if len(deleted) > 0 {
for i := range c.userList { // have deleted users
up, down := c.server.GetUserTraffic(c.Tag, c.userList[i].Uuid, true) err = c.server.DelUsers(deleted, c.Tag)
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)
if err != nil { if err != nil {
log.Printf("Report user traffic faild: %s", err) log.WithFields(log.Fields{
} else { "tag": c.Tag,
log.Printf("[%s: %d] Report %d online users", c.nodeInfo.Type, c.nodeInfo.Id, len(userTraffic)) "err": err,
}).Error("Delete users failed")
return nil
} }
} }
userTraffic = nil if len(added) > 0 {
runtime.GC() // 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 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
}
}

View File

@ -2,9 +2,42 @@ package node
import ( import (
"github.com/Yuzuki616/V2bX/api/panel" "github.com/Yuzuki616/V2bX/api/panel"
log "github.com/sirupsen/logrus"
"runtime"
"strconv" "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) { func compareUserList(old, new []panel.UserInfo) (deleted, added []panel.UserInfo) {
tmp := map[string]struct{}{} tmp := map[string]struct{}{}
tmp2 := map[string]struct{}{} tmp2 := map[string]struct{}{}