mirror of
https://github.com/XrayR-project/XrayR.git
synced 2025-06-15 08:49:53 +00:00
对接GoV2Panel面板 (#474)
* Update actions * up --------- Co-authored-by: thank243 <thank243@gmail.com>
This commit is contained in:
parent
b8d40c201b
commit
4c52e33adb
@ -62,6 +62,7 @@ A Xray backend framework that can easily support many panels.
|
|||||||
| [PMPanel](https://github.com/ByteInternetHK/PMPanel) | √ | √ | √ |
|
| [PMPanel](https://github.com/ByteInternetHK/PMPanel) | √ | √ | √ |
|
||||||
| [ProxyPanel](https://github.com/ProxyPanel/ProxyPanel) | √ | √ | √ |
|
| [ProxyPanel](https://github.com/ProxyPanel/ProxyPanel) | √ | √ | √ |
|
||||||
| [WHMCS (V2RaySocks)](https://v2raysocks.doxtex.com/) | √ | √ | √ |
|
| [WHMCS (V2RaySocks)](https://v2raysocks.doxtex.com/) | √ | √ | √ |
|
||||||
|
| [GoV2Panel](https://github.com/pingProMax/gov2panel) | √ | √ | √ |
|
||||||
|
|
||||||
## 软件安装
|
## 软件安装
|
||||||
|
|
||||||
|
402
api/gov2panel/gov2panel.go
Normal file
402
api/gov2panel/gov2panel.go
Normal file
@ -0,0 +1,402 @@
|
|||||||
|
package gov2panel
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"sync/atomic"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/bitly/go-simplejson"
|
||||||
|
"github.com/go-resty/resty/v2"
|
||||||
|
"github.com/gogf/gf/v2/util/gconv"
|
||||||
|
"github.com/xtls/xray-core/common/net"
|
||||||
|
"github.com/xtls/xray-core/infra/conf"
|
||||||
|
|
||||||
|
"github.com/XrayR-project/XrayR/api"
|
||||||
|
)
|
||||||
|
|
||||||
|
// APIClient create an api client to the panel.
|
||||||
|
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
|
||||||
|
resp atomic.Value
|
||||||
|
eTags map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
// New create an api instance
|
||||||
|
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{
|
||||||
|
"node_id": strconv.Itoa(apiConfig.NodeID),
|
||||||
|
"node_type": 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)
|
||||||
|
defer file.Close()
|
||||||
|
// handle errors while opening
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error when opening file: %s", err)
|
||||||
|
return LocalRuleList
|
||||||
|
}
|
||||||
|
|
||||||
|
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) (*simplejson.Json, error) {
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("request %s failed: %v", c.assembleURL(path), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if res.StatusCode() > 399 {
|
||||||
|
return nil, fmt.Errorf("request %s failed: %s, %v", c.assembleURL(path), res.String(), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
rtn, err := simplejson.NewJson(res.Body())
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("ret %s invalid", res.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
return rtn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetNodeInfo will pull NodeInfo Config from panel
|
||||||
|
func (c *APIClient) GetNodeInfo() (nodeInfo *api.NodeInfo, err error) {
|
||||||
|
server := new(serverConfig)
|
||||||
|
path := "/api/server/config"
|
||||||
|
|
||||||
|
res, err := c.client.R().
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
// update etag
|
||||||
|
if res.Header().Get("Etag") != "" && res.Header().Get("Etag") != c.eTags["node"] {
|
||||||
|
c.eTags["node"] = res.Header().Get("Etag")
|
||||||
|
}
|
||||||
|
|
||||||
|
nodeInfoResp, err := c.parseResponse(res, path, err)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
b, _ := nodeInfoResp.Encode()
|
||||||
|
json.Unmarshal(b, server)
|
||||||
|
|
||||||
|
if gconv.Uint32(server.Port) == 0 {
|
||||||
|
return nil, errors.New("server port must > 0")
|
||||||
|
}
|
||||||
|
|
||||||
|
c.resp.Store(server)
|
||||||
|
|
||||||
|
switch c.NodeType {
|
||||||
|
case "V2ray":
|
||||||
|
nodeInfo, err = c.parseV2rayNodeResponse(server)
|
||||||
|
case "Trojan":
|
||||||
|
nodeInfo, err = c.parseTrojanNodeResponse(server)
|
||||||
|
case "Shadowsocks":
|
||||||
|
nodeInfo, err = c.parseSSNodeResponse(server)
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unsupported node type: %s", c.NodeType)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("parse node info failed: %s, \nError: %v", res.String(), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nodeInfo, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUserList will pull user form panel
|
||||||
|
func (c *APIClient) GetUserList() (UserList *[]api.UserInfo, err error) {
|
||||||
|
var users []*user
|
||||||
|
path := "/api/server/user"
|
||||||
|
|
||||||
|
switch c.NodeType {
|
||||||
|
case "V2ray", "Trojan", "Shadowsocks":
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unsupported node type: %s", c.NodeType)
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := c.client.R().
|
||||||
|
SetHeader("If-None-Match", c.eTags["users"]).
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
// update etag
|
||||||
|
if res.Header().Get("Etag") != "" && res.Header().Get("Etag") != c.eTags["users"] {
|
||||||
|
c.eTags["users"] = res.Header().Get("Etag")
|
||||||
|
}
|
||||||
|
|
||||||
|
usersResp, err := c.parseResponse(res, path, err)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
b, _ := usersResp.Get("users").Encode()
|
||||||
|
json.Unmarshal(b, &users)
|
||||||
|
if len(users) == 0 {
|
||||||
|
return nil, errors.New("users is null")
|
||||||
|
}
|
||||||
|
|
||||||
|
userList := make([]api.UserInfo, len(users))
|
||||||
|
for i := 0; i < len(users); i++ {
|
||||||
|
u := api.UserInfo{
|
||||||
|
UID: users[i].Id,
|
||||||
|
UUID: users[i].Uuid,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Support 1.7.1 speed limit
|
||||||
|
if c.SpeedLimit > 0 {
|
||||||
|
u.SpeedLimit = uint64(c.SpeedLimit * 1000000 / 8)
|
||||||
|
} else {
|
||||||
|
u.SpeedLimit = uint64(users[i].SpeedLimit * 1000000 / 8)
|
||||||
|
}
|
||||||
|
|
||||||
|
u.DeviceLimit = c.DeviceLimit // todo waiting v2board send configuration
|
||||||
|
u.Email = u.UUID + "@gov2panel.user"
|
||||||
|
if c.NodeType == "Shadowsocks" {
|
||||||
|
u.Passwd = u.UUID
|
||||||
|
}
|
||||||
|
userList[i] = u
|
||||||
|
}
|
||||||
|
|
||||||
|
return &userList, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReportUserTraffic reports the user traffic
|
||||||
|
func (c *APIClient) ReportUserTraffic(userTraffic *[]api.UserTraffic) error {
|
||||||
|
path := "/api/server/push"
|
||||||
|
|
||||||
|
res, err := c.client.R().SetBody(userTraffic).ForceContentType("application/json").Post(path)
|
||||||
|
_, err = c.parseResponse(res, path, err)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetNodeRule implements the API interface
|
||||||
|
func (c *APIClient) GetNodeRule() (*[]api.DetectRule, error) {
|
||||||
|
routes := c.resp.Load().(*serverConfig).Routes
|
||||||
|
|
||||||
|
ruleList := c.LocalRuleList
|
||||||
|
|
||||||
|
for i := range routes {
|
||||||
|
if routes[i].Action == "block" {
|
||||||
|
|
||||||
|
ruleList = append(ruleList, api.DetectRule{
|
||||||
|
ID: i,
|
||||||
|
Pattern: regexp.MustCompile(strings.Join(routes[i].Match, "|")),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &ruleList, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReportNodeStatus implements the API interface
|
||||||
|
func (c *APIClient) ReportNodeStatus(nodeStatus *api.NodeStatus) (err error) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReportNodeOnlineUsers implements the API interface
|
||||||
|
func (c *APIClient) ReportNodeOnlineUsers(onlineUserList *[]api.OnlineUser) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReportIllegal implements the API interface
|
||||||
|
func (c *APIClient) ReportIllegal(detectResultList *[]api.DetectResult) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseTrojanNodeResponse parse the response for the given nodeInfo format
|
||||||
|
func (c *APIClient) parseTrojanNodeResponse(s *serverConfig) (*api.NodeInfo, error) {
|
||||||
|
// Create GeneralNodeInfo
|
||||||
|
nodeInfo := &api.NodeInfo{
|
||||||
|
NodeType: c.NodeType,
|
||||||
|
NodeID: c.NodeID,
|
||||||
|
Port: gconv.Uint32(s.Port),
|
||||||
|
TransportProtocol: "tcp",
|
||||||
|
EnableTLS: true,
|
||||||
|
Host: s.Host,
|
||||||
|
ServiceName: s.Sni,
|
||||||
|
NameServerConfig: s.parseDNSConfig(),
|
||||||
|
}
|
||||||
|
return nodeInfo, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseSSNodeResponse parse the response for the given nodeInfo format
|
||||||
|
func (c *APIClient) parseSSNodeResponse(s *serverConfig) (*api.NodeInfo, error) {
|
||||||
|
var header json.RawMessage
|
||||||
|
|
||||||
|
if s.Obfs == "http" {
|
||||||
|
path := "/"
|
||||||
|
if p := s.ObfsSettings.Path; p != "" {
|
||||||
|
if strings.HasPrefix(p, "/") {
|
||||||
|
path = p
|
||||||
|
} else {
|
||||||
|
path += p
|
||||||
|
}
|
||||||
|
}
|
||||||
|
h := simplejson.New()
|
||||||
|
h.Set("type", "http")
|
||||||
|
h.SetPath([]string{"request", "path"}, path)
|
||||||
|
header, _ = h.Encode()
|
||||||
|
}
|
||||||
|
// Create GeneralNodeInfo
|
||||||
|
return &api.NodeInfo{
|
||||||
|
NodeType: c.NodeType,
|
||||||
|
NodeID: c.NodeID,
|
||||||
|
Port: gconv.Uint32(s.Port),
|
||||||
|
TransportProtocol: "tcp",
|
||||||
|
CypherMethod: s.Encryption,
|
||||||
|
ServerKey: s.ServerKey, // shadowsocks2022 share key
|
||||||
|
NameServerConfig: s.parseDNSConfig(),
|
||||||
|
Header: header,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseV2rayNodeResponse parse the response for the given nodeInfo format
|
||||||
|
func (c *APIClient) parseV2rayNodeResponse(s *serverConfig) (*api.NodeInfo, error) {
|
||||||
|
var (
|
||||||
|
header json.RawMessage
|
||||||
|
enableTLS bool
|
||||||
|
)
|
||||||
|
|
||||||
|
switch s.Net {
|
||||||
|
case "tcp":
|
||||||
|
if s.Header != nil {
|
||||||
|
if httpHeader, err := s.Header.MarshalJSON(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else {
|
||||||
|
header = httpHeader
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.TLS == "tls" {
|
||||||
|
enableTLS = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create GeneralNodeInfo
|
||||||
|
return &api.NodeInfo{
|
||||||
|
NodeType: c.NodeType,
|
||||||
|
NodeID: c.NodeID,
|
||||||
|
Port: gconv.Uint32(s.Port),
|
||||||
|
AlterID: 0,
|
||||||
|
TransportProtocol: s.Net,
|
||||||
|
EnableTLS: enableTLS,
|
||||||
|
Path: s.Path,
|
||||||
|
Host: s.Host,
|
||||||
|
EnableVless: c.EnableVless,
|
||||||
|
VlessFlow: c.VlessFlow,
|
||||||
|
ServiceName: s.Sni,
|
||||||
|
Header: header,
|
||||||
|
NameServerConfig: s.parseDNSConfig(),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *serverConfig) parseDNSConfig() (nameServerList []*conf.NameServerConfig) {
|
||||||
|
for i := range s.Routes {
|
||||||
|
if s.Routes[i].Action == "dns" {
|
||||||
|
nameServerList = append(nameServerList, &conf.NameServerConfig{
|
||||||
|
Address: &conf.Address{Address: net.ParseAddress(s.Routes[i].ActionValue)},
|
||||||
|
Domains: s.Routes[i].Match,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
101
api/gov2panel/gov2panel_test.go
Normal file
101
api/gov2panel/gov2panel_test.go
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
package gov2panel_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/XrayR-project/XrayR/api"
|
||||||
|
"github.com/XrayR-project/XrayR/api/gov2panel"
|
||||||
|
)
|
||||||
|
|
||||||
|
func CreateClient() api.API {
|
||||||
|
apiConfig := &api.Config{
|
||||||
|
APIHost: "http://localhost:8080",
|
||||||
|
Key: "123456",
|
||||||
|
NodeID: 1,
|
||||||
|
NodeType: "V2ray",
|
||||||
|
}
|
||||||
|
client := gov2panel.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 := gov2panel.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 := gov2panel.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)
|
||||||
|
}
|
46
api/gov2panel/model.go
Normal file
46
api/gov2panel/model.go
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
package gov2panel
|
||||||
|
|
||||||
|
import "encoding/json"
|
||||||
|
|
||||||
|
type serverConfig struct {
|
||||||
|
v2ray
|
||||||
|
shadowsocks
|
||||||
|
//---
|
||||||
|
Routes []route `json:"routes"`
|
||||||
|
Header *json.RawMessage `json:"header"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type v2ray struct {
|
||||||
|
Port string `json:"port"`
|
||||||
|
Scy string `json:"scy"`
|
||||||
|
Net string `json:"net"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
Host string `json:"host"`
|
||||||
|
Path string `json:"path"`
|
||||||
|
TLS string `json:"tls"`
|
||||||
|
Sni string `json:"sni"`
|
||||||
|
Alpn string `json:"alpn"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type shadowsocks struct {
|
||||||
|
Encryption string `json:"encryption"`
|
||||||
|
Obfs string `json:"obfs"`
|
||||||
|
ObfsSettings struct {
|
||||||
|
Path string `json:"path"`
|
||||||
|
Host string `json:"host"`
|
||||||
|
} `json:"obfs_settings"`
|
||||||
|
ServerKey string `json:"server_key"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type route struct {
|
||||||
|
Id int `json:"id"`
|
||||||
|
Match []string `json:"match"`
|
||||||
|
Action string `json:"action"`
|
||||||
|
ActionValue string `json:"action_value"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type user struct {
|
||||||
|
Id int `json:"id"`
|
||||||
|
Uuid string `json:"uuid"`
|
||||||
|
SpeedLimit int `json:"speed_limit"`
|
||||||
|
}
|
3
go.mod
3
go.mod
@ -14,6 +14,7 @@ require (
|
|||||||
github.com/fsnotify/fsnotify v1.6.0
|
github.com/fsnotify/fsnotify v1.6.0
|
||||||
github.com/go-acme/lego/v4 v4.14.2
|
github.com/go-acme/lego/v4 v4.14.2
|
||||||
github.com/go-resty/resty/v2 v2.9.1
|
github.com/go-resty/resty/v2 v2.9.1
|
||||||
|
github.com/gogf/gf/v2 v2.5.4
|
||||||
github.com/patrickmn/go-cache v2.1.0+incompatible
|
github.com/patrickmn/go-cache v2.1.0+incompatible
|
||||||
github.com/r3labs/diff/v2 v2.15.1
|
github.com/r3labs/diff/v2 v2.15.1
|
||||||
github.com/redis/go-redis/v9 v9.2.1
|
github.com/redis/go-redis/v9 v9.2.1
|
||||||
@ -202,6 +203,8 @@ require (
|
|||||||
github.com/yandex-cloud/go-sdk v0.0.0-20220805164847-cf028e604997 // indirect
|
github.com/yandex-cloud/go-sdk v0.0.0-20220805164847-cf028e604997 // indirect
|
||||||
github.com/yusufpapurcu/wmi v1.2.3 // indirect
|
github.com/yusufpapurcu/wmi v1.2.3 // indirect
|
||||||
go.opencensus.io v0.24.0 // 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.uber.org/atomic v1.11.0 // indirect
|
go.uber.org/atomic v1.11.0 // indirect
|
||||||
go.uber.org/multierr v1.9.0 // indirect
|
go.uber.org/multierr v1.9.0 // indirect
|
||||||
go.uber.org/ratelimit v0.2.0 // indirect
|
go.uber.org/ratelimit v0.2.0 // indirect
|
||||||
|
6
go.sum
6
go.sum
@ -298,6 +298,8 @@ github.com/gobs/pretty v0.0.0-20180724170744-09732c25a95b/go.mod h1:Xo4aNUOrJnVr
|
|||||||
github.com/goccy/go-json v0.7.8/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
github.com/goccy/go-json v0.7.8/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||||
github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA=
|
github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA=
|
||||||
github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||||
|
github.com/gogf/gf/v2 v2.5.4 h1:UBCSw8mInkHmEqL0E1LYc6QhSpaNFY/wHcFrTI/rzTk=
|
||||||
|
github.com/gogf/gf/v2 v2.5.4/go.mod h1:7yf5qp0BznfsYx7Sw49m3mQvBsHpwAjJk3Q9ZnKoUEc=
|
||||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||||
github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A=
|
github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A=
|
||||||
@ -911,6 +913,10 @@ go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
|||||||
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
|
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
|
||||||
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
|
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
|
||||||
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
|
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
|
||||||
|
go.opentelemetry.io/otel v1.14.0 h1:/79Huy8wbf5DnIPhemGB+zEPVwnN6fuQybr/SRXa6hM=
|
||||||
|
go.opentelemetry.io/otel v1.14.0/go.mod h1:o4buv+dJzx8rohcUeRmWUZhqupFvzWis188WlggnNeU=
|
||||||
|
go.opentelemetry.io/otel/trace v1.14.0 h1:wp2Mmvj41tDsyAJXiWDWpfNsOiIyd38fy85pyKcFq/M=
|
||||||
|
go.opentelemetry.io/otel/trace v1.14.0/go.mod h1:8avnQLK+CG77yNLUae4ea2JDQ6iT+gozhnZjy/rw9G8=
|
||||||
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
|
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
|
||||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||||
|
@ -13,7 +13,7 @@ ConnectionConfig:
|
|||||||
DownlinkOnly: 4 # Time limit when the connection is closed after the uplink is closed, Second
|
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
|
BufferSize: 64 # The internal cache size of each connection, kB
|
||||||
Nodes:
|
Nodes:
|
||||||
- PanelType: "SSpanel" # Panel type: SSpanel, NewV2board, PMpanel, Proxypanel, V2RaySocks
|
- PanelType: "SSpanel" # Panel type: SSpanel, NewV2board, PMpanel, Proxypanel, V2RaySocks, GoV2Panel
|
||||||
ApiConfig:
|
ApiConfig:
|
||||||
ApiHost: "http://127.0.0.1:667"
|
ApiHost: "http://127.0.0.1:667"
|
||||||
ApiKey: "123"
|
ApiKey: "123"
|
||||||
@ -78,7 +78,7 @@ Nodes:
|
|||||||
ALICLOUD_ACCESS_KEY: aaa
|
ALICLOUD_ACCESS_KEY: aaa
|
||||||
ALICLOUD_SECRET_KEY: bbb
|
ALICLOUD_SECRET_KEY: bbb
|
||||||
|
|
||||||
# - PanelType: "SSpanel" # Panel type: SSpanel, V2board, NewV2board, PMpanel, Proxypanel, V2RaySocks
|
# - PanelType: "SSpanel" # Panel type: SSpanel, V2board, NewV2board, PMpanel, Proxypanel, V2RaySocks, GoV2Panel
|
||||||
# ApiConfig:
|
# ApiConfig:
|
||||||
# ApiHost: "http://127.0.0.1:668"
|
# ApiHost: "http://127.0.0.1:668"
|
||||||
# ApiKey: "123"
|
# ApiKey: "123"
|
||||||
|
@ -6,6 +6,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"github.com/XrayR-project/XrayR/api/gov2panel"
|
||||||
"github.com/XrayR-project/XrayR/api/newV2board"
|
"github.com/XrayR-project/XrayR/api/newV2board"
|
||||||
"github.com/XrayR-project/XrayR/app/mydispatcher"
|
"github.com/XrayR-project/XrayR/app/mydispatcher"
|
||||||
|
|
||||||
@ -184,6 +185,8 @@ func (p *Panel) Start() {
|
|||||||
apiClient = proxypanel.New(nodeConfig.ApiConfig)
|
apiClient = proxypanel.New(nodeConfig.ApiConfig)
|
||||||
case "V2RaySocks":
|
case "V2RaySocks":
|
||||||
apiClient = v2raysocks.New(nodeConfig.ApiConfig)
|
apiClient = v2raysocks.New(nodeConfig.ApiConfig)
|
||||||
|
case "GoV2Panel":
|
||||||
|
apiClient = gov2panel.New(nodeConfig.ApiConfig)
|
||||||
default:
|
default:
|
||||||
log.Panicf("Unsupport panel type: %s", nodeConfig.PanelType)
|
log.Panicf("Unsupport panel type: %s", nodeConfig.PanelType)
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user