package panel import ( "encoding/base64" "fmt" "reflect" "strconv" "strings" "time" "github.com/InazumaV/V2bX/common/crypt" "github.com/goccy/go-json" ) type CommonNodeRsp struct { Host string `json:"host"` ServerPort int `json:"server_port"` ServerName string `json:"server_name"` Routes []Route `json:"routes"` BaseConfig BaseConfig `json:"base_config"` } type Route struct { Id int `json:"id"` Match interface{} `json:"match"` Action string `json:"action"` ActionValue string `json:"action_value"` } type BaseConfig struct { PushInterval any `json:"push_interval"` PullInterval any `json:"pull_interval"` } type V2rayNodeRsp struct { Tls int `json:"tls"` Network string `json:"network"` NetworkSettings json.RawMessage `json:"networkSettings"` ServerName string `json:"server_name"` } type ShadowsocksNodeRsp struct { Cipher string `json:"cipher"` ServerKey string `json:"server_key"` } type HysteriaNodeRsp struct { UpMbps int `json:"up_mbps"` DownMbps int `json:"down_mbps"` Obfs string `json:"obfs"` } type NodeInfo struct { Id int Type string Rules Rules Host string Port int Network string RawDNS RawDNS ExtraConfig V2rayExtraConfig NetworkSettings json.RawMessage Tls bool ServerName string UpMbps int DownMbps int ServerKey string Cipher string HyObfs string PushInterval time.Duration PullInterval time.Duration } type RawDNS struct { DNSMap map[string]map[string]interface{} DNSJson []byte } type Rules struct { Regexp []string Protocol []string } type V2rayExtraConfig struct { EnableVless string `json:"EnableVless"` VlessFlow string `json:"VlessFlow"` EnableReality string `json:"EnableReality"` RealityConfig *RealityConfig `json:"RealityConfig"` } type RealityConfig struct { Dest string `yaml:"Dest" json:"Dest"` Xver string `yaml:"Xver" json:"Xver"` ServerNames []string `yaml:"ServerNames" json:"ServerNames"` PrivateKey string `yaml:"PrivateKey" json:"PrivateKey"` MinClientVer string `yaml:"MinClientVer" json:"MinClientVer"` MaxClientVer string `yaml:"MaxClientVer" json:"MaxClientVer"` MaxTimeDiff string `yaml:"MaxTimeDiff" json:"MaxTimeDiff"` ShortIds []string `yaml:"ShortIds" json:"ShortIds"` } func (c *Client) GetNodeInfo() (node *NodeInfo, err error) { const path = "/api/v1/server/UniProxy/config" r, err := c.client. R(). SetHeader("If-None-Match", c.nodeEtag). Get(path) if err = c.checkResponse(r, path, err); err != nil { return } if r.StatusCode() == 304 { return nil, nil } // parse common params node = &NodeInfo{ Id: c.NodeId, Type: c.NodeType, RawDNS: RawDNS{ DNSMap: make(map[string]map[string]interface{}), DNSJson: []byte(""), }, } common := CommonNodeRsp{} err = json.Unmarshal(r.Body(), &common) if err != nil { return nil, fmt.Errorf("decode common params error: %s", err) } for i := range common.Routes { var matchs []string if _, ok := common.Routes[i].Match.(string); ok { matchs = strings.Split(common.Routes[i].Match.(string), ",") } else if _, ok = common.Routes[i].Match.([]string); ok { matchs = common.Routes[i].Match.([]string) } else { temp := common.Routes[i].Match.([]interface{}) matchs = make([]string, len(temp)) for i := range temp { matchs[i] = temp[i].(string) } } switch common.Routes[i].Action { case "block": for _, v := range matchs { if strings.HasPrefix(v, "protocol:") { // protocol node.Rules.Protocol = append(node.Rules.Protocol, strings.TrimPrefix(v, "protocol:")) } else { // domain node.Rules.Regexp = append(node.Rules.Regexp, strings.TrimPrefix(v, "regexp:")) } } case "dns": var domains []string for _, v := range matchs { domains = append(domains, v) } if matchs[0] != "main" { node.RawDNS.DNSMap[strconv.Itoa(i)] = map[string]interface{}{ "address": common.Routes[i].ActionValue, "domains": domains, } } else { dns := []byte(strings.Join(matchs[1:], "")) node.RawDNS.DNSJson = dns break } } } node.ServerName = common.ServerName node.Host = common.Host node.Port = common.ServerPort node.PullInterval = intervalToTime(common.BaseConfig.PullInterval) node.PushInterval = intervalToTime(common.BaseConfig.PushInterval) // parse protocol params switch c.NodeType { case "v2ray": rsp := V2rayNodeRsp{} err = json.Unmarshal(r.Body(), &rsp) if err != nil { return nil, fmt.Errorf("decode v2ray params error: %s", err) } node.Network = rsp.Network node.NetworkSettings = rsp.NetworkSettings node.ServerName = rsp.ServerName if rsp.Tls == 1 { node.Tls = true } err = json.Unmarshal(rsp.NetworkSettings, &node.ExtraConfig) if err != nil { return nil, fmt.Errorf("decode v2ray extra error: %s", err) } if node.ExtraConfig.EnableReality == "true" { if node.ExtraConfig.RealityConfig == nil { node.ExtraConfig.EnableReality = "false" } else { key := crypt.GenX25519Private([]byte(strconv.Itoa(c.NodeId) + c.NodeType + c.Token + node.ExtraConfig.RealityConfig.PrivateKey)) node.ExtraConfig.RealityConfig.PrivateKey = base64.RawURLEncoding.EncodeToString(key) } } case "shadowsocks": rsp := ShadowsocksNodeRsp{} err = json.Unmarshal(r.Body(), &rsp) if err != nil { return nil, fmt.Errorf("decode v2ray params error: %s", err) } node.ServerKey = rsp.ServerKey node.Cipher = rsp.Cipher case "trojan": node.Tls = true case "hysteria": rsp := HysteriaNodeRsp{} err = json.Unmarshal(r.Body(), &rsp) if err != nil { return nil, fmt.Errorf("decode v2ray params error: %s", err) } node.DownMbps = rsp.DownMbps node.UpMbps = rsp.UpMbps node.HyObfs = rsp.Obfs } c.nodeEtag = r.Header().Get("ETag") return } func intervalToTime(i interface{}) time.Duration { switch reflect.TypeOf(i).Kind() { case reflect.Int: return time.Duration(i.(int)) * time.Second case reflect.String: i, _ := strconv.Atoi(i.(string)) return time.Duration(i) * time.Second case reflect.Float64: return time.Duration(i.(float64)) * time.Second default: return time.Duration(reflect.ValueOf(i).Int()) * time.Second } }