mirror of
https://github.com/XrayR-project/XrayR.git
synced 2025-07-30 06:04:31 +00:00
Compare commits
72 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
dbde0c24fc | ||
![]() |
1bbd061d42 | ||
![]() |
793f26e763 | ||
![]() |
175e46d0b7 | ||
![]() |
9261a6063d | ||
![]() |
5ee2679924 | ||
![]() |
06fe198243 | ||
![]() |
b3f31bf06b | ||
![]() |
0010a876f3 | ||
![]() |
dbed635aae | ||
![]() |
561f317e24 | ||
![]() |
c24a2a28f2 | ||
![]() |
fa80ed8014 | ||
![]() |
5409cb2dc3 | ||
![]() |
b8b3a16d3b | ||
![]() |
d1e5762937 | ||
![]() |
4f1dafa8ed | ||
![]() |
aae0f9b24d | ||
![]() |
aee8ca2980 | ||
![]() |
933e745a70 | ||
![]() |
515fc708dc | ||
![]() |
5e134967fd | ||
![]() |
4a234d50e2 | ||
![]() |
115d7bad6f | ||
![]() |
5ba0624bbc | ||
![]() |
4439afa29b | ||
![]() |
03c9fe4218 | ||
![]() |
f648aedc91 | ||
![]() |
1720fbdc4c | ||
![]() |
cd7907c4c9 | ||
![]() |
296ba86bdd | ||
![]() |
43e6ec85cd | ||
![]() |
7ba2d999f0 | ||
![]() |
53141f4416 | ||
![]() |
e378f0a367 | ||
![]() |
d01a4265cd | ||
![]() |
94d2c7c23a | ||
![]() |
3de7600a4c | ||
![]() |
a0378b6cff | ||
![]() |
4688833bc7 | ||
![]() |
294a2dfacb | ||
![]() |
5780731cd3 | ||
![]() |
911b0c2ff5 | ||
![]() |
fa7fb7087f | ||
![]() |
0a6bca9755 | ||
![]() |
8562c798a9 | ||
![]() |
56199afa90 | ||
![]() |
c688cf02b4 | ||
![]() |
c170272f40 | ||
![]() |
6ffbe599b4 | ||
![]() |
78c2e31bdf | ||
![]() |
c04d52330d | ||
![]() |
6f4bf62113 | ||
![]() |
56e993ce43 | ||
![]() |
6d3d6f9e53 | ||
![]() |
76076b2d6a | ||
![]() |
4e48fdbe61 | ||
![]() |
0f3a4a0008 | ||
![]() |
d9971b2181 | ||
![]() |
4d1ed21dc7 | ||
![]() |
9a03f85930 | ||
![]() |
11b46ea485 | ||
![]() |
dc3d256d85 | ||
![]() |
287c30e7d0 | ||
![]() |
97d89549dd | ||
![]() |
cac4288e07 | ||
![]() |
d1d5193cec | ||
![]() |
85607527a5 | ||
![]() |
fd0a23bf6c | ||
![]() |
b1bfd04895 | ||
![]() |
edf02307ad | ||
![]() |
551e2d4299 |
22
.github/workflows/release.yml
vendored
22
.github/workflows/release.yml
vendored
@@ -26,12 +26,10 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
# Include amd64 on all platforms.
|
||||
goos: [ windows, freebsd, openbsd, linux, dragonfly, darwin ]
|
||||
goos: [ windows, freebsd, openbsd, linux, darwin ]
|
||||
goarch: [ amd64, 386 ]
|
||||
exclude:
|
||||
# Exclude i386 on darwin and dragonfly.
|
||||
- goarch: 386
|
||||
goos: dragonfly
|
||||
# Exclude i386 on darwin.
|
||||
- goarch: 386
|
||||
goos: darwin
|
||||
- goarch: 386
|
||||
@@ -122,12 +120,12 @@ jobs:
|
||||
- name: Build XrayR
|
||||
run: |
|
||||
mkdir -p build_assets
|
||||
go build -v -o build_assets/XrayR -trimpath -ldflags "-s -w -buildid=" ./main
|
||||
go build -v -o build_assets/XrayR -trimpath -ldflags "-s -w -buildid="
|
||||
|
||||
- name: Build Mips softfloat XrayR
|
||||
if: matrix.goarch == 'mips' || matrix.goarch == 'mipsle'
|
||||
run: |
|
||||
GOMIPS=softfloat go build -v -o build_assets/XrayR_softfloat -trimpath -ldflags "-s -w -buildid=" ./main
|
||||
GOMIPS=softfloat go build -v -o build_assets/XrayR_softfloat -trimpath -ldflags "-s -w -buildid="
|
||||
- name: Rename Windows XrayR
|
||||
if: matrix.goos == 'windows'
|
||||
run: |
|
||||
@@ -143,12 +141,12 @@ jobs:
|
||||
command: |
|
||||
cp ${GITHUB_WORKSPACE}/README.md ./build_assets/README.md
|
||||
cp ${GITHUB_WORKSPACE}/LICENSE ./build_assets/LICENSE
|
||||
cp ${GITHUB_WORKSPACE}/main/dns.json ./build_assets/dns.json
|
||||
cp ${GITHUB_WORKSPACE}/main/route.json ./build_assets/route.json
|
||||
cp ${GITHUB_WORKSPACE}/main/custom_outbound.json ./build_assets/custom_outbound.json
|
||||
cp ${GITHUB_WORKSPACE}/main/custom_inbound.json ./build_assets/custom_inbound.json
|
||||
cp ${GITHUB_WORKSPACE}/main/rulelist ./build_assets/rulelist
|
||||
cp ${GITHUB_WORKSPACE}/main/config.yml.example ./build_assets/config.yml
|
||||
cp ${GITHUB_WORKSPACE}/release/config/dns.json ./build_assets/dns.json
|
||||
cp ${GITHUB_WORKSPACE}/release/config/route.json ./build_assets/route.json
|
||||
cp ${GITHUB_WORKSPACE}/release/config/custom_outbound.json ./build_assets/custom_outbound.json
|
||||
cp ${GITHUB_WORKSPACE}/release/config/custom_inbound.json ./build_assets/custom_inbound.json
|
||||
cp ${GITHUB_WORKSPACE}/release/config/rulelist ./build_assets/rulelist
|
||||
cp ${GITHUB_WORKSPACE}/release/config/config.yml.example ./build_assets/config.yml
|
||||
LIST=('geoip geoip geoip' 'domain-list-community dlc geosite')
|
||||
for i in "${LIST[@]}"
|
||||
do
|
||||
|
38
.gitignore
vendored
38
.gitignore
vendored
@@ -1,18 +1,20 @@
|
||||
main/main
|
||||
main/XrayR
|
||||
main/XrayR*
|
||||
main/mytest
|
||||
main/access.logo
|
||||
main/error.log
|
||||
api/chooseparser.go.bak
|
||||
common/Inboundbuilder/.lego/
|
||||
common/legocmd/.lego/
|
||||
.vscode/launch.json
|
||||
main/.lego
|
||||
main/cert
|
||||
main/config.yml
|
||||
./vscode
|
||||
.idea/*
|
||||
.DS_Store
|
||||
*.bak
|
||||
go.work*
|
||||
.idea
|
||||
*.iml
|
||||
out
|
||||
gen
|
||||
*.exe
|
||||
*.exe~
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
*.test
|
||||
*.out
|
||||
go.work
|
||||
main
|
||||
XrayR
|
||||
XrayR*
|
||||
access.log
|
||||
error.log
|
||||
.lego
|
||||
cert
|
||||
config.yml
|
@@ -1,10 +1,10 @@
|
||||
# Build go
|
||||
FROM golang:1.21-alpine AS builder
|
||||
FROM golang:1.22.0-alpine AS builder
|
||||
WORKDIR /app
|
||||
COPY . .
|
||||
ENV CGO_ENABLED=0
|
||||
RUN go mod download
|
||||
RUN go build -v -o XrayR -trimpath -ldflags "-s -w -buildid=" ./main
|
||||
RUN go build -v -o XrayR -trimpath -ldflags "-s -w -buildid="
|
||||
|
||||
# Release
|
||||
FROM alpine
|
||||
|
@@ -64,6 +64,7 @@ This project is just my personal learning and development and maintenance. I do
|
||||
| [PMPanel](https://github.com/ByteInternetHK/PMPanel) | √ | √ | √ |
|
||||
| [ProxyPanel](https://github.com/ProxyPanel/ProxyPanel) | √ | √ | √ |
|
||||
| [WHMCS (V2RaySocks)](https://v2raysocks.doxtex.com/) | √ | √ | √ |
|
||||
| [BunPanel](https://github.com/pennyMorant/bunpanel-release) | √ | √ | √ |
|
||||
|
||||
## Software Installation
|
||||
|
||||
|
@@ -61,6 +61,7 @@ Dự án này chỉ là học tập và phát triển và bảo trì cá nhân c
|
||||
| [PMPanel](https://github.com/ByteInternetHK/PMPanel) | √ | √ | √ |
|
||||
| [ProxyPanel](https://github.com/ProxyPanel/ProxyPanel) | √ | √ | √ |
|
||||
| [WHMCS (V2RaySocks)](https://v2raysocks.doxtex.com/) | √ | √ | √ |
|
||||
| [BunPanel](https://github.com/pennyMorant/bunpanel-release) | √ | √ | √ |
|
||||
|
||||
## Cài đặt phần mềm
|
||||
|
||||
|
@@ -63,6 +63,7 @@ A Xray backend framework that can easily support many panels.
|
||||
| [ProxyPanel](https://github.com/ProxyPanel/ProxyPanel) | √ | √ | √ |
|
||||
| [WHMCS (V2RaySocks)](https://v2raysocks.doxtex.com/) | √ | √ | √ |
|
||||
| [GoV2Panel](https://github.com/pingProMax/gov2panel) | √ | √ | √ |
|
||||
| [BunPanel](https://github.com/pennyMorant/bunpanel-release) | √ | √ | √ |
|
||||
|
||||
## 软件安装
|
||||
|
||||
|
@@ -59,6 +59,7 @@
|
||||
| [PMPanel](https://github.com/ByteInternetHK/PMPanel) | √ | √ | √ |
|
||||
| [ProxyPanel](https://github.com/ProxyPanel/ProxyPanel) | √ | √ | √ |
|
||||
| [WHMCS (V2RaySocks)](https://v2raysocks.doxtex.com/) | √ | √ | √ |
|
||||
| [BunPanel](https://github.com/pennyMorant/bunpanel-release) | √ | √ | √ |
|
||||
|
||||
## نصب نرم افزار
|
||||
|
||||
|
@@ -37,25 +37,47 @@ type NodeStatus struct {
|
||||
}
|
||||
|
||||
type NodeInfo struct {
|
||||
NodeType string // Must be V2ray, Trojan, and Shadowsocks
|
||||
NodeID int
|
||||
Port uint32
|
||||
SpeedLimit uint64 // Bps
|
||||
AlterID uint16
|
||||
TransportProtocol string
|
||||
FakeType string
|
||||
Host string
|
||||
Path string
|
||||
EnableTLS bool
|
||||
EnableVless bool
|
||||
VlessFlow string
|
||||
CypherMethod string
|
||||
ServerKey string
|
||||
ServiceName string
|
||||
Header json.RawMessage
|
||||
NameServerConfig []*conf.NameServerConfig
|
||||
EnableREALITY bool
|
||||
REALITYConfig *REALITYConfig
|
||||
AcceptProxyProtocol bool
|
||||
Authority string
|
||||
NodeType string // Must be V2ray, Trojan, and Shadowsocks
|
||||
NodeID int
|
||||
Port uint32
|
||||
SpeedLimit uint64 // Bps
|
||||
AlterID uint16
|
||||
TransportProtocol string
|
||||
FakeType string
|
||||
Host string
|
||||
Path string
|
||||
EnableTLS bool
|
||||
EnableSniffing bool
|
||||
RouteOnly bool
|
||||
EnableVless bool
|
||||
VlessFlow string
|
||||
CypherMethod string
|
||||
ServerKey string
|
||||
ServiceName string
|
||||
Method string
|
||||
Header json.RawMessage
|
||||
HttpHeaders map[string]*conf.StringList
|
||||
Headers map[string]string
|
||||
NameServerConfig []*conf.NameServerConfig
|
||||
EnableREALITY bool
|
||||
REALITYConfig *REALITYConfig
|
||||
Show bool
|
||||
EnableTFO bool
|
||||
Dest string
|
||||
ProxyProtocolVer uint64
|
||||
ServerNames []string
|
||||
PrivateKey string
|
||||
MinClientVer string
|
||||
MaxClientVer string
|
||||
MaxTimeDiff uint64
|
||||
ShortIds []string
|
||||
Xver uint64
|
||||
Flow string
|
||||
Security string
|
||||
Key string
|
||||
RejectUnknownSni bool
|
||||
}
|
||||
|
||||
type UserInfo struct {
|
||||
|
427
api/bunpanel/bunpanel.go
Normal file
427
api/bunpanel/bunpanel.go
Normal file
@@ -0,0 +1,427 @@
|
||||
package bunpanel
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/go-resty/resty/v2"
|
||||
|
||||
"github.com/XrayR-project/XrayR/api"
|
||||
)
|
||||
|
||||
type APIClient struct {
|
||||
client *resty.Client
|
||||
APIHost string
|
||||
NodeID int
|
||||
Key string
|
||||
NodeType string
|
||||
EnableVless bool
|
||||
VlessFlow string
|
||||
SpeedLimit float64
|
||||
DeviceLimit int
|
||||
LocalRuleList []api.DetectRule
|
||||
LastReportOnline map[int]int
|
||||
access sync.Mutex
|
||||
eTags map[string]string
|
||||
}
|
||||
|
||||
// ReportIllegal implements api.API.
|
||||
func (*APIClient) ReportIllegal(detectResultList *[]api.DetectResult) (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReportNodeStatus implements api.API.
|
||||
func (*APIClient) ReportNodeStatus(nodeStatus *api.NodeStatus) (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetNodeRule implements api.API.
|
||||
func (c *APIClient) GetNodeRule() (*[]api.DetectRule, error) {
|
||||
ruleList := c.LocalRuleList
|
||||
return &ruleList, nil
|
||||
}
|
||||
|
||||
func New(apiConfig *api.Config) *APIClient {
|
||||
client := resty.New()
|
||||
client.SetRetryCount(3)
|
||||
if apiConfig.Timeout > 0 {
|
||||
client.SetTimeout(time.Duration(apiConfig.Timeout) * time.Second)
|
||||
} else {
|
||||
client.SetTimeout(5 * time.Second)
|
||||
}
|
||||
client.OnError(func(req *resty.Request, err error) {
|
||||
if v, ok := err.(*resty.ResponseError); ok {
|
||||
// v.Response contains the last response from the server
|
||||
// v.Err contains the original error
|
||||
log.Print(v.Err)
|
||||
}
|
||||
})
|
||||
client.SetBaseURL(apiConfig.APIHost)
|
||||
// Create Key for each requests
|
||||
client.SetQueryParams(map[string]string{
|
||||
"serverId": strconv.Itoa(apiConfig.NodeID),
|
||||
"nodeType": strings.ToLower(apiConfig.NodeType),
|
||||
"token": apiConfig.Key,
|
||||
})
|
||||
// Read local rule list
|
||||
localRuleList := readLocalRuleList(apiConfig.RuleListPath)
|
||||
apiClient := &APIClient{
|
||||
client: client,
|
||||
NodeID: apiConfig.NodeID,
|
||||
Key: apiConfig.Key,
|
||||
APIHost: apiConfig.APIHost,
|
||||
NodeType: apiConfig.NodeType,
|
||||
EnableVless: apiConfig.EnableVless,
|
||||
VlessFlow: apiConfig.VlessFlow,
|
||||
SpeedLimit: apiConfig.SpeedLimit,
|
||||
DeviceLimit: apiConfig.DeviceLimit,
|
||||
LocalRuleList: localRuleList,
|
||||
eTags: make(map[string]string),
|
||||
}
|
||||
return apiClient
|
||||
}
|
||||
|
||||
// readLocalRuleList reads the local rule list file
|
||||
func readLocalRuleList(path string) (LocalRuleList []api.DetectRule) {
|
||||
LocalRuleList = make([]api.DetectRule, 0)
|
||||
|
||||
if path != "" {
|
||||
// open the file
|
||||
file, err := os.Open(path)
|
||||
|
||||
// handle errors while opening
|
||||
if err != nil {
|
||||
log.Printf("Error when opening file: %s", err)
|
||||
return LocalRuleList
|
||||
}
|
||||
defer file.Close()
|
||||
fileScanner := bufio.NewScanner(file)
|
||||
|
||||
// read line by line
|
||||
for fileScanner.Scan() {
|
||||
LocalRuleList = append(LocalRuleList, api.DetectRule{
|
||||
ID: -1,
|
||||
Pattern: regexp.MustCompile(fileScanner.Text()),
|
||||
})
|
||||
}
|
||||
// handle first encountered error while reading
|
||||
if err := fileScanner.Err(); err != nil {
|
||||
log.Fatalf("Error while reading file: %s", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return LocalRuleList
|
||||
}
|
||||
|
||||
// Describe return a description of the client
|
||||
func (c *APIClient) Describe() api.ClientInfo {
|
||||
return api.ClientInfo{APIHost: c.APIHost, NodeID: c.NodeID, Key: c.Key, NodeType: c.NodeType}
|
||||
}
|
||||
|
||||
// Debug set the client debug for client
|
||||
func (c *APIClient) Debug() {
|
||||
c.client.SetDebug(true)
|
||||
}
|
||||
|
||||
func (c *APIClient) assembleURL(path string) string {
|
||||
return c.APIHost + path
|
||||
}
|
||||
|
||||
func (c *APIClient) parseResponse(res *resty.Response, path string, err error) (*Response, error) {
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("request %s failed: %s", c.assembleURL(path), err)
|
||||
}
|
||||
|
||||
if res.StatusCode() > 400 {
|
||||
body := res.Body()
|
||||
return nil, fmt.Errorf("request %s failed: %s, %v", c.assembleURL(path), string(body), err)
|
||||
}
|
||||
response := res.Result().(*Response)
|
||||
|
||||
if response.StatusCode != 200 {
|
||||
res, _ := json.Marshal(&response)
|
||||
return nil, fmt.Errorf("statusCode %s invalid", string(res))
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (c *APIClient) GetNodeInfo() (nodeInfo *api.NodeInfo, err error) {
|
||||
path := fmt.Sprintf("/v2/server/%d/get", c.NodeID)
|
||||
res, err := c.client.R().
|
||||
SetResult(&Response{}).
|
||||
SetHeader("If-None-Match", c.eTags["node"]).
|
||||
ForceContentType("application/json").
|
||||
Get(path)
|
||||
// Etag identifier for a specific version of a resource. StatusCode = 304 means no changed
|
||||
if res.StatusCode() == 304 {
|
||||
return nil, errors.New(api.NodeNotModified)
|
||||
}
|
||||
|
||||
if res.Header().Get("ETag") != "" && res.Header().Get("ETag") != c.eTags["node"] {
|
||||
c.eTags["node"] = res.Header().Get("ETag")
|
||||
}
|
||||
|
||||
response, err := c.parseResponse(res, path, err)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
nodeInfoResponse := new(Server)
|
||||
|
||||
if err := json.Unmarshal(response.Datas, nodeInfoResponse); err != nil {
|
||||
return nil, fmt.Errorf("unmarshal %s failed: %s", reflect.TypeOf(nodeInfoResponse), err)
|
||||
}
|
||||
|
||||
nodeInfo, err = c.ParseNodeInfo(nodeInfoResponse)
|
||||
if err != nil {
|
||||
res, _ := json.Marshal(nodeInfoResponse)
|
||||
return nil, fmt.Errorf("parse node info failed: %s, \nError: %s, \nPlease check the doc of custom_config for help: https://xrayr-project.github.io/XrayR-doc/dui-jie-sspanel/sspanel/sspanel_custom_config", string(res), err)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
res, _ := json.Marshal(nodeInfoResponse)
|
||||
return nil, fmt.Errorf("parse node info failed: %s, \nError: %s", string(res), err)
|
||||
}
|
||||
|
||||
return nodeInfo, nil
|
||||
}
|
||||
|
||||
func (c *APIClient) GetUserList() (UserList *[]api.UserInfo, err error) {
|
||||
path := "/v2/user/get"
|
||||
res, err := c.client.R().
|
||||
SetQueryParam("serverId", strconv.Itoa(c.NodeID)).
|
||||
SetHeader("If-None-Match", c.eTags["users"]).
|
||||
SetResult(&Response{}).
|
||||
ForceContentType("application/json").
|
||||
Get(path)
|
||||
// Etag identifier for a specific version of a resource. StatusCode = 304 means no changed
|
||||
if res.StatusCode() == 304 {
|
||||
return nil, errors.New(api.UserNotModified)
|
||||
}
|
||||
|
||||
if res.Header().Get("ETag") != "" && res.Header().Get("ETag") != c.eTags["users"] {
|
||||
c.eTags["users"] = res.Header().Get("ETag")
|
||||
}
|
||||
|
||||
response, err := c.parseResponse(res, path, err)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
userListResponse := new([]User)
|
||||
|
||||
if err := json.Unmarshal(response.Datas, userListResponse); err != nil {
|
||||
return nil, fmt.Errorf("unmarshal %s failed: %s", reflect.TypeOf(userListResponse), err)
|
||||
}
|
||||
userList, err := c.ParseUserListResponse(userListResponse)
|
||||
if err != nil {
|
||||
res, _ := json.Marshal(userListResponse)
|
||||
return nil, fmt.Errorf("parse user list failed: %s", string(res))
|
||||
}
|
||||
return userList, nil
|
||||
}
|
||||
|
||||
func (c *APIClient) ReportNodeOnlineUsers(onlineUserList *[]api.OnlineUser) error {
|
||||
c.access.Lock()
|
||||
defer c.access.Unlock()
|
||||
|
||||
reportOnline := make(map[int]int)
|
||||
data := make([]OnlineUser, len(*onlineUserList))
|
||||
for i, user := range *onlineUserList {
|
||||
data[i] = OnlineUser{UID: user.UID, IP: user.IP}
|
||||
reportOnline[user.UID]++
|
||||
}
|
||||
c.LastReportOnline = reportOnline // Update LastReportOnline
|
||||
|
||||
postData := &PostData{Data: data}
|
||||
path := "/v2/user/online/create"
|
||||
res, err := c.client.R().
|
||||
SetQueryParam("serverId", strconv.Itoa(c.NodeID)).
|
||||
SetBody(postData).
|
||||
SetResult(&Response{}).
|
||||
ForceContentType("application/json").
|
||||
Post(path)
|
||||
|
||||
_, err = c.parseResponse(res, path, err)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *APIClient) ReportUserTraffic(userTraffic *[]api.UserTraffic) error {
|
||||
|
||||
data := make([]UserTraffic, len(*userTraffic))
|
||||
for i, traffic := range *userTraffic {
|
||||
data[i] = UserTraffic{
|
||||
UID: traffic.UID,
|
||||
Upload: traffic.Upload,
|
||||
Download: traffic.Download}
|
||||
}
|
||||
postData := &PostData{Data: data}
|
||||
path := "/v2/user/data-usage/create"
|
||||
res, err := c.client.R().
|
||||
SetQueryParam("serverId", strconv.Itoa(c.NodeID)).
|
||||
SetBody(postData).
|
||||
SetResult(&Response{}).
|
||||
ForceContentType("application/json").
|
||||
Post(path)
|
||||
_, err = c.parseResponse(res, path, err)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *APIClient) ParseUserListResponse(userInfoResponse *[]User) (*[]api.UserInfo, error) {
|
||||
c.access.Lock()
|
||||
// Clear Last report log
|
||||
defer func() {
|
||||
c.LastReportOnline = make(map[int]int)
|
||||
c.access.Unlock()
|
||||
}()
|
||||
|
||||
var deviceLimit, localDeviceLimit = 0, 0
|
||||
var speedLimit uint64 = 0
|
||||
var userList []api.UserInfo
|
||||
for _, user := range *userInfoResponse {
|
||||
if c.DeviceLimit > 0 {
|
||||
deviceLimit = c.DeviceLimit
|
||||
} else {
|
||||
deviceLimit = user.DeviceLimit
|
||||
}
|
||||
|
||||
// If there is still device available, add the user
|
||||
if deviceLimit > 0 && user.AliveIP > 0 {
|
||||
lastOnline := 0
|
||||
if v, ok := c.LastReportOnline[user.ID]; ok {
|
||||
lastOnline = v
|
||||
}
|
||||
// If there are any available device.
|
||||
if localDeviceLimit = deviceLimit - user.AliveIP + lastOnline; localDeviceLimit > 0 {
|
||||
deviceLimit = localDeviceLimit
|
||||
// If this backend server has reported any user in the last reporting period.
|
||||
} else if lastOnline > 0 {
|
||||
deviceLimit = lastOnline
|
||||
// Remove this user.
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if c.SpeedLimit > 0 {
|
||||
speedLimit = uint64((c.SpeedLimit * 1000000) / 8)
|
||||
} else {
|
||||
speedLimit = uint64((user.SpeedLimit * 1000000) / 8)
|
||||
}
|
||||
userList = append(userList, api.UserInfo{
|
||||
UID: user.ID,
|
||||
UUID: user.UUID,
|
||||
SpeedLimit: speedLimit,
|
||||
DeviceLimit: deviceLimit,
|
||||
Passwd: user.UUID,
|
||||
Email: user.UUID + "@bunpanel.user",
|
||||
})
|
||||
}
|
||||
|
||||
return &userList, nil
|
||||
}
|
||||
|
||||
func (c *APIClient) ParseNodeInfo(nodeInfoResponse *Server) (*api.NodeInfo, error) {
|
||||
var (
|
||||
speedLimit uint64 = 0
|
||||
enableTLS, enableVless, enableREALITY bool
|
||||
alterID uint16 = 0
|
||||
tlsType, transportProtocol string
|
||||
)
|
||||
|
||||
nodeConfig := nodeInfoResponse
|
||||
port := uint32(nodeConfig.Port)
|
||||
|
||||
switch c.NodeType {
|
||||
case "Shadowsocks":
|
||||
transportProtocol = "tcp"
|
||||
case "V2ray":
|
||||
transportProtocol = nodeConfig.Network
|
||||
tlsType = nodeConfig.Security
|
||||
|
||||
if tlsType == "tls" || tlsType == "xtls" {
|
||||
enableTLS = true
|
||||
}
|
||||
if tlsType == "reality" {
|
||||
enableREALITY = true
|
||||
enableVless = true
|
||||
}
|
||||
case "Trojan":
|
||||
enableTLS = true
|
||||
tlsType = "tls"
|
||||
transportProtocol = "tcp"
|
||||
}
|
||||
|
||||
// parse reality config
|
||||
realityConfig := new(api.REALITYConfig)
|
||||
if nodeConfig.RealitySettings != nil {
|
||||
r := new(RealitySettings)
|
||||
json.Unmarshal(nodeConfig.RealitySettings, r)
|
||||
realityConfig = &api.REALITYConfig{
|
||||
Dest: r.Dest,
|
||||
ProxyProtocolVer: r.ProxyProtocolVer,
|
||||
ServerNames: r.ServerNames,
|
||||
PrivateKey: r.PrivateKey,
|
||||
MinClientVer: r.MinClientVer,
|
||||
MaxClientVer: r.MaxClientVer,
|
||||
MaxTimeDiff: r.MaxTimeDiff,
|
||||
ShortIds: r.ShortIds,
|
||||
}
|
||||
}
|
||||
wsConfig := new(WsSettings)
|
||||
if nodeConfig.WsSettings != nil {
|
||||
json.Unmarshal(nodeConfig.WsSettings, wsConfig)
|
||||
}
|
||||
|
||||
grpcConfig := new(GrpcSettigns)
|
||||
if nodeConfig.GrpcSettings != nil {
|
||||
json.Unmarshal(nodeConfig.GrpcSettings, grpcConfig)
|
||||
}
|
||||
|
||||
tcpConfig := new(TcpSettings)
|
||||
if nodeConfig.TcpSettings != nil {
|
||||
json.Unmarshal(nodeConfig.TcpSettings, tcpConfig)
|
||||
}
|
||||
|
||||
// Create GeneralNodeInfo
|
||||
nodeInfo := &api.NodeInfo{
|
||||
NodeType: c.NodeType,
|
||||
NodeID: c.NodeID,
|
||||
Port: port,
|
||||
SpeedLimit: speedLimit,
|
||||
AlterID: alterID,
|
||||
TransportProtocol: transportProtocol,
|
||||
Host: wsConfig.Headers.Host,
|
||||
Path: wsConfig.Path,
|
||||
EnableTLS: enableTLS,
|
||||
EnableVless: enableVless,
|
||||
VlessFlow: nodeConfig.Flow,
|
||||
CypherMethod: nodeConfig.Method,
|
||||
ServiceName: grpcConfig.ServiceName,
|
||||
Header: tcpConfig.Header,
|
||||
EnableREALITY: enableREALITY,
|
||||
REALITYConfig: realityConfig,
|
||||
}
|
||||
|
||||
return nodeInfo, nil
|
||||
}
|
101
api/bunpanel/bunpanel_test.go
Normal file
101
api/bunpanel/bunpanel_test.go
Normal file
@@ -0,0 +1,101 @@
|
||||
package bunpanel_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/XrayR-project/XrayR/api"
|
||||
"github.com/XrayR-project/XrayR/api/bunpanel"
|
||||
)
|
||||
|
||||
func CreateClient() api.API {
|
||||
apiConfig := &api.Config{
|
||||
APIHost: "http://localhost:8080",
|
||||
Key: "123456",
|
||||
NodeID: 1,
|
||||
NodeType: "V2ray",
|
||||
}
|
||||
client := bunpanel.New(apiConfig)
|
||||
return client
|
||||
}
|
||||
|
||||
func TestGetV2rayNodeInfo(t *testing.T) {
|
||||
client := CreateClient()
|
||||
nodeInfo, err := client.GetNodeInfo()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
t.Log(nodeInfo)
|
||||
}
|
||||
|
||||
func TestGetSSNodeInfo(t *testing.T) {
|
||||
apiConfig := &api.Config{
|
||||
APIHost: "http://127.0.0.1:668",
|
||||
Key: "qwertyuiopasdfghjkl",
|
||||
NodeID: 1,
|
||||
NodeType: "Shadowsocks",
|
||||
}
|
||||
client := bunpanel.New(apiConfig)
|
||||
nodeInfo, err := client.GetNodeInfo()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
t.Log(nodeInfo)
|
||||
}
|
||||
|
||||
func TestGetTrojanNodeInfo(t *testing.T) {
|
||||
apiConfig := &api.Config{
|
||||
APIHost: "http://127.0.0.1:668",
|
||||
Key: "qwertyuiopasdfghjkl",
|
||||
NodeID: 1,
|
||||
NodeType: "Trojan",
|
||||
}
|
||||
client := bunpanel.New(apiConfig)
|
||||
nodeInfo, err := client.GetNodeInfo()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
t.Log(nodeInfo)
|
||||
}
|
||||
|
||||
func TestGetUserList(t *testing.T) {
|
||||
client := CreateClient()
|
||||
|
||||
userList, err := client.GetUserList()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
t.Log(userList)
|
||||
}
|
||||
|
||||
func TestReportReportUserTraffic(t *testing.T) {
|
||||
client := CreateClient()
|
||||
userList, err := client.GetUserList()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
generalUserTraffic := make([]api.UserTraffic, len(*userList))
|
||||
for i, userInfo := range *userList {
|
||||
generalUserTraffic[i] = api.UserTraffic{
|
||||
UID: userInfo.UID,
|
||||
Upload: 1111,
|
||||
Download: 2222,
|
||||
}
|
||||
}
|
||||
// client.Debug()
|
||||
err = client.ReportUserTraffic(&generalUserTraffic)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetNodeRule(t *testing.T) {
|
||||
client := CreateClient()
|
||||
client.Debug()
|
||||
ruleList, err := client.GetNodeRule()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
t.Log(ruleList)
|
||||
}
|
72
api/bunpanel/model.go
Normal file
72
api/bunpanel/model.go
Normal file
@@ -0,0 +1,72 @@
|
||||
package bunpanel
|
||||
|
||||
import "encoding/json"
|
||||
|
||||
type Server struct {
|
||||
Port int `json:"serverPort"`
|
||||
Network string `json:"network"`
|
||||
Method string `json:"method"`
|
||||
Security string `json:"security"`
|
||||
Flow string `json:"flow"`
|
||||
WsSettings json.RawMessage `json:"wsSettings"`
|
||||
RealitySettings json.RawMessage `json:"realitySettings"`
|
||||
GrpcSettings json.RawMessage `json:"grpcSettings"`
|
||||
TcpSettings json.RawMessage `json:"tcpSettings"`
|
||||
}
|
||||
|
||||
type WsSettings struct {
|
||||
Path string `json:"path"`
|
||||
Headers struct {
|
||||
Host string `json:"Host"`
|
||||
} `json:"headers"`
|
||||
}
|
||||
|
||||
type GrpcSettigns struct {
|
||||
ServiceName string `json:"serviceName"`
|
||||
}
|
||||
|
||||
type TcpSettings struct {
|
||||
Header json.RawMessage `json:"header"`
|
||||
}
|
||||
|
||||
type RealitySettings struct {
|
||||
Show bool `json:"show"`
|
||||
Dest string `json:"dest"`
|
||||
Xver uint64 `json:"xver"`
|
||||
ServerNames []string `json:"serverNames"`
|
||||
PrivateKey string `json:"privateKey"`
|
||||
MinClientVer string `json:"minClientVer"`
|
||||
MaxClientVer string `json:"maxClientVer"`
|
||||
MaxTimeDiff uint64 `json:"maxTimeDiff"`
|
||||
ProxyProtocolVer uint64 `json:"proxyProtocolVer"`
|
||||
ShortIds []string `json:"shortIds"`
|
||||
}
|
||||
|
||||
type User struct {
|
||||
ID int `json:"id"`
|
||||
UUID string `json:"uuid"`
|
||||
SpeedLimit float64 `json:"speedLimit"`
|
||||
DeviceLimit int `json:"ipLimit"`
|
||||
AliveIP int `json:"onlineIp"`
|
||||
}
|
||||
|
||||
type OnlineUser struct {
|
||||
UID int `json:"userId"`
|
||||
IP string `json:"ip"`
|
||||
}
|
||||
|
||||
// UserTraffic is the data structure of traffic
|
||||
type UserTraffic struct {
|
||||
UID int `json:"userId"`
|
||||
Upload int64 `json:"u"`
|
||||
Download int64 `json:"d"`
|
||||
}
|
||||
|
||||
type Response struct {
|
||||
StatusCode int `json:"statusCode"`
|
||||
Datas json.RawMessage `json:"datas"`
|
||||
}
|
||||
|
||||
type PostData struct {
|
||||
Data interface{} `json:"data"`
|
||||
}
|
@@ -5,7 +5,6 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"regexp"
|
||||
"strconv"
|
||||
@@ -13,6 +12,8 @@ import (
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/bitly/go-simplejson"
|
||||
"github.com/go-resty/resty/v2"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
@@ -176,7 +177,7 @@ func (c *APIClient) GetNodeInfo() (nodeInfo *api.NodeInfo, err error) {
|
||||
c.resp.Store(server)
|
||||
|
||||
switch c.NodeType {
|
||||
case "V2ray":
|
||||
case "V2ray", "Vmess", "Vless":
|
||||
nodeInfo, err = c.parseV2rayNodeResponse(server)
|
||||
case "Trojan":
|
||||
nodeInfo, err = c.parseTrojanNodeResponse(server)
|
||||
@@ -199,7 +200,7 @@ func (c *APIClient) GetUserList() (UserList *[]api.UserInfo, err error) {
|
||||
path := "/api/server/user"
|
||||
|
||||
switch c.NodeType {
|
||||
case "V2ray", "Trojan", "Shadowsocks":
|
||||
case "V2ray", "Trojan", "Shadowsocks", "Vmess", "Vless":
|
||||
break
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported node type: %s", c.NodeType)
|
||||
|
@@ -35,6 +35,21 @@ type v2ray struct {
|
||||
ServiceName string `json:"serviceName"`
|
||||
Header *json.RawMessage `json:"header"`
|
||||
} `json:"networkSettings"`
|
||||
VlessNetworkSettings struct {
|
||||
Path string `json:"path"`
|
||||
Headers *json.RawMessage `json:"headers"`
|
||||
ServiceName string `json:"serviceName"`
|
||||
Header *json.RawMessage `json:"header"`
|
||||
} `json:"network_settings"`
|
||||
VlessFlow string `json:"flow"`
|
||||
VlessTlsSettings struct {
|
||||
ServerPort string `json:"server_port"`
|
||||
Dest string `json:"dest"`
|
||||
xVer uint64 `json:"xver"`
|
||||
Sni string `json:"server_name"`
|
||||
PrivateKey string `json:"private_key"`
|
||||
ShortId string `json:"short_id"`
|
||||
} `json:"tls_settings"`
|
||||
Tls int `json:"tls"`
|
||||
}
|
||||
|
||||
|
@@ -5,7 +5,6 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"regexp"
|
||||
"strconv"
|
||||
@@ -13,6 +12,8 @@ import (
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/bitly/go-simplejson"
|
||||
"github.com/go-resty/resty/v2"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
@@ -54,10 +55,18 @@ func New(apiConfig *api.Config) *APIClient {
|
||||
}
|
||||
})
|
||||
client.SetBaseURL(apiConfig.APIHost)
|
||||
|
||||
var nodeType string
|
||||
|
||||
if apiConfig.NodeType == "V2ray" && apiConfig.EnableVless {
|
||||
nodeType = "vless"
|
||||
} else {
|
||||
nodeType = strings.ToLower(apiConfig.NodeType)
|
||||
}
|
||||
// Create Key for each requests
|
||||
client.SetQueryParams(map[string]string{
|
||||
"node_id": strconv.Itoa(apiConfig.NodeID),
|
||||
"node_type": strings.ToLower(apiConfig.NodeType),
|
||||
"node_type": nodeType,
|
||||
"token": apiConfig.Key,
|
||||
})
|
||||
// Read local rule list
|
||||
@@ -175,7 +184,7 @@ func (c *APIClient) GetNodeInfo() (nodeInfo *api.NodeInfo, err error) {
|
||||
c.resp.Store(server)
|
||||
|
||||
switch c.NodeType {
|
||||
case "V2ray":
|
||||
case "V2ray", "Vmess", "Vless":
|
||||
nodeInfo, err = c.parseV2rayNodeResponse(server)
|
||||
case "Trojan":
|
||||
nodeInfo, err = c.parseTrojanNodeResponse(server)
|
||||
@@ -198,7 +207,7 @@ func (c *APIClient) GetUserList() (UserList *[]api.UserInfo, err error) {
|
||||
path := "/api/v1/server/UniProxy/user"
|
||||
|
||||
switch c.NodeType {
|
||||
case "V2ray", "Trojan", "Shadowsocks":
|
||||
case "V2ray", "Trojan", "Shadowsocks", "Vmess", "Vless":
|
||||
break
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported node type: %s", c.NodeType)
|
||||
@@ -355,11 +364,37 @@ func (c *APIClient) parseSSNodeResponse(s *serverConfig) (*api.NodeInfo, error)
|
||||
// parseV2rayNodeResponse parse the response for the given nodeInfo format
|
||||
func (c *APIClient) parseV2rayNodeResponse(s *serverConfig) (*api.NodeInfo, error) {
|
||||
var (
|
||||
host string
|
||||
header json.RawMessage
|
||||
enableTLS bool
|
||||
host string
|
||||
header json.RawMessage
|
||||
enableTLS bool
|
||||
enableREALITY bool
|
||||
dest string
|
||||
xVer uint64
|
||||
)
|
||||
|
||||
if s.VlessTlsSettings.Dest != "" {
|
||||
dest = s.VlessTlsSettings.Dest
|
||||
} else {
|
||||
dest = s.VlessTlsSettings.Sni
|
||||
}
|
||||
if s.VlessTlsSettings.xVer != 0 {
|
||||
xVer = s.VlessTlsSettings.xVer
|
||||
} else {
|
||||
xVer = 0
|
||||
}
|
||||
|
||||
realityConfig := api.REALITYConfig{
|
||||
Dest: dest + ":" + s.VlessTlsSettings.ServerPort,
|
||||
ProxyProtocolVer: xVer,
|
||||
ServerNames: []string{s.VlessTlsSettings.Sni},
|
||||
PrivateKey: s.VlessTlsSettings.PrivateKey,
|
||||
ShortIds: []string{s.VlessTlsSettings.ShortId},
|
||||
}
|
||||
|
||||
if c.EnableVless {
|
||||
s.NetworkSettings = s.VlessNetworkSettings
|
||||
}
|
||||
|
||||
switch s.Network {
|
||||
case "ws":
|
||||
if s.NetworkSettings.Headers != nil {
|
||||
@@ -380,8 +415,16 @@ func (c *APIClient) parseV2rayNodeResponse(s *serverConfig) (*api.NodeInfo, erro
|
||||
}
|
||||
}
|
||||
|
||||
if s.Tls == 1 {
|
||||
switch s.Tls {
|
||||
case 0:
|
||||
enableTLS = false
|
||||
enableREALITY = false
|
||||
case 1:
|
||||
enableTLS = true
|
||||
enableREALITY = false
|
||||
case 2:
|
||||
enableTLS = true
|
||||
enableREALITY = true
|
||||
}
|
||||
|
||||
// Create GeneralNodeInfo
|
||||
@@ -395,9 +438,11 @@ func (c *APIClient) parseV2rayNodeResponse(s *serverConfig) (*api.NodeInfo, erro
|
||||
Path: s.NetworkSettings.Path,
|
||||
Host: host,
|
||||
EnableVless: c.EnableVless,
|
||||
VlessFlow: c.VlessFlow,
|
||||
VlessFlow: s.VlessFlow,
|
||||
ServiceName: s.NetworkSettings.ServiceName,
|
||||
Header: header,
|
||||
EnableREALITY: enableREALITY,
|
||||
REALITYConfig: &realityConfig,
|
||||
NameServerConfig: s.parseDNSConfig(),
|
||||
}, nil
|
||||
}
|
||||
|
@@ -4,13 +4,14 @@ import (
|
||||
"bufio"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/go-resty/resty/v2"
|
||||
|
||||
"github.com/XrayR-project/XrayR/api"
|
||||
|
@@ -4,13 +4,14 @@ import (
|
||||
"bufio"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/go-resty/resty/v2"
|
||||
|
||||
"github.com/XrayR-project/XrayR/api"
|
||||
@@ -144,7 +145,7 @@ func (c *APIClient) parseResponse(res *resty.Response, path string, err error) (
|
||||
func (c *APIClient) GetNodeInfo() (nodeInfo *api.NodeInfo, err error) {
|
||||
var path string
|
||||
switch c.NodeType {
|
||||
case "V2ray":
|
||||
case "V2ray", "Vmess", "Vless":
|
||||
path = fmt.Sprintf("/api/v2ray/v1/node/%d", c.NodeID)
|
||||
case "Trojan":
|
||||
path = fmt.Sprintf("/api/trojan/v1/node/%d", c.NodeID)
|
||||
@@ -165,7 +166,7 @@ func (c *APIClient) GetNodeInfo() (nodeInfo *api.NodeInfo, err error) {
|
||||
}
|
||||
|
||||
switch c.NodeType {
|
||||
case "V2ray":
|
||||
case "V2ray", "Vmess", "Vless":
|
||||
nodeInfo, err = c.ParseV2rayNodeResponse(&response.Data)
|
||||
case "Trojan":
|
||||
nodeInfo, err = c.ParseTrojanNodeResponse(&response.Data)
|
||||
@@ -177,7 +178,7 @@ func (c *APIClient) GetNodeInfo() (nodeInfo *api.NodeInfo, err error) {
|
||||
|
||||
if err != nil {
|
||||
res, _ := json.Marshal(response.Data)
|
||||
return nil, fmt.Errorf("Parse node info failed: %s, \nError: %s", string(res), err)
|
||||
return nil, fmt.Errorf("parse node info failed: %s, \nError: %s", string(res), err)
|
||||
}
|
||||
|
||||
return nodeInfo, nil
|
||||
@@ -187,7 +188,7 @@ func (c *APIClient) GetNodeInfo() (nodeInfo *api.NodeInfo, err error) {
|
||||
func (c *APIClient) GetUserList() (UserList *[]api.UserInfo, err error) {
|
||||
var path string
|
||||
switch c.NodeType {
|
||||
case "V2ray":
|
||||
case "V2ray", "Vmess", "Vless":
|
||||
path = fmt.Sprintf("/api/v2ray/v1/userList/%d", c.NodeID)
|
||||
case "Trojan":
|
||||
path = fmt.Sprintf("/api/trojan/v1/userList/%d", c.NodeID)
|
||||
@@ -208,7 +209,7 @@ func (c *APIClient) GetUserList() (UserList *[]api.UserInfo, err error) {
|
||||
}
|
||||
userList := new([]api.UserInfo)
|
||||
switch c.NodeType {
|
||||
case "V2ray":
|
||||
case "V2ray", "Vmess", "Vless":
|
||||
userList, err = c.ParseV2rayUserListResponse(&response.Data)
|
||||
case "Trojan":
|
||||
userList, err = c.ParseTrojanUserListResponse(&response.Data)
|
||||
@@ -228,7 +229,7 @@ func (c *APIClient) GetUserList() (UserList *[]api.UserInfo, err error) {
|
||||
func (c *APIClient) ReportNodeStatus(nodeStatus *api.NodeStatus) (err error) {
|
||||
var path string
|
||||
switch c.NodeType {
|
||||
case "V2ray":
|
||||
case "V2ray", "Vmess", "Vless":
|
||||
path = fmt.Sprintf("/api/v2ray/v1/nodeStatus/%d", c.NodeID)
|
||||
case "Trojan":
|
||||
path = fmt.Sprintf("/api/trojan/v1/nodeStatus/%d", c.NodeID)
|
||||
@@ -264,7 +265,7 @@ func (c *APIClient) ReportNodeOnlineUsers(onlineUserList *[]api.OnlineUser) erro
|
||||
|
||||
var path string
|
||||
switch c.NodeType {
|
||||
case "V2ray":
|
||||
case "V2ray", "Vmess", "Vless":
|
||||
path = fmt.Sprintf("/api/v2ray/v1/nodeOnline/%d", c.NodeID)
|
||||
case "Trojan":
|
||||
path = fmt.Sprintf("/api/trojan/v1/nodeOnline/%d", c.NodeID)
|
||||
@@ -297,7 +298,7 @@ func (c *APIClient) ReportNodeOnlineUsers(onlineUserList *[]api.OnlineUser) erro
|
||||
func (c *APIClient) ReportUserTraffic(userTraffic *[]api.UserTraffic) error {
|
||||
var path string
|
||||
switch c.NodeType {
|
||||
case "V2ray":
|
||||
case "V2ray", "Vmess", "Vless":
|
||||
path = fmt.Sprintf("/api/v2ray/v1/userTraffic/%d", c.NodeID)
|
||||
case "Trojan":
|
||||
path = fmt.Sprintf("/api/trojan/v1/userTraffic/%d", c.NodeID)
|
||||
@@ -332,7 +333,7 @@ func (c *APIClient) ReportUserTraffic(userTraffic *[]api.UserTraffic) error {
|
||||
func (c *APIClient) GetNodeRule() (*[]api.DetectRule, error) {
|
||||
var path string
|
||||
switch c.NodeType {
|
||||
case "V2ray":
|
||||
case "V2ray", "Vmess", "Vless":
|
||||
path = fmt.Sprintf("/api/v2ray/v1/nodeRule/%d", c.NodeID)
|
||||
case "Trojan":
|
||||
path = fmt.Sprintf("/api/trojan/v1/nodeRule/%d", c.NodeID)
|
||||
@@ -380,7 +381,7 @@ func (c *APIClient) GetNodeRule() (*[]api.DetectRule, error) {
|
||||
func (c *APIClient) ReportIllegal(detectResultList *[]api.DetectResult) error {
|
||||
var path string
|
||||
switch c.NodeType {
|
||||
case "V2ray":
|
||||
case "V2ray", "Vmess", "Vless":
|
||||
path = fmt.Sprintf("/api/v2ray/v1/trigger/%d", c.NodeID)
|
||||
case "Trojan":
|
||||
path = fmt.Sprintf("/api/trojan/v1/trigger/%d", c.NodeID)
|
||||
|
@@ -154,8 +154,8 @@ func TestReportIllegal(t *testing.T) {
|
||||
client := CreateClient()
|
||||
|
||||
detectResult := []api.DetectResult{
|
||||
{1, 1},
|
||||
{1, 2},
|
||||
{UID: 1, RuleID: 1},
|
||||
{UID: 1, RuleID: 2},
|
||||
}
|
||||
client.Debug()
|
||||
err := client.ReportIllegal(&detectResult)
|
||||
|
@@ -5,7 +5,6 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"reflect"
|
||||
"regexp"
|
||||
@@ -14,6 +13,8 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/go-resty/resty/v2"
|
||||
|
||||
"github.com/XrayR-project/XrayR/api"
|
||||
@@ -94,8 +95,13 @@ func readLocalRuleList(path string) (LocalRuleList []api.DetectRule) {
|
||||
if path != "" {
|
||||
// open the file
|
||||
file, err := os.Open(path)
|
||||
defer file.Close()
|
||||
|
||||
defer func(file *os.File) {
|
||||
err := file.Close()
|
||||
if err != nil {
|
||||
log.Printf("Error when closing file: %s", err)
|
||||
}
|
||||
}(file)
|
||||
// handle errors while opening
|
||||
if err != nil {
|
||||
log.Printf("Error when opening file: %s", err)
|
||||
@@ -142,7 +148,7 @@ func (c *APIClient) parseResponse(res *resty.Response, path string, err error) (
|
||||
|
||||
if res.StatusCode() > 400 {
|
||||
body := res.Body()
|
||||
return nil, fmt.Errorf("request %s failed: %s, %s", c.assembleURL(path), string(body), err)
|
||||
return nil, fmt.Errorf("request %s failed: %s, %v", c.assembleURL(path), string(body), err)
|
||||
}
|
||||
response := res.Result().(*Response)
|
||||
|
||||
@@ -209,13 +215,13 @@ func (c *APIClient) GetNodeInfo() (nodeInfo *api.NodeInfo, err error) {
|
||||
nodeInfo, err = c.ParseSSPanelNodeInfo(nodeInfoResponse)
|
||||
if err != nil {
|
||||
res, _ := json.Marshal(nodeInfoResponse)
|
||||
return nil, fmt.Errorf("Parse node info failed: %s, \nError: %s, \nPlease check the doc of custom_config for help: https://xrayr-project.github.io/XrayR-doc/dui-jie-sspanel/sspanel/sspanel_custom_config", string(res), err)
|
||||
return nil, fmt.Errorf("parse node info failed: %s, \nError: %s, \nPlease check the doc of custom_config for help: https://xrayr-project.github.io/XrayR-doc/dui-jie-sspanel/sspanel/sspanel_custom_config", string(res), err)
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
res, _ := json.Marshal(nodeInfoResponse)
|
||||
return nil, fmt.Errorf("Parse node info failed: %s, \nError: %s", string(res), err)
|
||||
return nil, fmt.Errorf("parse node info failed: %s, \nError: %s", string(res), err)
|
||||
}
|
||||
|
||||
return nodeInfo, nil
|
||||
@@ -290,16 +296,12 @@ func (c *APIClient) ReportNodeOnlineUsers(onlineUserList *[]api.OnlineUser) erro
|
||||
data := make([]OnlineUser, len(*onlineUserList))
|
||||
for i, user := range *onlineUserList {
|
||||
data[i] = OnlineUser{UID: user.UID, IP: user.IP}
|
||||
if _, ok := reportOnline[user.UID]; ok {
|
||||
reportOnline[user.UID]++
|
||||
} else {
|
||||
reportOnline[user.UID] = 1
|
||||
}
|
||||
reportOnline[user.UID]++ // will start from 1 if key doesn’t exist
|
||||
}
|
||||
c.LastReportOnline = reportOnline // Update LastReportOnline
|
||||
|
||||
postData := &PostData{Data: data}
|
||||
path := fmt.Sprintf("/mod_mu/users/aliveip")
|
||||
path := "/mod_mu/users/aliveip"
|
||||
res, err := c.client.R().
|
||||
SetQueryParam("node_id", strconv.Itoa(c.NodeID)).
|
||||
SetBody(postData).
|
||||
@@ -473,7 +475,7 @@ func (c *APIClient) ParseV2rayNodeResponse(nodeInfoResponse *NodeInfoResponse) (
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("marshal Header Type %s into config fialed: %s", header, err)
|
||||
return nil, fmt.Errorf("marshal Header Type %s into config failed: %s", header, err)
|
||||
}
|
||||
|
||||
// Create GeneralNodeInfo
|
||||
@@ -519,6 +521,11 @@ func (c *APIClient) ParseSSNodeResponse(nodeInfoResponse *NodeInfoResponse) (*ap
|
||||
return nil, fmt.Errorf("unmarshal %s failed: %s", reflect.TypeOf(userListResponse), err)
|
||||
}
|
||||
|
||||
// init server port
|
||||
if len(*userListResponse) != 0 {
|
||||
port = (*userListResponse)[0].Port
|
||||
}
|
||||
|
||||
if c.SpeedLimit > 0 {
|
||||
speedLimit = uint64((c.SpeedLimit * 1000000) / 8)
|
||||
} else {
|
||||
@@ -682,7 +689,7 @@ func (c *APIClient) ParseUserListResponse(userInfoResponse *[]UserResponse) (*[]
|
||||
c.access.Unlock()
|
||||
}()
|
||||
|
||||
var deviceLimit, localDeviceLimit int = 0, 0
|
||||
var deviceLimit, localDeviceLimit = 0, 0
|
||||
var speedLimit uint64 = 0
|
||||
var userList []api.UserInfo
|
||||
for _, user := range *userInfoResponse {
|
||||
@@ -733,10 +740,10 @@ func (c *APIClient) ParseUserListResponse(userInfoResponse *[]UserResponse) (*[]
|
||||
// Only available for SSPanel version >= 2021.11
|
||||
func (c *APIClient) ParseSSPanelNodeInfo(nodeInfoResponse *NodeInfoResponse) (*api.NodeInfo, error) {
|
||||
var (
|
||||
speedLimit uint64 = 0
|
||||
enableTLS, enableVless bool
|
||||
alterID uint16 = 0
|
||||
tlsType, transportProtocol string
|
||||
speedLimit uint64 = 0
|
||||
enableTLS, enableVless bool
|
||||
alterID uint16 = 0
|
||||
transportProtocol string
|
||||
)
|
||||
|
||||
// Check if custom_config is null
|
||||
@@ -768,8 +775,8 @@ func (c *APIClient) ParseSSPanelNodeInfo(nodeInfoResponse *NodeInfoResponse) (*a
|
||||
transportProtocol = "tcp"
|
||||
case "V2ray":
|
||||
transportProtocol = nodeConfig.Network
|
||||
tlsType = nodeConfig.Security
|
||||
|
||||
tlsType := nodeConfig.Security
|
||||
if tlsType == "tls" || tlsType == "xtls" {
|
||||
enableTLS = true
|
||||
}
|
||||
@@ -779,13 +786,8 @@ func (c *APIClient) ParseSSPanelNodeInfo(nodeInfoResponse *NodeInfoResponse) (*a
|
||||
}
|
||||
case "Trojan":
|
||||
enableTLS = true
|
||||
tlsType = "tls"
|
||||
transportProtocol = "tcp"
|
||||
|
||||
if nodeConfig.Security != "" {
|
||||
tlsType = nodeConfig.Security // try to read security from config
|
||||
}
|
||||
|
||||
// Select transport protocol
|
||||
if nodeConfig.Network != "" {
|
||||
transportProtocol = nodeConfig.Network // try to read transport protocol from config
|
||||
|
@@ -148,8 +148,8 @@ func TestReportIllegal(t *testing.T) {
|
||||
client := CreateClient()
|
||||
|
||||
detectResult := []api.DetectResult{
|
||||
{1, 2},
|
||||
{1, 3},
|
||||
{UID: 1, RuleID: 2},
|
||||
{UID: 1, RuleID: 3},
|
||||
}
|
||||
client.Debug()
|
||||
err := client.ReportIllegal(&detectResult)
|
||||
|
@@ -5,3 +5,27 @@ type UserTraffic struct {
|
||||
Upload int64 `json:"u"`
|
||||
Download int64 `json:"d"`
|
||||
}
|
||||
|
||||
type NodeStatus struct {
|
||||
CPU string `json:"cpu"`
|
||||
Mem string `json:"mem"`
|
||||
Net string `json:"net"`
|
||||
Disk string `json:"disk"`
|
||||
Uptime int `json:"uptime"`
|
||||
}
|
||||
|
||||
type NodeOnline struct {
|
||||
UID int `json:"uid"`
|
||||
IP string `json:"ip"`
|
||||
}
|
||||
|
||||
type REALITYConfig struct {
|
||||
Dest string `json:"dest,omitempty"`
|
||||
ProxyProtocolVer uint64 `json:"proxy_protocol_ver,omitempty"`
|
||||
ServerNames []string `json:"server_names,omitempty"`
|
||||
PrivateKey string `json:"private_key,omitempty"`
|
||||
MinClientVer string `json:"min_client_ver,omitempty"`
|
||||
MaxClientVer string `json:"max_client_ver,omitempty"`
|
||||
MaxTimeDiff uint64 `json:"max_time_diff,omitempty"`
|
||||
ShortIds []string `json:"short_ids,omitempty"`
|
||||
}
|
@@ -3,8 +3,8 @@ package v2raysocks
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"regexp"
|
||||
"strconv"
|
||||
@@ -12,6 +12,8 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/bitly/go-simplejson"
|
||||
"github.com/go-resty/resty/v2"
|
||||
"github.com/sagernet/sing-shadowsocks/shadowaead_2022"
|
||||
@@ -34,6 +36,7 @@ type APIClient struct {
|
||||
LocalRuleList []api.DetectRule
|
||||
ConfigResp *simplejson.Json
|
||||
access sync.Mutex
|
||||
eTags map[string]string
|
||||
}
|
||||
|
||||
// New create an api instance
|
||||
@@ -46,13 +49,16 @@ func New(apiConfig *api.Config) *APIClient {
|
||||
} else {
|
||||
client.SetTimeout(5 * time.Second)
|
||||
}
|
||||
|
||||
client.OnError(func(req *resty.Request, err error) {
|
||||
if v, ok := err.(*resty.ResponseError); ok {
|
||||
var v *resty.ResponseError
|
||||
if errors.As(err, &v) {
|
||||
// v.Response contains the last response from the server
|
||||
// v.Err contains the original error
|
||||
log.Print(v.Err)
|
||||
}
|
||||
})
|
||||
|
||||
// Create Key for each requests
|
||||
client.SetQueryParams(map[string]string{
|
||||
"node_id": strconv.Itoa(apiConfig.NodeID),
|
||||
@@ -71,6 +77,7 @@ func New(apiConfig *api.Config) *APIClient {
|
||||
SpeedLimit: apiConfig.SpeedLimit,
|
||||
DeviceLimit: apiConfig.DeviceLimit,
|
||||
LocalRuleList: localRuleList,
|
||||
eTags: make(map[string]string),
|
||||
}
|
||||
return apiClient
|
||||
}
|
||||
@@ -144,12 +151,13 @@ func (c *APIClient) parseResponse(res *resty.Response, path string, err error) (
|
||||
func (c *APIClient) GetNodeInfo() (nodeInfo *api.NodeInfo, err error) {
|
||||
var nodeType string
|
||||
switch c.NodeType {
|
||||
case "V2ray", "Trojan", "Shadowsocks":
|
||||
case "V2ray", "Vmess", "Vless", "Trojan", "Shadowsocks":
|
||||
nodeType = strings.ToLower(c.NodeType)
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported Node type: %s", c.NodeType)
|
||||
}
|
||||
res, err := c.client.R().
|
||||
SetHeader("If-None-Match", c.eTags["config"]).
|
||||
SetQueryParams(map[string]string{
|
||||
"act": "config",
|
||||
"nodetype": nodeType,
|
||||
@@ -157,6 +165,15 @@ func (c *APIClient) GetNodeInfo() (nodeInfo *api.NodeInfo, err error) {
|
||||
ForceContentType("application/json").
|
||||
Get(c.APIHost)
|
||||
|
||||
// Etag identifier for a specific version of a resource. StatusCode = 304 means no changed
|
||||
if res.StatusCode() == 304 {
|
||||
return nil, errors.New(api.NodeNotModified)
|
||||
}
|
||||
// update etag
|
||||
if res.Header().Get("Etag") != "" && res.Header().Get("Etag") != c.eTags["config"] {
|
||||
c.eTags["config"] = res.Header().Get("Etag")
|
||||
}
|
||||
|
||||
response, err := c.parseResponse(res, "", err)
|
||||
c.access.Lock()
|
||||
defer c.access.Unlock()
|
||||
@@ -166,7 +183,7 @@ func (c *APIClient) GetNodeInfo() (nodeInfo *api.NodeInfo, err error) {
|
||||
}
|
||||
|
||||
switch c.NodeType {
|
||||
case "V2ray":
|
||||
case "V2ray", "Vmess", "Vless":
|
||||
nodeInfo, err = c.ParseV2rayNodeResponse(response)
|
||||
case "Trojan":
|
||||
nodeInfo, err = c.ParseTrojanNodeResponse(response)
|
||||
@@ -188,12 +205,13 @@ func (c *APIClient) GetNodeInfo() (nodeInfo *api.NodeInfo, err error) {
|
||||
func (c *APIClient) GetUserList() (UserList *[]api.UserInfo, err error) {
|
||||
var nodeType string
|
||||
switch c.NodeType {
|
||||
case "V2ray", "Trojan", "Shadowsocks":
|
||||
case "V2ray", "Vmess", "Vless", "Trojan", "Shadowsocks":
|
||||
nodeType = strings.ToLower(c.NodeType)
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported Node type: %s", c.NodeType)
|
||||
}
|
||||
res, err := c.client.R().
|
||||
SetHeader("If-None-Match", c.eTags["user"]).
|
||||
SetQueryParams(map[string]string{
|
||||
"act": "user",
|
||||
"nodetype": nodeType,
|
||||
@@ -201,6 +219,15 @@ func (c *APIClient) GetUserList() (UserList *[]api.UserInfo, err error) {
|
||||
ForceContentType("application/json").
|
||||
Get(c.APIHost)
|
||||
|
||||
// Etag identifier for a specific version of a resource. StatusCode = 304 means no changed
|
||||
if res.StatusCode() == 304 {
|
||||
return nil, errors.New(api.UserNotModified)
|
||||
}
|
||||
// update etag
|
||||
if res.Header().Get("Etag") != "" && res.Header().Get("Etag") != c.eTags["user"] {
|
||||
c.eTags["user"] = res.Header().Get("Etag")
|
||||
}
|
||||
|
||||
response, err := c.parseResponse(res, "", err)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -216,20 +243,27 @@ func (c *APIClient) GetUserList() (UserList *[]api.UserInfo, err error) {
|
||||
user.Passwd = response.Get("data").GetIndex(i).Get("shadowsocks_user").Get("secret").MustString()
|
||||
user.Method = response.Get("data").GetIndex(i).Get("shadowsocks_user").Get("cipher").MustString()
|
||||
user.SpeedLimit = response.Get("data").GetIndex(i).Get("shadowsocks_user").Get("speed_limit").MustUint64() * 1000000 / 8
|
||||
user.DeviceLimit = response.Get("data").GetIndex(i).Get("shadowsocks_user").Get("device_limit").MustInt()
|
||||
case "Trojan":
|
||||
user.UUID = response.Get("data").GetIndex(i).Get("trojan_user").Get("password").MustString()
|
||||
user.Email = response.Get("data").GetIndex(i).Get("trojan_user").Get("password").MustString()
|
||||
user.SpeedLimit = response.Get("data").GetIndex(i).Get("trojan_user").Get("speed_limit").MustUint64() * 1000000 / 8
|
||||
case "V2ray":
|
||||
user.DeviceLimit = response.Get("data").GetIndex(i).Get("trojan_user").Get("device_limit").MustInt()
|
||||
case "V2ray", "Vmess", "Vless":
|
||||
user.UUID = response.Get("data").GetIndex(i).Get("v2ray_user").Get("uuid").MustString()
|
||||
user.Email = response.Get("data").GetIndex(i).Get("v2ray_user").Get("email").MustString()
|
||||
user.AlterID = uint16(response.Get("data").GetIndex(i).Get("v2ray_user").Get("alter_id").MustUint64())
|
||||
user.SpeedLimit = response.Get("data").GetIndex(i).Get("v2ray_user").Get("speed_limit").MustUint64() * 1000000 / 8
|
||||
user.DeviceLimit = response.Get("data").GetIndex(i).Get("v2ray_user").Get("device_limit").MustInt()
|
||||
}
|
||||
if c.SpeedLimit > 0 {
|
||||
user.SpeedLimit = uint64((c.SpeedLimit * 1000000) / 8)
|
||||
}
|
||||
user.DeviceLimit = c.DeviceLimit
|
||||
|
||||
if c.DeviceLimit > 0 {
|
||||
user.DeviceLimit = c.DeviceLimit
|
||||
}
|
||||
|
||||
userList[i] = user
|
||||
}
|
||||
return &userList, nil
|
||||
@@ -265,11 +299,7 @@ func (c *APIClient) ReportUserTraffic(userTraffic *[]api.UserTraffic) error {
|
||||
// GetNodeRule implements the API interface
|
||||
func (c *APIClient) GetNodeRule() (*[]api.DetectRule, error) {
|
||||
ruleList := c.LocalRuleList
|
||||
if c.NodeType != "V2ray" {
|
||||
return &ruleList, nil
|
||||
}
|
||||
|
||||
// Only support the rule for v2ray
|
||||
// fix: reuse config response
|
||||
c.access.Lock()
|
||||
defer c.access.Unlock()
|
||||
@@ -287,11 +317,49 @@ func (c *APIClient) GetNodeRule() (*[]api.DetectRule, error) {
|
||||
|
||||
// ReportNodeStatus implements the API interface
|
||||
func (c *APIClient) ReportNodeStatus(nodeStatus *api.NodeStatus) (err error) {
|
||||
systemload := NodeStatus{
|
||||
Uptime: int(nodeStatus.Uptime),
|
||||
CPU: fmt.Sprintf("%d%%", int(nodeStatus.CPU)),
|
||||
Mem: fmt.Sprintf("%d%%", int(nodeStatus.Mem)),
|
||||
Disk: fmt.Sprintf("%d%%", int(nodeStatus.Disk)),
|
||||
}
|
||||
|
||||
res, err := c.client.R().
|
||||
SetQueryParam("node_id", strconv.Itoa(c.NodeID)).
|
||||
SetQueryParams(map[string]string{
|
||||
"act": "nodestatus",
|
||||
"nodetype": strings.ToLower(c.NodeType),
|
||||
}).
|
||||
SetBody(systemload).
|
||||
ForceContentType("application/json").
|
||||
Post(c.APIHost)
|
||||
_, err = c.parseResponse(res, "", err)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReportNodeOnlineUsers implements the API interface
|
||||
func (c *APIClient) ReportNodeOnlineUsers(onlineUserList *[]api.OnlineUser) error {
|
||||
data := make([]NodeOnline, len(*onlineUserList))
|
||||
for i, user := range *onlineUserList {
|
||||
data[i] = NodeOnline{UID: user.UID, IP: user.IP}
|
||||
}
|
||||
|
||||
res, err := c.client.R().
|
||||
SetQueryParam("node_id", strconv.Itoa(c.NodeID)).
|
||||
SetQueryParams(map[string]string{
|
||||
"act": "onlineusers",
|
||||
"nodetype": strings.ToLower(c.NodeType),
|
||||
}).
|
||||
SetBody(data).
|
||||
ForceContentType("application/json").
|
||||
Post(c.APIHost)
|
||||
_, err = c.parseResponse(res, "", err)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -333,14 +401,6 @@ func (c *APIClient) ParseSSNodeResponse(nodeInfoResponse *simplejson.Json) (*api
|
||||
// Shadowsocks 2022
|
||||
if C.Contains(shadowaead_2022.List, method) {
|
||||
serverPsk = inboundInfo.Get("settings").Get("password").MustString()
|
||||
} else {
|
||||
userInfo, err := c.GetUserList()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(*userInfo) > 0 {
|
||||
method = (*userInfo)[0].Method
|
||||
}
|
||||
}
|
||||
|
||||
// Create GeneralNodeInfo
|
||||
@@ -361,6 +421,8 @@ func (c *APIClient) ParseV2rayNodeResponse(nodeInfoResponse *simplejson.Json) (*
|
||||
var path, host, serviceName string
|
||||
var header json.RawMessage
|
||||
var enableTLS bool
|
||||
var enableVless bool
|
||||
var enableReality bool
|
||||
var alterID uint16 = 0
|
||||
|
||||
tmpInboundInfo := nodeInfoResponse.Get("inbounds").MustArray()
|
||||
@@ -388,10 +450,24 @@ func (c *APIClient) ParseV2rayNodeResponse(nodeInfoResponse *simplejson.Json) (*
|
||||
}
|
||||
|
||||
}
|
||||
if inboundInfo.Get("streamSettings").Get("security").MustString() == "tls" {
|
||||
enableTLS = true
|
||||
} else {
|
||||
enableTLS = false
|
||||
|
||||
enableTLS = inboundInfo.Get("streamSettings").Get("security").MustString() == "tls"
|
||||
enableVless = inboundInfo.Get("streamSettings").Get("security").MustString() == "reality"
|
||||
enableReality = enableVless
|
||||
|
||||
realityConfig := new(api.REALITYConfig)
|
||||
if enableVless {
|
||||
// parse reality config
|
||||
realityConfig = &api.REALITYConfig{
|
||||
Dest: inboundInfo.Get("streamSettings").Get("realitySettings").Get("dest").MustString(),
|
||||
ProxyProtocolVer: inboundInfo.Get("streamSettings").Get("realitySettings").Get("xver").MustUint64(),
|
||||
ServerNames: inboundInfo.Get("streamSettings").Get("realitySettings").Get("serverNames").MustStringArray(),
|
||||
PrivateKey: inboundInfo.Get("streamSettings").Get("realitySettings").Get("privateKey").MustString(),
|
||||
MinClientVer: inboundInfo.Get("streamSettings").Get("realitySettings").Get("minClientVer").MustString(),
|
||||
MaxClientVer: inboundInfo.Get("streamSettings").Get("realitySettings").Get("maxClientVer").MustString(),
|
||||
MaxTimeDiff: inboundInfo.Get("streamSettings").Get("realitySettings").Get("maxTimeDiff").MustUint64(),
|
||||
ShortIds: inboundInfo.Get("streamSettings").Get("realitySettings").Get("shortIds").MustStringArray(),
|
||||
}
|
||||
}
|
||||
|
||||
// Create GeneralNodeInfo
|
||||
@@ -405,10 +481,12 @@ func (c *APIClient) ParseV2rayNodeResponse(nodeInfoResponse *simplejson.Json) (*
|
||||
EnableTLS: enableTLS,
|
||||
Path: path,
|
||||
Host: host,
|
||||
EnableVless: c.EnableVless,
|
||||
EnableVless: enableVless,
|
||||
VlessFlow: c.VlessFlow,
|
||||
ServiceName: serviceName,
|
||||
Header: header,
|
||||
EnableREALITY: enableReality,
|
||||
REALITYConfig: realityConfig,
|
||||
}
|
||||
return nodeInfo, nil
|
||||
}
|
||||
|
@@ -240,10 +240,14 @@ func (d *DefaultDispatcher) Dispatch(ctx context.Context, destination net.Destin
|
||||
if !destination.IsValid() {
|
||||
panic("Dispatcher: Invalid destination.")
|
||||
}
|
||||
ob := &session.Outbound{
|
||||
Target: destination,
|
||||
outbounds := session.OutboundsFromContext(ctx)
|
||||
if len(outbounds) == 0 {
|
||||
outbounds = []*session.Outbound{{}}
|
||||
ctx = session.ContextWithOutbounds(ctx, outbounds)
|
||||
}
|
||||
ctx = session.ContextWithOutbound(ctx, ob)
|
||||
ob := outbounds[len(outbounds)-1]
|
||||
ob.OriginalTarget = destination
|
||||
ob.Target = destination
|
||||
content := session.ContentFromContext(ctx)
|
||||
if content == nil {
|
||||
content = new(session.Content)
|
||||
@@ -288,10 +292,14 @@ func (d *DefaultDispatcher) DispatchLink(ctx context.Context, destination net.De
|
||||
if !destination.IsValid() {
|
||||
return newError("Dispatcher: Invalid destination.")
|
||||
}
|
||||
ob := &session.Outbound{
|
||||
Target: destination,
|
||||
outbounds := session.OutboundsFromContext(ctx)
|
||||
if len(outbounds) == 0 {
|
||||
outbounds = []*session.Outbound{{}}
|
||||
ctx = session.ContextWithOutbounds(ctx, outbounds)
|
||||
}
|
||||
ctx = session.ContextWithOutbound(ctx, ob)
|
||||
ob := outbounds[len(outbounds)-1]
|
||||
ob.OriginalTarget = destination
|
||||
ob.Target = destination
|
||||
content := session.ContentFromContext(ctx)
|
||||
if content == nil {
|
||||
content = new(session.Content)
|
||||
@@ -374,7 +382,8 @@ func sniffer(ctx context.Context, cReader *cachedReader, metadataOnly bool, netw
|
||||
}
|
||||
|
||||
func (d *DefaultDispatcher) routedDispatch(ctx context.Context, link *transport.Link, destination net.Destination) {
|
||||
ob := session.OutboundFromContext(ctx)
|
||||
outbounds := session.OutboundsFromContext(ctx)
|
||||
ob := outbounds[len(outbounds)-1]
|
||||
if hosts, ok := d.dns.(dns.HostsLookup); ok && destination.Address.Family().IsDomain() {
|
||||
proxied := hosts.LookupHosts(ob.Target.String())
|
||||
if proxied != nil {
|
||||
|
@@ -26,7 +26,9 @@ func newFakeDNSSniffer(ctx context.Context) (protocolSnifferWithMetadata, error)
|
||||
return protocolSnifferWithMetadata{}, errNotInit
|
||||
}
|
||||
return protocolSnifferWithMetadata{protocolSniffer: func(ctx context.Context, bytes []byte) (SniffResult, error) {
|
||||
Target := session.OutboundFromContext(ctx).Target
|
||||
outbounds := session.OutboundsFromContext(ctx)
|
||||
ob := outbounds[len(outbounds)-1]
|
||||
Target := ob.Target
|
||||
if Target.Network == net.Network_TCP || Target.Network == net.Network_UDP {
|
||||
domainFromFakeDNS := fakeDNSEngine.GetDomainFromFakeDNS(Target.Address)
|
||||
if domainFromFakeDNS != "" {
|
||||
|
@@ -1,9 +1,7 @@
|
||||
package main
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path"
|
||||
@@ -12,36 +10,40 @@ import (
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/fsnotify/fsnotify"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
"github.com/XrayR-project/XrayR/panel"
|
||||
)
|
||||
|
||||
var (
|
||||
configFile = flag.String("config", "", "Config file for XrayR.")
|
||||
printVersion = flag.Bool("version", false, "show version")
|
||||
cfgFile string
|
||||
rootCmd = &cobra.Command{
|
||||
Use: "XrayR",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
if err := run(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
version = "0.9.1"
|
||||
codename = "XrayR"
|
||||
intro = "A Xray backend that supports many panels"
|
||||
)
|
||||
|
||||
func showVersion() {
|
||||
fmt.Printf("%s %s (%s) \n", codename, version, intro)
|
||||
func init() {
|
||||
rootCmd.PersistentFlags().StringVarP(&cfgFile, "config", "c", "", "Config file for XrayR.")
|
||||
}
|
||||
|
||||
func getConfig() *viper.Viper {
|
||||
config := viper.New()
|
||||
|
||||
// Set custom path and name
|
||||
if *configFile != "" {
|
||||
configName := path.Base(*configFile)
|
||||
configFileExt := path.Ext(*configFile)
|
||||
if cfgFile != "" {
|
||||
configName := path.Base(cfgFile)
|
||||
configFileExt := path.Ext(cfgFile)
|
||||
configNameOnly := strings.TrimSuffix(configName, configFileExt)
|
||||
configPath := path.Dir(*configFile)
|
||||
configPath := path.Dir(cfgFile)
|
||||
config.SetConfigName(configNameOnly)
|
||||
config.SetConfigType(strings.TrimPrefix(configFileExt, "."))
|
||||
config.AddConfigPath(configPath)
|
||||
@@ -65,18 +67,19 @@ func getConfig() *viper.Viper {
|
||||
return config
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
func run() error {
|
||||
showVersion()
|
||||
if *printVersion {
|
||||
return
|
||||
}
|
||||
|
||||
config := getConfig()
|
||||
panelConfig := &panel.Config{}
|
||||
if err := config.Unmarshal(panelConfig); err != nil {
|
||||
log.Panicf("Parse config file %v failed: %s \n", configFile, err)
|
||||
return fmt.Errorf("Parse config file %v failed: %s \n", cfgFile, err)
|
||||
}
|
||||
|
||||
if panelConfig.LogConfig.Level == "debug" {
|
||||
log.SetReportCaller(true)
|
||||
}
|
||||
|
||||
p := panel.New(panelConfig)
|
||||
lastTime := time.Now()
|
||||
config.OnConfigChange(func(e fsnotify.Event) {
|
||||
@@ -88,21 +91,31 @@ func main() {
|
||||
// Delete old instance and trigger GC
|
||||
runtime.GC()
|
||||
if err := config.Unmarshal(panelConfig); err != nil {
|
||||
log.Panicf("Parse config file %v failed: %s \n", configFile, err)
|
||||
log.Panicf("Parse config file %v failed: %s \n", cfgFile, err)
|
||||
}
|
||||
|
||||
if panelConfig.LogConfig.Level == "debug" {
|
||||
log.SetReportCaller(true)
|
||||
}
|
||||
|
||||
p.Start()
|
||||
lastTime = time.Now()
|
||||
}
|
||||
})
|
||||
|
||||
p.Start()
|
||||
defer p.Close()
|
||||
|
||||
// Explicitly triggering GC to remove garbage from config loading.
|
||||
runtime.GC()
|
||||
// Running backend
|
||||
{
|
||||
osSignals := make(chan os.Signal, 1)
|
||||
signal.Notify(osSignals, os.Interrupt, os.Kill, syscall.SIGTERM)
|
||||
<-osSignals
|
||||
}
|
||||
osSignals := make(chan os.Signal, 1)
|
||||
signal.Notify(osSignals, os.Interrupt, os.Kill, syscall.SIGTERM)
|
||||
<-osSignals
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func Execute() error {
|
||||
return rootCmd.Execute()
|
||||
}
|
27
cmd/version.go
Normal file
27
cmd/version.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var (
|
||||
version = "0.9.3"
|
||||
codename = "XrayR"
|
||||
intro = "A Xray backend that supports many panels"
|
||||
)
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(&cobra.Command{
|
||||
Use: "version",
|
||||
Short: "Print current version of XrayR",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
showVersion()
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func showVersion() {
|
||||
fmt.Printf("%s %s (%s) \n", codename, version, intro)
|
||||
}
|
66
cmd/x25519.go
Normal file
66
cmd/x25519.go
Normal file
@@ -0,0 +1,66 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"golang.org/x/crypto/curve25519"
|
||||
)
|
||||
|
||||
var (
|
||||
priKey string
|
||||
x25519Cmd = &cobra.Command{
|
||||
Use: "x25519",
|
||||
Short: "Generate key pair for x25519 key exchange",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
if err := x25519(); err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
x25519Cmd.PersistentFlags().StringVarP(&priKey, "input", "i", "", "Input private key (base64.RawURLEncoding)")
|
||||
rootCmd.AddCommand(x25519Cmd)
|
||||
}
|
||||
|
||||
func x25519() error {
|
||||
privateKey := make([]byte, curve25519.ScalarSize)
|
||||
|
||||
if priKey == "" {
|
||||
if _, err := rand.Read(privateKey); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
p, err := base64.RawURLEncoding.DecodeString(priKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(p) != curve25519.ScalarSize {
|
||||
return errors.New("invalid private key")
|
||||
}
|
||||
privateKey = p
|
||||
}
|
||||
|
||||
// Modify random bytes using algorithm described at:
|
||||
// https://cr.yp.to/ecdh.html.
|
||||
privateKey[0] &= 248
|
||||
privateKey[31] &= 127
|
||||
privateKey[31] |= 64
|
||||
|
||||
publicKey, err := curve25519.X25519(privateKey, curve25519.Basepoint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
output := fmt.Sprintf("Private key: %v\nPublic key: %v",
|
||||
base64.RawURLEncoding.EncodeToString(privateKey),
|
||||
base64.RawURLEncoding.EncodeToString(publicKey))
|
||||
fmt.Println(output)
|
||||
|
||||
return nil
|
||||
}
|
@@ -66,7 +66,9 @@ func (l *Limiter) AddInboundLimiter(tag string, nodeSpeedLimit uint64, userList
|
||||
// init redis store
|
||||
rs := redisStore.NewRedis(redis.NewClient(
|
||||
&redis.Options{
|
||||
Network: globalLimit.RedisNetwork,
|
||||
Addr: globalLimit.RedisAddr,
|
||||
Username: globalLimit.RedisUsername,
|
||||
Password: globalLimit.RedisPassword,
|
||||
DB: globalLimit.RedisDB,
|
||||
}),
|
||||
|
@@ -2,7 +2,9 @@ package limiter
|
||||
|
||||
type GlobalDeviceLimitConfig struct {
|
||||
Enable bool `mapstructure:"Enable"`
|
||||
RedisAddr string `mapstructure:"RedisAddr"` // host:port
|
||||
RedisNetwork string `mapstructure:"RedisNetwork"` // tcp or unix
|
||||
RedisAddr string `mapstructure:"RedisAddr"` // host:port, or /path/to/unix.sock
|
||||
RedisUsername string `mapstructure:"RedisUsername"`
|
||||
RedisPassword string `mapstructure:"RedisPassword"`
|
||||
RedisDB int `mapstructure:"RedisDB"`
|
||||
Timeout int `mapstructure:"Timeout"`
|
||||
|
@@ -6,12 +6,13 @@ import (
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"log"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/go-acme/lego/v4/certcrypto"
|
||||
"github.com/go-acme/lego/v4/lego"
|
||||
"github.com/go-acme/lego/v4/registration"
|
||||
|
@@ -4,11 +4,12 @@ import (
|
||||
"bytes"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/go-acme/lego/v4/certcrypto"
|
||||
"github.com/go-acme/lego/v4/certificate"
|
||||
"golang.org/x/net/idna"
|
||||
|
@@ -3,9 +3,10 @@ package mylego
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/x509"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/go-acme/lego/v4/certcrypto"
|
||||
"github.com/go-acme/lego/v4/certificate"
|
||||
"github.com/go-acme/lego/v4/lego"
|
||||
|
@@ -2,11 +2,11 @@ package mylego
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/go-acme/lego/v4/certificate"
|
||||
"github.com/go-acme/lego/v4/lego"
|
||||
"github.com/go-acme/lego/v4/registration"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
const rootPathWarningMessage = `!!!! HEADS UP !!!!
|
||||
|
@@ -1,10 +1,11 @@
|
||||
package mylego
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/go-acme/lego/v4/certcrypto"
|
||||
"github.com/go-acme/lego/v4/challenge/dns01"
|
||||
"github.com/go-acme/lego/v4/challenge/http01"
|
||||
|
340
go.mod
340
go.mod
@@ -1,8 +1,6 @@
|
||||
module github.com/XrayR-project/XrayR
|
||||
|
||||
go 1.21
|
||||
|
||||
toolchain go1.21.1
|
||||
go 1.22.0
|
||||
|
||||
require (
|
||||
dario.cat/mergo v1.0.0
|
||||
@@ -11,126 +9,173 @@ require (
|
||||
github.com/eko/gocache/lib/v4 v4.1.5
|
||||
github.com/eko/gocache/store/go_cache/v4 v4.2.1
|
||||
github.com/eko/gocache/store/redis/v4 v4.2.1
|
||||
github.com/fsnotify/fsnotify v1.6.0
|
||||
github.com/go-acme/lego/v4 v4.14.2
|
||||
github.com/go-resty/resty/v2 v2.9.1
|
||||
github.com/gogf/gf/v2 v2.5.4
|
||||
github.com/fsnotify/fsnotify v1.7.0
|
||||
github.com/go-acme/lego/v4 v4.16.1
|
||||
github.com/go-resty/resty/v2 v2.11.0
|
||||
github.com/gogf/gf/v2 v2.6.4
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible
|
||||
github.com/r3labs/diff/v2 v2.15.1
|
||||
github.com/redis/go-redis/v9 v9.2.1
|
||||
github.com/sagernet/sing v0.2.13
|
||||
github.com/sagernet/sing-shadowsocks v0.2.5
|
||||
github.com/shirou/gopsutil/v3 v3.23.9
|
||||
github.com/spf13/viper v1.17.0
|
||||
github.com/stretchr/testify v1.8.4
|
||||
github.com/xtls/xray-core v1.8.4
|
||||
golang.org/x/crypto v0.14.0
|
||||
golang.org/x/net v0.16.0
|
||||
golang.org/x/time v0.3.0
|
||||
google.golang.org/protobuf v1.31.0
|
||||
github.com/redis/go-redis/v9 v9.5.1
|
||||
github.com/sagernet/sing v0.3.8
|
||||
github.com/sagernet/sing-shadowsocks v0.2.6
|
||||
github.com/shirou/gopsutil/v3 v3.24.2
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
github.com/spf13/cobra v1.8.0
|
||||
github.com/spf13/viper v1.18.2
|
||||
github.com/stretchr/testify v1.9.0
|
||||
github.com/xtls/xray-core v1.8.13
|
||||
golang.org/x/crypto v0.24.0
|
||||
golang.org/x/net v0.26.0
|
||||
golang.org/x/time v0.5.0
|
||||
google.golang.org/protobuf v1.34.1
|
||||
)
|
||||
|
||||
require (
|
||||
cloud.google.com/go/compute v1.23.0 // indirect
|
||||
cloud.google.com/go/compute v1.25.1 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.2.3 // indirect
|
||||
github.com/AdamSLevy/jsonrpc2/v14 v14.1.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.1.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/privatedns/armprivatedns v1.1.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.10.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.1 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.2 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.2.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/privatedns/armprivatedns v1.2.0 // indirect
|
||||
github.com/Azure/go-autorest v14.2.0+incompatible // indirect
|
||||
github.com/Azure/go-autorest/autorest v0.11.24 // indirect
|
||||
github.com/Azure/go-autorest/autorest/adal v0.9.18 // indirect
|
||||
github.com/Azure/go-autorest/autorest v0.11.29 // indirect
|
||||
github.com/Azure/go-autorest/autorest/adal v0.9.23 // indirect
|
||||
github.com/Azure/go-autorest/autorest/azure/auth v0.5.12 // indirect
|
||||
github.com/Azure/go-autorest/autorest/azure/cli v0.4.5 // indirect
|
||||
github.com/Azure/go-autorest/autorest/azure/cli v0.4.6 // indirect
|
||||
github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect
|
||||
github.com/Azure/go-autorest/autorest/to v0.4.0 // indirect
|
||||
github.com/Azure/go-autorest/logger v0.2.1 // indirect
|
||||
github.com/Azure/go-autorest/tracing v0.6.0 // indirect
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0 // indirect
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 // indirect
|
||||
github.com/BurntSushi/toml v1.3.2 // indirect
|
||||
github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 // indirect
|
||||
github.com/CloudyKit/jet/v6 v6.2.0 // indirect
|
||||
github.com/Joker/jade v1.1.3 // indirect
|
||||
github.com/OmarTariq612/goech v0.0.0-20240405204721-8e2e1dafd3a0 // indirect
|
||||
github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87 // indirect
|
||||
github.com/Shopify/goreferrer v0.0.0-20220729165902-8cddb4f5de06 // indirect
|
||||
github.com/akamai/AkamaiOPEN-edgegrid-golang v1.2.2 // indirect
|
||||
github.com/aliyun/alibaba-cloud-sdk-go v1.61.1755 // indirect
|
||||
github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 // indirect
|
||||
github.com/andybalholm/brotli v1.0.5 // indirect
|
||||
github.com/aws/aws-sdk-go-v2 v1.19.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/config v1.18.28 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.13.27 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.5 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.35 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.29 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.36 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.29 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/lightsail v1.27.2 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/route53 v1.28.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.12.13 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.13 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.19.3 // indirect
|
||||
github.com/aws/smithy-go v1.13.5 // indirect
|
||||
github.com/aliyun/alibaba-cloud-sdk-go v1.62.695 // indirect
|
||||
github.com/andybalholm/brotli v1.1.0 // indirect
|
||||
github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2 v1.25.3 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/config v1.27.7 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.7 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.15.3 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.3 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.3 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.5 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/lightsail v1.36.2 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/route53 v1.40.2 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.20.2 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.23.2 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.28.4 // indirect
|
||||
github.com/aws/smithy-go v1.20.1 // indirect
|
||||
github.com/aymerick/douceur v0.2.0 // indirect
|
||||
github.com/benbjohnson/clock v1.3.5 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect
|
||||
github.com/boombuler/barcode v1.0.1 // indirect
|
||||
github.com/bytedance/sonic v1.11.3 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||
github.com/civo/civogo v0.3.11 // indirect
|
||||
github.com/cloudflare/cloudflare-go v0.70.0 // indirect
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect
|
||||
github.com/chenzhuoyu/iasm v0.9.1 // indirect
|
||||
github.com/civo/civogo v0.3.63 // indirect
|
||||
github.com/cloudflare/circl v1.3.8 // indirect
|
||||
github.com/cloudflare/cloudflare-go v0.90.0 // indirect
|
||||
github.com/cpu/goacmedns v0.1.1 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/deepmap/oapi-codegen v1.9.1 // indirect
|
||||
github.com/deepmap/oapi-codegen v1.16.2 // indirect
|
||||
github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140 // indirect
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
github.com/dimchansky/utfbom v1.1.1 // indirect
|
||||
github.com/dnsimple/dnsimple-go v1.2.0 // indirect
|
||||
github.com/exoscale/egoscale v0.100.1 // indirect
|
||||
github.com/dnsimple/dnsimple-go v1.7.0 // indirect
|
||||
github.com/exoscale/egoscale v1.19.0 // indirect
|
||||
github.com/fatih/structs v1.1.0 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/flosch/pongo2/v4 v4.0.2 // indirect
|
||||
github.com/francoispqt/gojay v1.2.13 // indirect
|
||||
github.com/gaukas/godicttls v0.0.4 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
|
||||
github.com/ghodss/yaml v1.0.1-0.20220118164431-d8423dcdf344 // indirect
|
||||
github.com/go-errors/errors v1.0.1 // indirect
|
||||
github.com/go-jose/go-jose/v3 v3.0.0 // indirect
|
||||
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||
github.com/gin-gonic/gin v1.9.1 // indirect
|
||||
github.com/go-errors/errors v1.5.1 // indirect
|
||||
github.com/go-jose/go-jose/v4 v4.0.1 // indirect
|
||||
github.com/go-logr/logr v1.4.1 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-ole/go-ole v1.3.0 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.19.0 // indirect
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
|
||||
github.com/go-viper/mapstructure/v2 v2.0.0-alpha.1 // indirect
|
||||
github.com/goccy/go-json v0.10.2 // indirect
|
||||
github.com/gofrs/uuid v4.4.0+incompatible // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
|
||||
github.com/golang-jwt/jwt/v5 v5.2.1 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/golang/mock v1.6.0 // indirect
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/golang/mock v1.7.0-rc.1 // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/gomarkdown/markdown v0.0.0-20231222211730-1d6d20845b47 // indirect
|
||||
github.com/google/btree v1.1.2 // indirect
|
||||
github.com/google/go-querystring v1.1.0 // indirect
|
||||
github.com/google/pprof v0.0.0-20230821062121-407c9e7a662f // indirect
|
||||
github.com/google/gofuzz v1.2.0 // indirect
|
||||
github.com/google/pprof v0.0.0-20240227163752-401108e1b7e7 // indirect
|
||||
github.com/google/s2a-go v0.1.7 // indirect
|
||||
github.com/google/uuid v1.3.1 // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.1 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.12.0 // indirect
|
||||
github.com/gophercloud/gophercloud v1.0.0 // indirect
|
||||
github.com/gophercloud/utils v0.0.0-20210216074907-f6de111f2eae // indirect
|
||||
github.com/gorilla/websocket v1.5.0 // indirect
|
||||
github.com/hashicorp/errwrap v1.0.0 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.12.3 // indirect
|
||||
github.com/gophercloud/gophercloud v1.11.0 // indirect
|
||||
github.com/gophercloud/utils v0.0.0-20231010081019-80377eca5d56 // indirect
|
||||
github.com/gorilla/css v1.0.1 // indirect
|
||||
github.com/gorilla/websocket v1.5.1 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||
github.com/hashicorp/go-retryablehttp v0.7.4 // indirect
|
||||
github.com/hashicorp/go-retryablehttp v0.7.5 // indirect
|
||||
github.com/hashicorp/go-uuid v1.0.3 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/infobloxopen/infoblox-go-client v1.1.1 // indirect
|
||||
github.com/iris-contrib/schema v0.0.6 // indirect
|
||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213 // indirect
|
||||
github.com/klauspost/compress v1.17.0 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.5 // indirect
|
||||
github.com/kataras/blocks v0.0.8 // indirect
|
||||
github.com/kataras/golog v0.1.11 // indirect
|
||||
github.com/kataras/iris/v12 v12.2.10 // indirect
|
||||
github.com/kataras/pio v0.0.13 // indirect
|
||||
github.com/kataras/sitemap v0.0.6 // indirect
|
||||
github.com/kataras/tunnel v0.0.4 // indirect
|
||||
github.com/klauspost/compress v1.17.7 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
|
||||
github.com/kolo/xmlrpc v0.0.0-20220921171641-a4b6fa1dd06b // indirect
|
||||
github.com/kylelemons/godebug v1.1.0 // indirect
|
||||
github.com/labbsr0x/bindman-dns-webhook v1.0.2 // indirect
|
||||
github.com/labbsr0x/goh v1.0.1 // indirect
|
||||
github.com/linode/linodego v1.17.2 // indirect
|
||||
github.com/liquidweb/go-lwApi v0.0.5 // indirect
|
||||
github.com/liquidweb/liquidweb-cli v0.6.9 // indirect
|
||||
github.com/liquidweb/liquidweb-go v1.6.3 // indirect
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
|
||||
github.com/labstack/echo/v4 v4.11.4 // indirect
|
||||
github.com/labstack/gommon v0.4.2 // indirect
|
||||
github.com/leodido/go-urn v1.4.0 // indirect
|
||||
github.com/linode/linodego v1.30.0 // indirect
|
||||
github.com/liquidweb/liquidweb-cli v0.7.0 // indirect
|
||||
github.com/liquidweb/liquidweb-go v1.6.4 // indirect
|
||||
github.com/lufia/plan9stats v0.0.0-20240226150601-1dcf7310316a // indirect
|
||||
github.com/magiconair/properties v1.8.7 // indirect
|
||||
github.com/mattn/go-isatty v0.0.19 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
|
||||
github.com/miekg/dns v1.1.55 // indirect
|
||||
github.com/mailgun/raymond/v2 v2.0.48 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/microcosm-cc/bluemonday v1.0.26 // indirect
|
||||
github.com/miekg/dns v1.1.59 // indirect
|
||||
github.com/mimuret/golang-iij-dpf v0.9.1 // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
@@ -138,95 +183,120 @@ require (
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04 // indirect
|
||||
github.com/nrdcg/auroradns v1.1.0 // indirect
|
||||
github.com/nrdcg/bunny-go v0.0.0-20230728143221-c9dda82568d9 // indirect
|
||||
github.com/nrdcg/bunny-go v0.0.0-20240207213615-dde5bf4577a3 // indirect
|
||||
github.com/nrdcg/desec v0.7.0 // indirect
|
||||
github.com/nrdcg/dnspod-go v0.4.0 // indirect
|
||||
github.com/nrdcg/freemyip v0.2.0 // indirect
|
||||
github.com/nrdcg/goinwx v0.8.2 // indirect
|
||||
github.com/nrdcg/goinwx v0.10.0 // indirect
|
||||
github.com/nrdcg/mailinabox v0.2.0 // indirect
|
||||
github.com/nrdcg/namesilo v0.2.1 // indirect
|
||||
github.com/nrdcg/nodion v0.1.0 // indirect
|
||||
github.com/nrdcg/porkbun v0.2.0 // indirect
|
||||
github.com/nrdcg/porkbun v0.3.0 // indirect
|
||||
github.com/nzdjb/go-metaname v1.0.0 // indirect
|
||||
github.com/onsi/ginkgo/v2 v2.12.0 // indirect
|
||||
github.com/onsi/ginkgo/v2 v2.16.0 // indirect
|
||||
github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b // indirect
|
||||
github.com/oracle/oci-go-sdk v24.3.0+incompatible // indirect
|
||||
github.com/ovh/go-ovh v1.4.2 // indirect
|
||||
github.com/ovh/go-ovh v1.4.3 // indirect
|
||||
github.com/pelletier/go-toml v1.9.5 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.1.0 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.1.1 // indirect
|
||||
github.com/pires/go-proxyproto v0.7.0 // indirect
|
||||
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
|
||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
|
||||
github.com/pquerna/otp v1.4.0 // indirect
|
||||
github.com/prometheus/client_golang v1.14.0 // indirect
|
||||
github.com/prometheus/client_model v0.3.0 // indirect
|
||||
github.com/prometheus/common v0.37.0 // indirect
|
||||
github.com/prometheus/procfs v0.8.0 // indirect
|
||||
github.com/quic-go/qtls-go1-20 v0.3.3 // indirect
|
||||
github.com/quic-go/quic-go v0.38.1 // indirect
|
||||
github.com/refraction-networking/utls v1.4.3 // indirect
|
||||
github.com/prometheus/client_golang v1.19.0 // indirect
|
||||
github.com/prometheus/client_model v0.6.0 // indirect
|
||||
github.com/prometheus/common v0.50.0 // indirect
|
||||
github.com/prometheus/procfs v0.13.0 // indirect
|
||||
github.com/quic-go/quic-go v0.44.0 // indirect
|
||||
github.com/refraction-networking/utls v1.6.6 // indirect
|
||||
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect
|
||||
github.com/sacloud/api-client-go v0.2.8 // indirect
|
||||
github.com/sacloud/go-http v0.1.6 // indirect
|
||||
github.com/sacloud/iaas-api-go v1.11.1 // indirect
|
||||
github.com/sacloud/packages-go v0.0.9 // indirect
|
||||
github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c // indirect
|
||||
github.com/sagikazarmark/locafero v0.3.0 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/sacloud/api-client-go v0.2.10 // indirect
|
||||
github.com/sacloud/go-http v0.1.8 // indirect
|
||||
github.com/sacloud/iaas-api-go v1.11.2 // indirect
|
||||
github.com/sacloud/packages-go v0.0.10 // indirect
|
||||
github.com/sagikazarmark/locafero v0.4.0 // indirect
|
||||
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
||||
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.17 // indirect
|
||||
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.25 // indirect
|
||||
github.com/schollz/closestmatch v2.1.0+incompatible // indirect
|
||||
github.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb // indirect
|
||||
github.com/shoenig/go-m1cpu v0.1.6 // indirect
|
||||
github.com/sirupsen/logrus v1.8.1 // indirect
|
||||
github.com/shopspring/decimal v1.3.1 // indirect
|
||||
github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9 // indirect
|
||||
github.com/softlayer/softlayer-go v1.1.2 // indirect
|
||||
github.com/softlayer/softlayer-go v1.1.3 // indirect
|
||||
github.com/softlayer/xmlrpc v0.0.0-20200409220501-5f089df7cb7e // indirect
|
||||
github.com/sourcegraph/conc v0.3.0 // indirect
|
||||
github.com/spf13/afero v1.10.0 // indirect
|
||||
github.com/spf13/cast v1.5.1 // indirect
|
||||
github.com/spf13/afero v1.11.0 // indirect
|
||||
github.com/spf13/cast v1.6.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/stretchr/objx v0.5.0 // indirect
|
||||
github.com/stretchr/objx v0.5.2 // indirect
|
||||
github.com/subosito/gotenv v1.6.0 // 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/tklauser/go-sysconf v0.3.12 // indirect
|
||||
github.com/tklauser/numcpus v0.6.1 // indirect
|
||||
github.com/transip/gotransip/v6 v6.20.0 // indirect
|
||||
github.com/ultradns/ultradns-go-sdk v1.5.0-20230427130837-23c9b0c // indirect
|
||||
github.com/tdewolff/minify/v2 v2.20.19 // indirect
|
||||
github.com/tdewolff/parse/v2 v2.7.12 // indirect
|
||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.878 // indirect
|
||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.878 // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.13 // indirect
|
||||
github.com/tklauser/numcpus v0.7.0 // indirect
|
||||
github.com/transip/gotransip/v6 v6.23.0 // indirect
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/ugorji/go/codec v1.2.12 // indirect
|
||||
github.com/ultradns/ultradns-go-sdk v1.6.1-20231103022937-8589b6a // indirect
|
||||
github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/valyala/fasttemplate v1.2.2 // indirect
|
||||
github.com/vinyldns/go-vinyldns v0.9.16 // indirect
|
||||
github.com/vishvananda/netlink v1.2.1-beta.2.0.20230316163032-ced5aaba43e3 // indirect
|
||||
github.com/vishvananda/netns v0.0.4 // indirect
|
||||
github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect
|
||||
github.com/vmihailenco/msgpack/v5 v5.3.5 // indirect
|
||||
github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect
|
||||
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
|
||||
github.com/vultr/govultr/v2 v2.17.2 // indirect
|
||||
github.com/xtls/reality v0.0.0-20230828171259-e426190d57f6 // indirect
|
||||
github.com/yandex-cloud/go-genproto v0.0.0-20220805142335-27b56ddae16f // indirect
|
||||
github.com/yandex-cloud/go-sdk v0.0.0-20220805164847-cf028e604997 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.3 // indirect
|
||||
github.com/xtls/reality v0.0.0-20240429224917-ecc4401070cc // indirect
|
||||
github.com/yandex-cloud/go-genproto v0.0.0-20240311082839-58e1a7554a75 // indirect
|
||||
github.com/yandex-cloud/go-sdk v0.0.0-20240311083148-81c0846b96cd // indirect
|
||||
github.com/yosssi/ace v0.0.5 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||
go.opencensus.io v0.24.0 // indirect
|
||||
go.opentelemetry.io/otel v1.14.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.14.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect
|
||||
go.opentelemetry.io/otel v1.24.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.24.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.24.0 // indirect
|
||||
go.uber.org/atomic v1.11.0 // indirect
|
||||
go.uber.org/multierr v1.9.0 // indirect
|
||||
go.uber.org/ratelimit v0.2.0 // indirect
|
||||
go4.org/netipx v0.0.0-20230824141953-6213f710f925 // indirect
|
||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
|
||||
golang.org/x/mod v0.12.0 // indirect
|
||||
golang.org/x/oauth2 v0.12.0 // indirect
|
||||
golang.org/x/sys v0.13.0 // indirect
|
||||
golang.org/x/text v0.13.0 // indirect
|
||||
golang.org/x/tools v0.13.0 // indirect
|
||||
google.golang.org/api v0.143.0 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/genproto v0.0.0-20230913181813-007df8e322eb // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20230913181813-007df8e322eb // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230920204549-e6e6cdab5c13 // indirect
|
||||
google.golang.org/grpc v1.58.2 // indirect
|
||||
go.uber.org/mock v0.4.0 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
go.uber.org/ratelimit v0.3.1 // indirect
|
||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect
|
||||
golang.org/x/arch v0.7.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect
|
||||
golang.org/x/mod v0.17.0 // indirect
|
||||
golang.org/x/oauth2 v0.18.0 // indirect
|
||||
golang.org/x/sync v0.7.0 // indirect
|
||||
golang.org/x/sys v0.21.0 // indirect
|
||||
golang.org/x/text v0.16.0 // indirect
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
|
||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
|
||||
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 // indirect
|
||||
google.golang.org/api v0.170.0 // indirect
|
||||
google.golang.org/appengine v1.6.8 // indirect
|
||||
google.golang.org/genproto v0.0.0-20240314234333-6e1732d8331c // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 // indirect
|
||||
google.golang.org/grpc v1.64.0 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/ns1/ns1-go.v2 v2.7.6 // indirect
|
||||
gopkg.in/ns1/ns1-go.v2 v2.9.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
gvisor.dev/gvisor v0.0.0-20230822212503-5bf4e5f98744 // indirect
|
||||
lukechampine.com/blake3 v1.2.1 // indirect
|
||||
gvisor.dev/gvisor v0.0.0-20231202080848-1f7806d17489 // indirect
|
||||
k8s.io/api v0.29.2 // indirect
|
||||
k8s.io/apimachinery v0.29.2 // indirect
|
||||
k8s.io/klog/v2 v2.120.1 // indirect
|
||||
k8s.io/utils v0.0.0-20240310230437-4693a0247e57 // indirect
|
||||
lukechampine.com/blake3 v1.3.0 // indirect
|
||||
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
|
||||
)
|
||||
|
||||
replace github.com/exoscale/egoscale => github.com/exoscale/egoscale v0.102.0
|
||||
replace github.com/exoscale/egoscale => github.com/exoscale/egoscale v0.102.3
|
||||
|
13
main.go
Normal file
13
main.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/XrayR-project/XrayR/cmd"
|
||||
)
|
||||
|
||||
func main() {
|
||||
if err := cmd.Execute(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
@@ -2,16 +2,12 @@ package panel
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"log"
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
"github.com/XrayR-project/XrayR/api/gov2panel"
|
||||
"github.com/XrayR-project/XrayR/api/newV2board"
|
||||
"github.com/XrayR-project/XrayR/app/mydispatcher"
|
||||
|
||||
"dario.cat/mergo"
|
||||
"github.com/r3labs/diff/v2"
|
||||
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"
|
||||
@@ -19,11 +15,15 @@ import (
|
||||
"github.com/xtls/xray-core/infra/conf"
|
||||
|
||||
"github.com/XrayR-project/XrayR/api"
|
||||
"github.com/XrayR-project/XrayR/api/bunpanel"
|
||||
"github.com/XrayR-project/XrayR/api/gov2panel"
|
||||
"github.com/XrayR-project/XrayR/api/newV2board"
|
||||
"github.com/XrayR-project/XrayR/api/pmpanel"
|
||||
"github.com/XrayR-project/XrayR/api/proxypanel"
|
||||
"github.com/XrayR-project/XrayR/api/sspanel"
|
||||
"github.com/XrayR-project/XrayR/api/v2raysocks"
|
||||
_ "github.com/XrayR-project/XrayR/main/distro/all"
|
||||
"github.com/XrayR-project/XrayR/app/mydispatcher"
|
||||
_ "github.com/XrayR-project/XrayR/cmd/distro/all"
|
||||
"github.com/XrayR-project/XrayR/service"
|
||||
"github.com/XrayR-project/XrayR/service/controller"
|
||||
)
|
||||
@@ -154,7 +154,6 @@ func (p *Panel) loadCore(panelConfig *Config) *core.Instance {
|
||||
if err != nil {
|
||||
log.Panicf("failed to create instance: %s", err)
|
||||
}
|
||||
log.Printf("Xray Core Version: %s", core.Version())
|
||||
|
||||
return server
|
||||
}
|
||||
@@ -177,7 +176,7 @@ func (p *Panel) Start() {
|
||||
switch nodeConfig.PanelType {
|
||||
case "SSpanel":
|
||||
apiClient = sspanel.New(nodeConfig.ApiConfig)
|
||||
case "NewV2board":
|
||||
case "NewV2board", "V2board":
|
||||
apiClient = newV2board.New(nodeConfig.ApiConfig)
|
||||
case "PMpanel":
|
||||
apiClient = pmpanel.New(nodeConfig.ApiConfig)
|
||||
@@ -187,6 +186,8 @@ func (p *Panel) Start() {
|
||||
apiClient = v2raysocks.New(nodeConfig.ApiConfig)
|
||||
case "GoV2Panel":
|
||||
apiClient = gov2panel.New(nodeConfig.ApiConfig)
|
||||
case "BunPanel":
|
||||
apiClient = bunpanel.New(nodeConfig.ApiConfig)
|
||||
default:
|
||||
log.Panicf("Unsupport panel type: %s", nodeConfig.PanelType)
|
||||
}
|
||||
@@ -207,7 +208,7 @@ func (p *Panel) Start() {
|
||||
for _, s := range p.Service {
|
||||
err := s.Start()
|
||||
if err != nil {
|
||||
log.Panicf("Panel Start fialed: %s", err)
|
||||
log.Panicf("Panel Start failed: %s", err)
|
||||
}
|
||||
}
|
||||
p.Running = true
|
||||
@@ -221,7 +222,7 @@ func (p *Panel) Close() {
|
||||
for _, s := range p.Service {
|
||||
err := s.Close()
|
||||
if err != nil {
|
||||
log.Panicf("Panel Close fialed: %s", err)
|
||||
log.Panicf("Panel Close failed: %s", err)
|
||||
}
|
||||
}
|
||||
p.Service = nil
|
||||
|
@@ -1,5 +1,5 @@
|
||||
Log:
|
||||
Level: warning # Log level: none, error, warning, info, debug
|
||||
Level: warning # Log level: none, error, warning, info, debug
|
||||
AccessPath: # /etc/XrayR/access.Log
|
||||
ErrorPath: # /etc/XrayR/error.log
|
||||
DnsConfigPath: # /etc/XrayR/dns.json # Path to dns config, check https://xtls.github.io/config/dns.html for help
|
||||
@@ -13,12 +13,12 @@ ConnectionConfig:
|
||||
DownlinkOnly: 4 # Time limit when the connection is closed after the uplink is closed, Second
|
||||
BufferSize: 64 # The internal cache size of each connection, kB
|
||||
Nodes:
|
||||
- PanelType: "SSpanel" # Panel type: SSpanel, NewV2board, PMpanel, Proxypanel, V2RaySocks, GoV2Panel
|
||||
- PanelType: "SSpanel" # Panel type: SSpanel, NewV2board, PMpanel, Proxypanel, V2RaySocks, GoV2Panel, BunPanel
|
||||
ApiConfig:
|
||||
ApiHost: "http://127.0.0.1:667"
|
||||
ApiKey: "123"
|
||||
NodeID: 41
|
||||
NodeType: V2ray # Node type: V2ray, Shadowsocks, Trojan, Shadowsocks-Plugin
|
||||
NodeType: V2ray # Node type: V2ray,vmess,vless, Shadowsocks, Trojan, Shadowsocks-Plugin
|
||||
Timeout: 30 # Timeout for the api request
|
||||
EnableVless: false # Enable Vless for V2ray Type
|
||||
VlessFlow: "xtls-rprx-vision" # Only support vless
|
||||
@@ -40,7 +40,9 @@ Nodes:
|
||||
LimitDuration: 0 # How many minutes will the limiting last (unit: minute)
|
||||
GlobalDeviceLimitConfig:
|
||||
Enable: false # Enable the global device limit of a user
|
||||
RedisAddr: 127.0.0.1:6379 # The redis server address
|
||||
RedisNetwork: tcp # Redis protocol, tcp or unix
|
||||
RedisAddr: 127.0.0.1:6379 # Redis server address, or unix socket path
|
||||
RedisUsername: # Redis username
|
||||
RedisPassword: YOUR PASSWORD # Redis password
|
||||
RedisDB: 0 # Redis DB
|
||||
Timeout: 5 # Timeout for redis request
|
||||
@@ -53,14 +55,14 @@ Nodes:
|
||||
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 disable
|
||||
DisableLocalREALITYConfig: false # disable local reality config
|
||||
EnableREALITY: true # Enable REALITY
|
||||
EnableREALITY: false # Enable REALITY
|
||||
REALITYConfigs:
|
||||
Show: true # Show REALITY debug
|
||||
Dest: www.smzdm.com:443 # Required, Same as fallback
|
||||
Dest: www.amazon.com:443 # Required, Same as fallback
|
||||
ProxyProtocolVer: 0 # Send PROXY protocol version, 0 for disable
|
||||
ServerNames: # Required, list of available serverNames for the client, * wildcard is not supported at the moment.
|
||||
- www.smzdm.com
|
||||
PrivateKey: YOUR_PRIVATE_KEY # Required, execute './xray x25519' to generate.
|
||||
- www.amazon.com
|
||||
PrivateKey: YOUR_PRIVATE_KEY # Required, execute './XrayR x25519' to generate.
|
||||
MinClientVer: # Optional, minimum version of Xray client, format is x.y.z.
|
||||
MaxClientVer: # Optional, maximum version of Xray client, format is x.y.z.
|
||||
MaxTimeDiff: 0 # Optional, maximum allowed time difference, unit is in milliseconds.
|
||||
@@ -119,11 +121,11 @@ Nodes:
|
||||
# EnableREALITY: true # Enable REALITY
|
||||
# REALITYConfigs:
|
||||
# Show: true # Show REALITY debug
|
||||
# Dest: www.smzdm.com:443 # Required, Same as fallback
|
||||
# Dest: www.amazon.com:443 # Required, Same as fallback
|
||||
# ProxyProtocolVer: 0 # Send PROXY protocol version, 0 for disable
|
||||
# ServerNames: # Required, list of available serverNames for the client, * wildcard is not supported at the moment.
|
||||
# - www.smzdm.com
|
||||
# PrivateKey: YOUR_PRIVATE_KEY # Required, execute './xray x25519' to generate.
|
||||
# - www.amazon.com
|
||||
# PrivateKey: YOUR_PRIVATE_KEY # Required, execute './XrayR x25519' to generate.
|
||||
# MinClientVer: # Optional, minimum version of Xray client, format is x.y.z.
|
||||
# MaxClientVer: # Optional, maximum version of Xray client, format is x.y.z.
|
||||
# MaxTimeDiff: 0 # Optional, maximum allowed time difference, unit is in milliseconds.
|
@@ -1,6 +1,6 @@
|
||||
[
|
||||
{
|
||||
"listen": "0.0.0.0",
|
||||
"listen": "127.0.0.1",
|
||||
"port": 1234,
|
||||
"protocol": "socks",
|
||||
"settings": {
|
@@ -3,10 +3,10 @@ package controller
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"reflect"
|
||||
"time"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/xtls/xray-core/common/protocol"
|
||||
"github.com/xtls/xray-core/common/task"
|
||||
"github.com/xtls/xray-core/core"
|
||||
@@ -44,6 +44,7 @@ type Controller struct {
|
||||
stm stats.Manager
|
||||
dispatcher *mydispatcher.DefaultDispatcher
|
||||
startAt time.Time
|
||||
logger *log.Entry
|
||||
}
|
||||
|
||||
type periodicTask struct {
|
||||
@@ -53,6 +54,11 @@ type periodicTask struct {
|
||||
|
||||
// New return a Controller service with default parameters.
|
||||
func New(server *core.Instance, api api.API, config *Config, panelType string) *Controller {
|
||||
logger := log.NewEntry(log.StandardLogger()).WithFields(log.Fields{
|
||||
"Host": api.Describe().APIHost,
|
||||
"Type": api.Describe().NodeType,
|
||||
"ID": api.Describe().NodeID,
|
||||
})
|
||||
controller := &Controller{
|
||||
server: server,
|
||||
config: config,
|
||||
@@ -63,6 +69,7 @@ func New(server *core.Instance, api api.API, config *Config, panelType string) *
|
||||
stm: server.GetFeature(stats.ManagerType()).(stats.Manager),
|
||||
dispatcher: server.GetFeature(routing.DispatcherType()).(*mydispatcher.DefaultDispatcher),
|
||||
startAt: time.Now(),
|
||||
logger: logger,
|
||||
}
|
||||
|
||||
return controller
|
||||
@@ -85,7 +92,7 @@ func (c *Controller) Start() error {
|
||||
// Add new tag
|
||||
err = c.addNewTag(newNodeInfo)
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
c.logger.Panic(err)
|
||||
return err
|
||||
}
|
||||
// Update user
|
||||
@@ -104,16 +111,16 @@ func (c *Controller) Start() error {
|
||||
|
||||
// Add Limiter
|
||||
if err := c.AddInboundLimiter(c.Tag, newNodeInfo.SpeedLimit, userInfo, c.config.GlobalDeviceLimitConfig); err != nil {
|
||||
log.Print(err)
|
||||
c.logger.Print(err)
|
||||
}
|
||||
|
||||
// Add Rule Manager
|
||||
if !c.config.DisableGetRule {
|
||||
if ruleList, err := c.apiClient.GetNodeRule(); err != nil {
|
||||
log.Printf("Get rule list filed: %s", err)
|
||||
c.logger.Printf("Get rule list filed: %s", err)
|
||||
} else if len(*ruleList) > 0 {
|
||||
if err := c.UpdateRule(c.Tag, *ruleList); err != nil {
|
||||
log.Print(err)
|
||||
c.logger.Print(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -155,7 +162,7 @@ func (c *Controller) Start() error {
|
||||
|
||||
// Start periodic tasks
|
||||
for i := range c.tasks {
|
||||
log.Printf("%s Start %s periodic task", c.logPrefix(), c.tasks[i].tag)
|
||||
c.logger.Printf("Start %s periodic task", c.tasks[i].tag)
|
||||
go c.tasks[i].Start()
|
||||
}
|
||||
|
||||
@@ -167,7 +174,7 @@ func (c *Controller) Close() error {
|
||||
for i := range c.tasks {
|
||||
if c.tasks[i].Periodic != nil {
|
||||
if err := c.tasks[i].Periodic.Close(); err != nil {
|
||||
log.Panicf("%s %s periodic task close failed: %s", c.logPrefix(), c.tasks[i].tag, err)
|
||||
c.logger.Panicf("%s periodic task close failed: %s", c.tasks[i].tag, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -189,7 +196,7 @@ func (c *Controller) nodeInfoMonitor() (err error) {
|
||||
nodeInfoChanged = false
|
||||
newNodeInfo = c.nodeInfo
|
||||
} else {
|
||||
log.Print(err)
|
||||
c.logger.Print(err)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@@ -205,7 +212,7 @@ func (c *Controller) nodeInfoMonitor() (err error) {
|
||||
usersChanged = false
|
||||
newUserInfo = c.userList
|
||||
} else {
|
||||
log.Print(err)
|
||||
c.logger.Print(err)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@@ -217,14 +224,14 @@ func (c *Controller) nodeInfoMonitor() (err error) {
|
||||
oldTag := c.Tag
|
||||
err := c.removeOldTag(oldTag)
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
c.logger.Print(err)
|
||||
return nil
|
||||
}
|
||||
if c.nodeInfo.NodeType == "Shadowsocks-Plugin" {
|
||||
err = c.removeOldTag(fmt.Sprintf("dokodemo-door_%s+1", c.Tag))
|
||||
}
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
c.logger.Print(err)
|
||||
return nil
|
||||
}
|
||||
// Add new tag
|
||||
@@ -232,13 +239,13 @@ func (c *Controller) nodeInfoMonitor() (err error) {
|
||||
c.Tag = c.buildNodeTag()
|
||||
err = c.addNewTag(newNodeInfo)
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
c.logger.Print(err)
|
||||
return nil
|
||||
}
|
||||
nodeInfoChanged = true
|
||||
// Remove Old limiter
|
||||
if err = c.DeleteInboundLimiter(oldTag); err != nil {
|
||||
log.Print(err)
|
||||
c.logger.Print(err)
|
||||
return nil
|
||||
}
|
||||
} else {
|
||||
@@ -250,11 +257,11 @@ func (c *Controller) nodeInfoMonitor() (err error) {
|
||||
if !c.config.DisableGetRule {
|
||||
if ruleList, err := c.apiClient.GetNodeRule(); err != nil {
|
||||
if err.Error() != api.RuleNotModified {
|
||||
log.Printf("Get rule list filed: %s", err)
|
||||
c.logger.Printf("Get rule list filed: %s", err)
|
||||
}
|
||||
} else if len(*ruleList) > 0 {
|
||||
if err := c.UpdateRule(c.Tag, *ruleList); err != nil {
|
||||
log.Print(err)
|
||||
c.logger.Print(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -262,13 +269,13 @@ func (c *Controller) nodeInfoMonitor() (err error) {
|
||||
if nodeInfoChanged {
|
||||
err = c.addNewUser(newUserInfo, newNodeInfo)
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
c.logger.Print(err)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Add Limiter
|
||||
if err := c.AddInboundLimiter(c.Tag, newNodeInfo.SpeedLimit, newUserInfo, c.config.GlobalDeviceLimitConfig); err != nil {
|
||||
log.Print(err)
|
||||
c.logger.Print(err)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -283,21 +290,21 @@ func (c *Controller) nodeInfoMonitor() (err error) {
|
||||
}
|
||||
err := c.removeUsers(deletedEmail, c.Tag)
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
c.logger.Print(err)
|
||||
}
|
||||
}
|
||||
if len(added) > 0 {
|
||||
err = c.addNewUser(&added, c.nodeInfo)
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
c.logger.Print(err)
|
||||
}
|
||||
// Update Limiter
|
||||
if err := c.UpdateInboundLimiter(c.Tag, &added); err != nil {
|
||||
log.Print(err)
|
||||
c.logger.Print(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
log.Printf("%s %d user deleted, %d user added", c.logPrefix(), len(deleted), len(added))
|
||||
c.logger.Printf("%d user deleted, %d user added", len(deleted), len(added))
|
||||
}
|
||||
c.userList = newUserInfo
|
||||
return nil
|
||||
@@ -398,8 +405,8 @@ func (c *Controller) addInboundForSSPlugin(newNodeInfo api.NodeInfo) (err error)
|
||||
func (c *Controller) addNewUser(userInfo *[]api.UserInfo, nodeInfo *api.NodeInfo) (err error) {
|
||||
users := make([]*protocol.User, 0)
|
||||
switch nodeInfo.NodeType {
|
||||
case "V2ray":
|
||||
if nodeInfo.EnableVless {
|
||||
case "V2ray", "Vmess", "Vless":
|
||||
if nodeInfo.EnableVless || (nodeInfo.NodeType == "Vless" && nodeInfo.NodeType != "Vmess") {
|
||||
users = c.buildVlessUser(userInfo)
|
||||
} else {
|
||||
users = c.buildVmessUser(userInfo)
|
||||
@@ -418,7 +425,7 @@ func (c *Controller) addNewUser(userInfo *[]api.UserInfo, nodeInfo *api.NodeInfo
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Printf("%s Added %d new users", c.logPrefix(), len(*userInfo))
|
||||
c.logger.Printf("Added %d new users", len(*userInfo))
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -466,7 +473,7 @@ func limitUser(c *Controller, user api.UserInfo, silentUsers *[]api.UserInfo) {
|
||||
currentSpeedLimit: c.config.AutoSpeedLimitConfig.LimitSpeed,
|
||||
originSpeedLimit: user.SpeedLimit,
|
||||
}
|
||||
log.Printf("Limit User: %s Speed: %d End: %s", c.buildUserTag(&user), c.config.AutoSpeedLimitConfig.LimitSpeed, time.Unix(c.limitedUsers[user].end, 0).Format("01-02 15:04:05"))
|
||||
c.logger.Printf("Limit User: %s Speed: %d End: %s", c.buildUserTag(&user), c.config.AutoSpeedLimitConfig.LimitSpeed, time.Unix(c.limitedUsers[user].end, 0).Format("01-02 15:04:05"))
|
||||
user.SpeedLimit = uint64((c.config.AutoSpeedLimitConfig.LimitSpeed * 1000000) / 8)
|
||||
*silentUsers = append(*silentUsers, user)
|
||||
}
|
||||
@@ -480,7 +487,7 @@ func (c *Controller) userInfoMonitor() (err error) {
|
||||
// Get server status
|
||||
CPU, Mem, Disk, Uptime, err := serverstatus.GetSystemInfo()
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
c.logger.Print(err)
|
||||
}
|
||||
err = c.apiClient.ReportNodeStatus(
|
||||
&api.NodeStatus{
|
||||
@@ -490,25 +497,25 @@ func (c *Controller) userInfoMonitor() (err error) {
|
||||
Uptime: Uptime,
|
||||
})
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
c.logger.Print(err)
|
||||
}
|
||||
// Unlock users
|
||||
if c.config.AutoSpeedLimitConfig.Limit > 0 && len(c.limitedUsers) > 0 {
|
||||
log.Printf("%s Limited users:", c.logPrefix())
|
||||
c.logger.Printf("Limited users:")
|
||||
toReleaseUsers := make([]api.UserInfo, 0)
|
||||
for user, limitInfo := range c.limitedUsers {
|
||||
if time.Now().Unix() > limitInfo.end {
|
||||
user.SpeedLimit = limitInfo.originSpeedLimit
|
||||
toReleaseUsers = append(toReleaseUsers, user)
|
||||
log.Printf("User: %s Speed: %d End: nil (Unlimit)", c.buildUserTag(&user), user.SpeedLimit)
|
||||
c.logger.Printf("User: %s Speed: %d End: nil (Unlimit)", c.buildUserTag(&user), user.SpeedLimit)
|
||||
delete(c.limitedUsers, user)
|
||||
} else {
|
||||
log.Printf("User: %s Speed: %d End: %s", c.buildUserTag(&user), limitInfo.currentSpeedLimit, time.Unix(c.limitedUsers[user].end, 0).Format("01-02 15:04:05"))
|
||||
c.logger.Printf("User: %s Speed: %d End: %s", c.buildUserTag(&user), limitInfo.currentSpeedLimit, time.Unix(c.limitedUsers[user].end, 0).Format("01-02 15:04:05"))
|
||||
}
|
||||
}
|
||||
if len(toReleaseUsers) > 0 {
|
||||
if err := c.UpdateInboundLimiter(c.Tag, &toReleaseUsers); err != nil {
|
||||
log.Print(err)
|
||||
c.logger.Print(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -559,7 +566,7 @@ func (c *Controller) userInfoMonitor() (err error) {
|
||||
}
|
||||
if len(limitedUsers) > 0 {
|
||||
if err := c.UpdateInboundLimiter(c.Tag, &limitedUsers); err != nil {
|
||||
log.Print(err)
|
||||
c.logger.Print(err)
|
||||
}
|
||||
}
|
||||
if len(userTraffic) > 0 {
|
||||
@@ -569,7 +576,7 @@ func (c *Controller) userInfoMonitor() (err error) {
|
||||
}
|
||||
// If report traffic error, not clear the traffic
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
c.logger.Print(err)
|
||||
} else {
|
||||
c.resetTraffic(&upCounterList, &downCounterList)
|
||||
}
|
||||
@@ -577,23 +584,23 @@ func (c *Controller) userInfoMonitor() (err error) {
|
||||
|
||||
// Report Online info
|
||||
if onlineDevice, err := c.GetOnlineDevice(c.Tag); err != nil {
|
||||
log.Print(err)
|
||||
c.logger.Print(err)
|
||||
} else if len(*onlineDevice) > 0 {
|
||||
if err = c.apiClient.ReportNodeOnlineUsers(onlineDevice); err != nil {
|
||||
log.Print(err)
|
||||
c.logger.Print(err)
|
||||
} else {
|
||||
log.Printf("%s Report %d online users", c.logPrefix(), len(*onlineDevice))
|
||||
c.logger.Printf("Report %d online users", len(*onlineDevice))
|
||||
}
|
||||
}
|
||||
|
||||
// Report Illegal user
|
||||
if detectResult, err := c.GetDetectResult(c.Tag); err != nil {
|
||||
log.Print(err)
|
||||
c.logger.Print(err)
|
||||
} else if len(*detectResult) > 0 {
|
||||
if err = c.apiClient.ReportIllegal(detectResult); err != nil {
|
||||
log.Print(err)
|
||||
c.logger.Print(err)
|
||||
} else {
|
||||
log.Printf("%s Report %d illegal behaviors", c.logPrefix(), len(*detectResult))
|
||||
c.logger.Printf("Report %d illegal behaviors", len(*detectResult))
|
||||
}
|
||||
|
||||
}
|
||||
@@ -604,9 +611,9 @@ func (c *Controller) buildNodeTag() string {
|
||||
return fmt.Sprintf("%s_%s_%d", c.nodeInfo.NodeType, c.config.ListenIP, c.nodeInfo.Port)
|
||||
}
|
||||
|
||||
func (c *Controller) logPrefix() string {
|
||||
return fmt.Sprintf("[%s] %s(ID=%d)", c.clientInfo.APIHost, c.nodeInfo.NodeType, c.nodeInfo.NodeID)
|
||||
}
|
||||
// func (c *Controller) logPrefix() string {
|
||||
// return fmt.Sprintf("[%s] %s(ID=%d)", c.clientInfo.APIHost, c.nodeInfo.NodeType, c.nodeInfo.NodeID)
|
||||
// }
|
||||
|
||||
// Check Cert
|
||||
func (c *Controller) certMonitor() error {
|
||||
@@ -615,12 +622,12 @@ func (c *Controller) certMonitor() error {
|
||||
case "dns", "http", "tls":
|
||||
lego, err := mylego.New(c.config.CertConfig)
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
c.logger.Print(err)
|
||||
}
|
||||
// Xray-core supports the OcspStapling certification hot renew
|
||||
_, _, _, err = lego.RenewCert()
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
c.logger.Print(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -13,8 +13,8 @@ import (
|
||||
|
||||
"github.com/XrayR-project/XrayR/api"
|
||||
"github.com/XrayR-project/XrayR/api/sspanel"
|
||||
_ "github.com/XrayR-project/XrayR/cmd/distro/all"
|
||||
"github.com/XrayR-project/XrayR/common/mylego"
|
||||
_ "github.com/XrayR-project/XrayR/main/distro/all"
|
||||
. "github.com/XrayR-project/XrayR/service/controller"
|
||||
)
|
||||
|
||||
|
@@ -41,7 +41,7 @@ func InboundBuilder(config *Config, nodeInfo *api.NodeInfo, tag string) (*core.I
|
||||
// SniffingConfig
|
||||
sniffingConfig := &conf.SniffingConfig{
|
||||
Enabled: true,
|
||||
DestOverride: &conf.StringList{"http", "tls"},
|
||||
DestOverride: &conf.StringList{"http", "tls", "quic", "fakedns"},
|
||||
}
|
||||
if config.DisableSniffing {
|
||||
sniffingConfig.Enabled = false
|
||||
@@ -57,8 +57,8 @@ func InboundBuilder(config *Config, nodeInfo *api.NodeInfo, tag string) (*core.I
|
||||
var proxySetting any
|
||||
// Build Protocol and Protocol setting
|
||||
switch nodeInfo.NodeType {
|
||||
case "V2ray":
|
||||
if nodeInfo.EnableVless {
|
||||
case "V2ray", "Vmess", "Vless":
|
||||
if nodeInfo.EnableVless || (nodeInfo.NodeType == "Vless" && nodeInfo.NodeType != "Vmess") {
|
||||
protocol = "vless"
|
||||
// Enable fallback
|
||||
if config.EnableFallback {
|
||||
@@ -139,7 +139,7 @@ func InboundBuilder(config *Config, nodeInfo *api.NodeInfo, tag string) (*core.I
|
||||
|
||||
setting, err := json.Marshal(proxySetting)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("marshal proxy %s config fialed: %s", nodeInfo.NodeType, err)
|
||||
return nil, fmt.Errorf("marshal proxy %s config failed: %s", nodeInfo.NodeType, err)
|
||||
}
|
||||
inboundDetourConfig.Protocol = protocol
|
||||
inboundDetourConfig.Settings = &setting
|
||||
@@ -164,6 +164,7 @@ func InboundBuilder(config *Config, nodeInfo *api.NodeInfo, tag string) (*core.I
|
||||
headers["Host"] = nodeInfo.Host
|
||||
wsSettings := &conf.WebSocketConfig{
|
||||
AcceptProxyProtocol: config.EnableProxyProtocol,
|
||||
Host: nodeInfo.Host,
|
||||
Path: nodeInfo.Path,
|
||||
Headers: headers,
|
||||
}
|
||||
@@ -171,17 +172,34 @@ func InboundBuilder(config *Config, nodeInfo *api.NodeInfo, tag string) (*core.I
|
||||
case "http":
|
||||
hosts := conf.StringList{nodeInfo.Host}
|
||||
httpSettings := &conf.HTTPConfig{
|
||||
Host: &hosts,
|
||||
Path: nodeInfo.Path,
|
||||
Host: &hosts,
|
||||
Path: nodeInfo.Path,
|
||||
Method: nodeInfo.Method,
|
||||
Headers: nodeInfo.HttpHeaders,
|
||||
}
|
||||
streamSetting.HTTPSettings = httpSettings
|
||||
case "grpc":
|
||||
grpcSettings := &conf.GRPCConfig{
|
||||
ServiceName: nodeInfo.ServiceName,
|
||||
Authority: nodeInfo.Authority,
|
||||
}
|
||||
streamSetting.GRPCConfig = grpcSettings
|
||||
case "quic":
|
||||
quicSettings := &conf.QUICConfig{
|
||||
Header: nodeInfo.Header,
|
||||
Security: nodeInfo.Security,
|
||||
Key: nodeInfo.Key,
|
||||
}
|
||||
streamSetting.QUICSettings = quicSettings
|
||||
case "httpupgrade":
|
||||
httpupgradeSettings := &conf.HttpUpgradeConfig{
|
||||
Headers: nodeInfo.Headers,
|
||||
Path: nodeInfo.Path,
|
||||
Host: nodeInfo.Host,
|
||||
AcceptProxyProtocol: nodeInfo.AcceptProxyProtocol,
|
||||
}
|
||||
streamSetting.HTTPUPGRADESettings = httpupgradeSettings
|
||||
}
|
||||
|
||||
streamSetting.Network = &transportProtocol
|
||||
|
||||
// Build TLS and REALITY settings
|
||||
@@ -287,13 +305,13 @@ func buildVlessFallbacks(fallbackConfigs []*FallBackConfig) ([]*conf.VLessInboun
|
||||
for i, c := range fallbackConfigs {
|
||||
|
||||
if c.Dest == "" {
|
||||
return nil, fmt.Errorf("dest is required for fallback fialed")
|
||||
return nil, fmt.Errorf("dest is required for fallback failed")
|
||||
}
|
||||
|
||||
var dest json.RawMessage
|
||||
dest, err := json.Marshal(c.Dest)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("marshal dest %s config fialed: %s", dest, err)
|
||||
return nil, fmt.Errorf("marshal dest %s config failed: %s", dest, err)
|
||||
}
|
||||
vlessFallBacks[i] = &conf.VLessInboundFallback{
|
||||
Name: c.SNI,
|
||||
@@ -315,13 +333,13 @@ func buildTrojanFallbacks(fallbackConfigs []*FallBackConfig) ([]*conf.TrojanInbo
|
||||
for i, c := range fallbackConfigs {
|
||||
|
||||
if c.Dest == "" {
|
||||
return nil, fmt.Errorf("dest is required for fallback fialed")
|
||||
return nil, fmt.Errorf("dest is required for fallback failed")
|
||||
}
|
||||
|
||||
var dest json.RawMessage
|
||||
dest, err := json.Marshal(c.Dest)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("marshal dest %s config fialed: %s", dest, err)
|
||||
return nil, fmt.Errorf("marshal dest %s config failed: %s", dest, err)
|
||||
}
|
||||
trojanFallBacks[i] = &conf.TrojanInboundFallback{
|
||||
Name: c.SNI,
|
||||
|
@@ -4,7 +4,6 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
"github.com/xtls/xray-core/core"
|
||||
"github.com/xtls/xray-core/infra/conf"
|
||||
|
||||
@@ -17,11 +16,8 @@ func OutboundBuilder(config *Config, nodeInfo *api.NodeInfo, tag string) (*core.
|
||||
outboundDetourConfig.Protocol = "freedom"
|
||||
outboundDetourConfig.Tag = tag
|
||||
|
||||
// Build Send IP address
|
||||
if config.SendIP != "" {
|
||||
ipAddress := net.ParseAddress(config.SendIP)
|
||||
outboundDetourConfig.SendThrough = &conf.Address{Address: ipAddress}
|
||||
}
|
||||
// SendThrough setting
|
||||
outboundDetourConfig.SendThrough = &config.SendIP
|
||||
|
||||
// Freedom Protocol setting
|
||||
var domainStrategy = "Asis"
|
||||
@@ -42,7 +38,7 @@ func OutboundBuilder(config *Config, nodeInfo *api.NodeInfo, tag string) (*core.
|
||||
var setting json.RawMessage
|
||||
setting, err := json.Marshal(proxySetting)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("marshal proxy %s config fialed: %s", nodeInfo.NodeType, err)
|
||||
return nil, fmt.Errorf("marshal proxy %s config failed: %s", nodeInfo.NodeType, err)
|
||||
}
|
||||
outboundDetourConfig.Settings = &setting
|
||||
return outboundDetourConfig.Build()
|
||||
|
Reference in New Issue
Block a user