mirror of
https://github.com/wyx2685/V2bX.git
synced 2025-01-22 09:58:14 -05:00
Merge remote-tracking branch 'origin/dev' into dev
# Conflicts: # conf/node.go
This commit is contained in:
commit
251702c06a
@ -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内核支持
|
||||
- [ ] 完善使用文档
|
||||
- [ ] 尽可能统一日志输出格式
|
||||
|
||||
## 软件安装
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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))
|
||||
}
|
||||
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
78
common/task/task.go
Normal file
78
common/task/task.go
Normal 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
15
common/task/task_test.go
Normal 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)
|
||||
}
|
50
conf/conf.go
50
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
|
||||
}
|
||||
|
52
conf/node.go
52
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"`
|
||||
}
|
||||
|
52
conf/watch.go
Normal file
52
conf/watch.go
Normal 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
|
||||
}
|
@ -26,7 +26,7 @@ func NewCore(c *conf.CoreConfig) (Core, error) {
|
||||
}
|
||||
cs = append(cs, core1)
|
||||
}
|
||||
return &Selecter{
|
||||
return &Selector{
|
||||
cores: cs,
|
||||
}, nil
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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 {}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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()...)
|
@ -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"
|
||||
|
@ -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":
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
20
go.mod
20
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
|
||||
|
41
go.sum
41
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=
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
14
node/cert.go
14
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")
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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 {
|
||||
|
264
node/task.go
264
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
|
||||
}
|
||||
}
|
||||
|
33
node/user.go
33
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{}{}
|
||||
|
Loading…
Reference in New Issue
Block a user