mirror of
https://github.com/XrayR-project/XrayR.git
synced 2025-06-08 13:29:54 +00:00
330 lines
7.3 KiB
Go
330 lines
7.3 KiB
Go
package gov2panel
|
|
|
|
import (
|
|
"bufio"
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"log"
|
|
"os"
|
|
"regexp"
|
|
"time"
|
|
|
|
"github.com/XrayR-project/XrayR/api"
|
|
"github.com/gogf/gf/v2/encoding/gjson"
|
|
"github.com/gogf/gf/v2/frame/g"
|
|
"github.com/gogf/gf/v2/net/gclient"
|
|
"github.com/xtls/xray-core/common/net"
|
|
"github.com/xtls/xray-core/infra/conf"
|
|
)
|
|
|
|
// APIClient API config
|
|
type APIClient struct {
|
|
ctx context.Context
|
|
APIHost string
|
|
NodeID int
|
|
Key string
|
|
NodeType string
|
|
EnableVless bool
|
|
VlessFlow string
|
|
Timeout int
|
|
SpeedLimit float64
|
|
DeviceLimit int
|
|
RuleListPath string
|
|
DisableCustomConfig bool
|
|
|
|
LocalRuleList []api.DetectRule
|
|
}
|
|
|
|
// New create an api instance
|
|
func New(apiConfig *api.Config) *APIClient {
|
|
|
|
//https://goframe.org/pages/viewpage.action?pageId=1114381
|
|
|
|
apiClient := &APIClient{
|
|
ctx: context.Background(),
|
|
APIHost: apiConfig.APIHost,
|
|
NodeID: apiConfig.NodeID,
|
|
Key: apiConfig.Key,
|
|
NodeType: apiConfig.NodeType,
|
|
EnableVless: apiConfig.EnableVless,
|
|
VlessFlow: apiConfig.VlessFlow,
|
|
Timeout: apiConfig.Timeout,
|
|
DeviceLimit: apiConfig.DeviceLimit,
|
|
RuleListPath: apiConfig.RuleListPath,
|
|
DisableCustomConfig: apiConfig.DisableCustomConfig,
|
|
|
|
LocalRuleList: readLocalRuleList(apiConfig.RuleListPath), //加载本地路由规则
|
|
}
|
|
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
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
file.Close()
|
|
}
|
|
|
|
return LocalRuleList
|
|
}
|
|
|
|
func (c *APIClient) GetNodeInfo() (nodeInfo *api.NodeInfo, err error) {
|
|
|
|
apiPath := "/api/server/config"
|
|
reslutJson, err := c.sendRequest(
|
|
nil,
|
|
"POST",
|
|
apiPath,
|
|
g.Map{})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if reslutJson.Get("data").String() == "" {
|
|
return nil, errors.New("gov2panel node config data is null")
|
|
}
|
|
|
|
if reslutJson.Get("data.port").Int() == 0 {
|
|
return nil, errors.New("server port must > 0")
|
|
}
|
|
|
|
nodeInfo = new(api.NodeInfo)
|
|
err = reslutJson.Get("data").Scan(nodeInfo)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("parse node info failed: \nError: %v", err)
|
|
}
|
|
|
|
routes := make([]route, 0)
|
|
err = reslutJson.Get("data.routes").Scan(&routes)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("parse node routes failed: \nError: %v", err)
|
|
}
|
|
|
|
nodeInfo.NodeType = c.NodeType
|
|
nodeInfo.NodeID = c.NodeID
|
|
nodeInfo.EnableVless = c.EnableVless
|
|
nodeInfo.VlessFlow = c.VlessFlow
|
|
|
|
nodeInfo.AlterID = 0
|
|
|
|
nodeInfo.NameServerConfig = parseDNSConfig(routes)
|
|
|
|
return nodeInfo, nil
|
|
|
|
}
|
|
|
|
func parseDNSConfig(routes []route) (nameServerList []*conf.NameServerConfig) {
|
|
|
|
nameServerList = make([]*conf.NameServerConfig, 0)
|
|
for i := range routes {
|
|
if routes[i].Action == "dns" {
|
|
nameServerList = append(nameServerList, &conf.NameServerConfig{
|
|
Address: &conf.Address{Address: net.ParseAddress(routes[i].ActionValue)},
|
|
Domains: routes[i].Match,
|
|
})
|
|
}
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
// GetUserList will pull user form panel
|
|
func (c *APIClient) GetUserList() (UserList *[]api.UserInfo, err error) {
|
|
|
|
apiPath := "/api/server/user"
|
|
|
|
switch c.NodeType {
|
|
case "V2ray", "Trojan", "Shadowsocks", "Vmess", "Vless":
|
|
break
|
|
default:
|
|
return nil, fmt.Errorf("unsupported node type: %s", c.NodeType)
|
|
}
|
|
|
|
reslutJson, err := c.sendRequest(
|
|
nil,
|
|
"GET",
|
|
apiPath,
|
|
g.Map{})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var users []*user
|
|
reslutJson.Get("data.users").Scan(&users)
|
|
|
|
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
|
|
}
|
|
|
|
func (c *APIClient) ReportNodeStatus(nodeStatus *api.NodeStatus) (err error) {
|
|
return
|
|
}
|
|
|
|
func (c *APIClient) ReportNodeOnlineUsers(onlineUser *[]api.OnlineUser) (err error) {
|
|
return
|
|
}
|
|
|
|
// ReportUserTraffic reports the user traffic
|
|
func (c *APIClient) ReportUserTraffic(userTraffic *[]api.UserTraffic) (err error) {
|
|
apiPath := "/api/server/push"
|
|
reslutJson, err := c.sendRequest(
|
|
nil,
|
|
"POST",
|
|
apiPath,
|
|
g.Map{
|
|
"data": userTraffic,
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if reslutJson.Get("code").Int() != 0 {
|
|
return errors.New(reslutJson.Get("message").String())
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func (c *APIClient) Describe() api.ClientInfo {
|
|
return api.ClientInfo{APIHost: c.APIHost, NodeID: c.NodeID, Key: c.Key, NodeType: c.NodeType}
|
|
}
|
|
|
|
// GetNodeRule implements the API interface
|
|
func (c *APIClient) GetNodeRule() (*[]api.DetectRule, error) {
|
|
ruleList := c.LocalRuleList
|
|
|
|
apiPath := "/api/server/config"
|
|
reslutJson, err := c.sendRequest(
|
|
nil,
|
|
"POST",
|
|
apiPath,
|
|
g.Map{})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
routes := make([]route, 0)
|
|
err = reslutJson.Get("data.routes").Scan(&routes)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("parse node routes failed: \nError: %v", err)
|
|
}
|
|
|
|
for i := range routes {
|
|
if routes[i].Action == "block" {
|
|
for _, v := range routes[i].Match {
|
|
ruleList = append(ruleList, api.DetectRule{
|
|
ID: i,
|
|
Pattern: regexp.MustCompile(v),
|
|
})
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
return &ruleList, nil
|
|
|
|
}
|
|
|
|
func (c *APIClient) ReportIllegal(detectResultList *[]api.DetectResult) (err error) {
|
|
return
|
|
}
|
|
|
|
func (c *APIClient) Debug() {
|
|
|
|
}
|
|
|
|
// request 统一请求接口
|
|
func (c *APIClient) sendRequest(headerM map[string]string, method string, url string, data g.Map) (reslutJson *gjson.Json, err error) {
|
|
url = c.APIHost + url
|
|
|
|
client := gclient.New()
|
|
|
|
var gResponse *gclient.Response
|
|
|
|
if c.Timeout > 0 {
|
|
client.SetTimeout(time.Duration(c.Timeout) * time.Second) //方法用于设置当前请求超时时间
|
|
} else {
|
|
client.SetTimeout(5 * time.Second)
|
|
}
|
|
client.Retry(3, 10*time.Second) //方法用于设置请求失败时重连次数和重连间隔。
|
|
|
|
client.SetHeaderMap(headerM)
|
|
client.SetHeader("Content-Type", "application/json")
|
|
|
|
data["token"] = c.Key
|
|
data["node_id"] = c.NodeID
|
|
|
|
switch method {
|
|
case "GET":
|
|
gResponse, err = client.Get(c.ctx, url, data)
|
|
case "POST":
|
|
gResponse, err = client.Post(c.ctx, url, data)
|
|
default:
|
|
err = fmt.Errorf("unsupported method: %s", method)
|
|
return
|
|
}
|
|
|
|
if err != nil {
|
|
return
|
|
}
|
|
defer gResponse.Close()
|
|
|
|
reslutJson = gjson.New(gResponse.ReadAllString())
|
|
if reslutJson == nil {
|
|
err = fmt.Errorf("http reslut to json, err : %s", gResponse.ReadAllString())
|
|
}
|
|
if reslutJson.Get("code").Int() != 0 {
|
|
err = errors.New(reslutJson.Get("message").String())
|
|
return
|
|
}
|
|
|
|
return
|
|
}
|