feat(proxy): enhance proxy and group handling with new configuration options
This commit is contained in:
committed by
Leif Draven
parent
82e447c55e
commit
224365ce79
+26
-28
@@ -1,6 +1,8 @@
|
||||
package adapter
|
||||
|
||||
import (
|
||||
"embed"
|
||||
|
||||
"github.com/perfect-panel/server/internal/model/server"
|
||||
"github.com/perfect-panel/server/pkg/adapter/clash"
|
||||
"github.com/perfect-panel/server/pkg/adapter/general"
|
||||
@@ -13,9 +15,11 @@ import (
|
||||
"github.com/perfect-panel/server/pkg/adapter/v2rayn"
|
||||
)
|
||||
|
||||
//go:embed template/*
|
||||
var TemplateFS embed.FS
|
||||
|
||||
var (
|
||||
AutoSelect = "Auto - UrlTest"
|
||||
Selection = "Selection"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
@@ -30,69 +34,63 @@ type Adapter struct {
|
||||
|
||||
func NewAdapter(cfg *Config) *Adapter {
|
||||
// 转换服务器列表
|
||||
proxies := adapterProxies(cfg.Nodes)
|
||||
defaultGroup := FindDefaultGroup(cfg.Rules)
|
||||
proxies, nodes, tags := adapterProxies(cfg.Nodes)
|
||||
// 转换规则组
|
||||
g, r := adapterRules(cfg.Rules)
|
||||
|
||||
// 生成代理组
|
||||
proxyGroup, nodes := generateProxyGroup(proxies)
|
||||
|
||||
// 加入兜底节点
|
||||
for i, group := range g {
|
||||
if group.Direct {
|
||||
continue
|
||||
}
|
||||
if len(group.Proxies) == 0 {
|
||||
p := append([]string{AutoSelect, Selection}, nodes...)
|
||||
g[i].Proxies = append(p, "DIRECT")
|
||||
}
|
||||
g, r, d := adapterRules(cfg.Rules)
|
||||
if d == "" {
|
||||
d = AutoSelect
|
||||
}
|
||||
|
||||
// 生成默认代理组
|
||||
proxyGroup := append(generateDefaultGroup(), g...)
|
||||
// 合并代理组
|
||||
proxyGroup = RemoveEmptyGroup(append(proxyGroup, g...))
|
||||
// 处理标签
|
||||
proxyGroup = adapterTags(cfg.Tags, proxyGroup)
|
||||
|
||||
proxyGroup = SortGroups(proxyGroup, nodes, tags, d)
|
||||
return &Adapter{
|
||||
Adapter: proxy.Adapter{
|
||||
Proxies: proxies,
|
||||
Group: SortGroups(proxyGroup, defaultGroup),
|
||||
Rules: r,
|
||||
Nodes: nodes,
|
||||
Default: defaultGroup,
|
||||
Proxies: proxies,
|
||||
Group: proxyGroup,
|
||||
Rules: r,
|
||||
Nodes: nodes,
|
||||
Default: d,
|
||||
TemplateFS: &TemplateFS,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// BuildClash generates a Clash configuration for the given UUID.
|
||||
func (m *Adapter) BuildClash(uuid string) ([]byte, error) {
|
||||
client := clash.NewClash(m.Adapter)
|
||||
return client.Build(uuid)
|
||||
}
|
||||
|
||||
// BuildGeneral generates a general configuration for the given UUID.
|
||||
func (m *Adapter) BuildGeneral(uuid string) []byte {
|
||||
return general.GenerateBase64General(m.Proxies, uuid)
|
||||
}
|
||||
|
||||
// BuildLoon generates a Loon configuration for the given UUID.
|
||||
func (m *Adapter) BuildLoon(uuid string) []byte {
|
||||
return loon.BuildLoon(m.Proxies, uuid)
|
||||
}
|
||||
|
||||
// BuildQuantumultX generates a Quantumult X configuration for the given UUID.
|
||||
func (m *Adapter) BuildQuantumultX(uuid string) string {
|
||||
return quantumultx.BuildQuantumultX(m.Proxies, uuid)
|
||||
}
|
||||
|
||||
// BuildSingbox generates a Singbox configuration for the given UUID.
|
||||
func (m *Adapter) BuildSingbox(uuid string) ([]byte, error) {
|
||||
return singbox.BuildSingbox(m.Adapter, uuid)
|
||||
}
|
||||
|
||||
func (m *Adapter) BuildShadowrocket(uuid string, userInfo shadowrocket.UserInfo) []byte {
|
||||
return shadowrocket.BuildShadowrocket(m.Proxies, uuid, userInfo)
|
||||
}
|
||||
|
||||
// BuildSurfboard generates a Surfboard configuration for the given site name and user info.
|
||||
func (m *Adapter) BuildSurfboard(siteName string, user surfboard.UserInfo) []byte {
|
||||
return surfboard.BuildSurfboard(m.Adapter, siteName, user)
|
||||
}
|
||||
|
||||
// BuildV2rayN generates a V2rayN configuration for the given UUID.
|
||||
func (m *Adapter) BuildV2rayN(uuid string) []byte {
|
||||
return v2rayn.NewV2rayN(m.Adapter).Build(uuid)
|
||||
}
|
||||
|
||||
+39
-13
@@ -1,8 +1,11 @@
|
||||
package clash
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"text/template"
|
||||
|
||||
"github.com/Masterminds/sprig/v3"
|
||||
"github.com/perfect-panel/server/pkg/adapter/proxy"
|
||||
"github.com/perfect-panel/server/pkg/logger"
|
||||
"gopkg.in/yaml.v3"
|
||||
@@ -20,22 +23,16 @@ func NewClash(adapter proxy.Adapter) *Clash {
|
||||
|
||||
func (c *Clash) Build(uuid string) ([]byte, error) {
|
||||
var proxies []Proxy
|
||||
for _, v := range c.Proxies {
|
||||
p, err := c.parseProxy(v, uuid)
|
||||
for _, proxied := range c.Adapter.Proxies {
|
||||
p, err := c.parseProxy(proxied, uuid)
|
||||
if err != nil {
|
||||
logger.Errorf("Failed to parse proxy for %s: %s", v.Name, err.Error())
|
||||
logger.Errorw("Failed to parse proxy", logger.Field("error", err), logger.Field("proxy", p.Name))
|
||||
continue
|
||||
}
|
||||
proxies = append(proxies, *p)
|
||||
}
|
||||
var rawConfig RawConfig
|
||||
if err := yaml.Unmarshal([]byte(DefaultTemplate), &rawConfig); err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal template: %w", err)
|
||||
}
|
||||
rawConfig.Proxies = proxies
|
||||
// generate proxy groups
|
||||
var groups []ProxyGroup
|
||||
for _, group := range c.Group {
|
||||
for _, group := range c.Adapter.Group {
|
||||
groups = append(groups, ProxyGroup{
|
||||
Name: group.Name,
|
||||
Type: string(group.Type),
|
||||
@@ -44,9 +41,38 @@ func (c *Clash) Build(uuid string) ([]byte, error) {
|
||||
Interval: group.Interval,
|
||||
})
|
||||
}
|
||||
rawConfig.ProxyGroups = groups
|
||||
rawConfig.Rules = append(c.Rules, fmt.Sprintf("MATCH,%s", c.Default))
|
||||
return yaml.Marshal(&rawConfig)
|
||||
var rules = append(c.Rules, fmt.Sprintf("MATCH,%s", c.Default))
|
||||
|
||||
tmplBytes, err := c.TemplateFS.ReadFile("template/clash.tpl")
|
||||
if err != nil {
|
||||
logger.Errorw("Failed to read template file", logger.Field("error", err))
|
||||
return nil, fmt.Errorf("failed to read template file: %w", err)
|
||||
}
|
||||
tpl, err := template.New("clash.yaml").Funcs(sprig.FuncMap()).Funcs(template.FuncMap{
|
||||
"toYaml": func(v interface{}) string {
|
||||
out, err := yaml.Marshal(v)
|
||||
if err != nil {
|
||||
return fmt.Sprintf("# YAML encode error: %v", err.Error())
|
||||
}
|
||||
return string(out)
|
||||
},
|
||||
}).Parse(string(tmplBytes))
|
||||
if err != nil {
|
||||
logger.Errorw("[Clash] Failed to parse template", logger.Field("error", err))
|
||||
return nil, fmt.Errorf("failed to parse template: %w", err)
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
err = tpl.Execute(&buf, map[string]interface{}{
|
||||
"Proxies": proxies,
|
||||
"ProxyGroups": groups,
|
||||
"Rules": rules,
|
||||
})
|
||||
if err != nil {
|
||||
logger.Errorw("[Clash] Failed to execute template", logger.Field("error", err))
|
||||
return nil, fmt.Errorf("failed to execute template: %w", err)
|
||||
}
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
func (c *Clash) parseProxy(p proxy.Proxy, uuid string) (*Proxy, error) {
|
||||
|
||||
@@ -1,31 +1,50 @@
|
||||
package clash
|
||||
|
||||
const DefaultTemplate = `
|
||||
mixed-port: 7890
|
||||
mode: rule
|
||||
ipv6: true
|
||||
allow-lan: true
|
||||
bind-address: "*"
|
||||
mode: rule
|
||||
log-level: info
|
||||
external-controller: 127.0.0.1:9090
|
||||
global-client-fingerprint: chrome
|
||||
mixed-port: 7890
|
||||
log-level: error
|
||||
unified-delay: true
|
||||
geox-url:
|
||||
mmdb: "https://testingcf.jsdelivr.net/gh/MetaCubeX/meta-rules-dat@release/geoip.metadb"
|
||||
tcp-concurrent: true
|
||||
external-controller: 0.0.0.0:9090
|
||||
|
||||
tun:
|
||||
enable: true
|
||||
stack: system
|
||||
auto-route: true
|
||||
|
||||
dns:
|
||||
enable: true
|
||||
cache-algorithm: arc
|
||||
listen: 0.0.0.0:1053
|
||||
ipv6: true
|
||||
enhanced-mode: fake-ip
|
||||
fake-ip-range: 198.18.0.1/16
|
||||
use-hosts: true
|
||||
fake-ip-filter:
|
||||
- "*.lan"
|
||||
- "lens.l.google.com"
|
||||
- "*.srv.nintendo.net"
|
||||
- "*.stun.playstation.net"
|
||||
- "xbox.*.*.microsoft.com"
|
||||
- "*.xboxlive.com"
|
||||
- "*.msftncsi.com"
|
||||
- "*.msftconnecttest.com"
|
||||
default-nameserver:
|
||||
- 120.53.53.53
|
||||
- 1.12.12.12
|
||||
- 119.29.29.29
|
||||
- 223.5.5.5
|
||||
nameserver:
|
||||
- https://120.53.53.53/dns-query#skip-cert-verify=true
|
||||
- tls://1.12.12.12#skip-cert-verify=true
|
||||
proxy-server-nameserver:
|
||||
- https://120.53.53.53/dns-query#skip-cert-verify=true
|
||||
- tls://1.12.12.12#skip-cert-verify=true
|
||||
- system
|
||||
- 119.29.29.29
|
||||
- 223.5.5.5
|
||||
fallback:
|
||||
- 8.8.8.8
|
||||
- 1.1.1.1
|
||||
fallback-filter:
|
||||
geoip: true
|
||||
geoip-code: CN
|
||||
|
||||
proxies:
|
||||
|
||||
|
||||
+19
-12
@@ -1,22 +1,26 @@
|
||||
package proxy
|
||||
|
||||
import "embed"
|
||||
|
||||
// Adapter represents a proxy adapter
|
||||
type Adapter struct {
|
||||
Proxies []Proxy
|
||||
Group []Group
|
||||
Rules []string // rule
|
||||
Nodes []string // all node
|
||||
Default string // Default Node
|
||||
Proxies []Proxy
|
||||
Group []Group
|
||||
Rules []string // rule
|
||||
Nodes []string // all node
|
||||
Default string // Default Node
|
||||
TemplateFS *embed.FS // Template file system
|
||||
}
|
||||
|
||||
// Proxy represents a proxy server
|
||||
type Proxy struct {
|
||||
Name string
|
||||
Server string
|
||||
Port int
|
||||
Protocol string
|
||||
Country string
|
||||
Option any
|
||||
Name string // Name of the proxy
|
||||
Server string // Server address of the proxy
|
||||
Port int // Port of the proxy server
|
||||
Protocol string // Protocol type (e.g., shadowsocks, vless, vmess, trojan, hysteria2, tuic, anytls)
|
||||
Country string // Country of the proxy
|
||||
Tags []string // Tags for the proxy
|
||||
Option any // Additional options for the proxy configuration
|
||||
}
|
||||
|
||||
// Group represents a group of proxies
|
||||
@@ -26,7 +30,10 @@ type Group struct {
|
||||
Proxies []string
|
||||
URL string
|
||||
Interval int
|
||||
Direct bool
|
||||
Reject bool // Reject group
|
||||
Direct bool // Direct group
|
||||
Tags []string // Tags for the group
|
||||
Default bool // Default group
|
||||
}
|
||||
|
||||
type GroupType string
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
mode: rule
|
||||
ipv6: true
|
||||
allow-lan: true
|
||||
bind-address: "*"
|
||||
mixed-port: 7890
|
||||
log-level: error
|
||||
unified-delay: true
|
||||
tcp-concurrent: true
|
||||
external-controller: 0.0.0.0:9090
|
||||
|
||||
tun:
|
||||
enable: true
|
||||
stack: system
|
||||
auto-route: true
|
||||
|
||||
dns:
|
||||
enable: true
|
||||
cache-algorithm: arc
|
||||
listen: 0.0.0.0:1053
|
||||
ipv6: true
|
||||
enhanced-mode: fake-ip
|
||||
fake-ip-range: 198.18.0.1/16
|
||||
fake-ip-filter:
|
||||
- "*.lan"
|
||||
- "lens.l.google.com"
|
||||
- "*.srv.nintendo.net"
|
||||
- "*.stun.playstation.net"
|
||||
- "xbox.*.*.microsoft.com"
|
||||
- "*.xboxlive.com"
|
||||
- "*.msftncsi.com"
|
||||
- "*.msftconnecttest.com"
|
||||
default-nameserver:
|
||||
- 119.29.29.29
|
||||
- 223.5.5.5
|
||||
nameserver:
|
||||
- system
|
||||
- 119.29.29.29
|
||||
- 223.5.5.5
|
||||
fallback:
|
||||
- 8.8.8.8
|
||||
- 1.1.1.1
|
||||
fallback-filter:
|
||||
geoip: true
|
||||
geoip-code: CN
|
||||
|
||||
proxies:
|
||||
{{.Proxies | toYaml | indent 2}}
|
||||
proxy-groups:
|
||||
{{.ProxyGroups | toYaml | indent 2}}
|
||||
rules:
|
||||
{{.Rules | toYaml | indent 2}}
|
||||
+116
-98
@@ -2,6 +2,7 @@ package adapter
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
"github.com/perfect-panel/server/internal/model/server"
|
||||
@@ -11,14 +12,21 @@ import (
|
||||
"github.com/perfect-panel/server/pkg/tool"
|
||||
)
|
||||
|
||||
// addNode creates a new proxy node based on the provided server data and host/port.
|
||||
func addNode(data *server.Server, host string, port int) *proxy.Proxy {
|
||||
var option any
|
||||
tags := strings.Split(data.Tags, ",")
|
||||
if len(tags) > 0 {
|
||||
tags = tool.RemoveDuplicateElements(tags...)
|
||||
}
|
||||
|
||||
node := proxy.Proxy{
|
||||
Name: data.Name,
|
||||
Server: host,
|
||||
Port: port,
|
||||
Country: data.Country,
|
||||
Protocol: data.Protocol,
|
||||
Tags: tags,
|
||||
}
|
||||
switch data.Protocol {
|
||||
case "shadowsocks":
|
||||
@@ -82,64 +90,45 @@ func addNode(data *server.Server, host string, port int) *proxy.Proxy {
|
||||
return &node
|
||||
}
|
||||
|
||||
func addProxyToGroup(proxyName, groupName string, groups []proxy.Group) []proxy.Group {
|
||||
for i, group := range groups {
|
||||
if group.Name == groupName {
|
||||
groups[i].Proxies = tool.RemoveDuplicateElements(append(group.Proxies, proxyName)...)
|
||||
return groups
|
||||
}
|
||||
}
|
||||
groups = append(groups, proxy.Group{
|
||||
Name: groupName,
|
||||
Type: proxy.GroupTypeSelect,
|
||||
Proxies: []string{proxyName},
|
||||
})
|
||||
return groups
|
||||
}
|
||||
|
||||
func adapterRules(groups []*server.RuleGroup) (proxyGroup []proxy.Group, rules []string) {
|
||||
func adapterRules(groups []*server.RuleGroup) (proxyGroup []proxy.Group, rules []string, defaultGroup string) {
|
||||
for _, group := range groups {
|
||||
if group.Default {
|
||||
log.Printf("[Debug] 规则组 %s 是默认组", group.Name)
|
||||
defaultGroup = group.Name
|
||||
}
|
||||
switch group.Type {
|
||||
case server.RuleGroupTypeBan:
|
||||
case server.RuleGroupTypeReject:
|
||||
proxyGroup = append(proxyGroup, proxy.Group{
|
||||
Name: group.Name,
|
||||
Type: proxy.GroupTypeSelect,
|
||||
Proxies: []string{"REJECT", "DIRECT"},
|
||||
Direct: true,
|
||||
Proxies: []string{"REJECT", "DIRECT", AutoSelect},
|
||||
Reject: true,
|
||||
})
|
||||
case server.RuleGroupTypeAuto:
|
||||
case server.RuleGroupTypeDirect:
|
||||
proxyGroup = append(proxyGroup, proxy.Group{
|
||||
Name: group.Name,
|
||||
Type: proxy.GroupTypeURLTest,
|
||||
URL: "https://www.gstatic.com/generate_204",
|
||||
Proxies: RemoveEmptyString(strings.Split(group.Tags, ",")),
|
||||
Type: proxy.GroupTypeSelect,
|
||||
Proxies: []string{"DIRECT", AutoSelect},
|
||||
Direct: true,
|
||||
})
|
||||
default:
|
||||
proxyGroup = append(proxyGroup, proxy.Group{
|
||||
Name: group.Name,
|
||||
Type: proxy.GroupTypeSelect,
|
||||
Proxies: RemoveEmptyString(strings.Split(group.Tags, ",")),
|
||||
Proxies: []string{},
|
||||
Tags: RemoveEmptyString(strings.Split(group.Tags, ",")),
|
||||
Default: group.Default,
|
||||
})
|
||||
}
|
||||
|
||||
rules = append(rules, strings.Split(group.Rules, "\n")...)
|
||||
}
|
||||
return
|
||||
log.Printf("[Dapter] 生成规则组: %d", len(proxyGroup))
|
||||
return proxyGroup, tool.RemoveDuplicateElements(rules...), defaultGroup
|
||||
}
|
||||
|
||||
func adapterTags(tags map[string][]*server.Server, group []proxy.Group) (proxyGroup []proxy.Group) {
|
||||
for tag, servers := range tags {
|
||||
proxies := adapterProxies(servers)
|
||||
if len(proxies) != 0 {
|
||||
for _, p := range proxies {
|
||||
group = addProxyToGroup(p.Name, tag, group)
|
||||
}
|
||||
}
|
||||
}
|
||||
return group
|
||||
}
|
||||
|
||||
func generateProxyGroup(servers []proxy.Proxy) (proxyGroup []proxy.Group, nodes []string) {
|
||||
// generateDefaultGroup generates a default proxy group with auto-selection and manual selection options.
|
||||
func generateDefaultGroup() (proxyGroup []proxy.Group) {
|
||||
proxyGroup = append(proxyGroup, proxy.Group{
|
||||
Name: AutoSelect,
|
||||
Type: proxy.GroupTypeURLTest,
|
||||
@@ -148,23 +137,12 @@ func generateProxyGroup(servers []proxy.Proxy) (proxyGroup []proxy.Group, nodes
|
||||
Interval: 300,
|
||||
})
|
||||
|
||||
// 设置手动选择分组
|
||||
proxyGroup = append(proxyGroup, proxy.Group{
|
||||
Name: Selection,
|
||||
Type: proxy.GroupTypeSelect,
|
||||
Proxies: []string{AutoSelect},
|
||||
})
|
||||
|
||||
for _, node := range servers {
|
||||
proxyGroup = addProxyToGroup(node.Name, AutoSelect, proxyGroup)
|
||||
proxyGroup = addProxyToGroup(node.Name, Selection, proxyGroup)
|
||||
nodes = append(nodes, node.Name)
|
||||
}
|
||||
return proxyGroup, tool.RemoveDuplicateElements(nodes...)
|
||||
return proxyGroup
|
||||
}
|
||||
|
||||
func adapterProxies(servers []*server.Server) []proxy.Proxy {
|
||||
func adapterProxies(servers []*server.Server) ([]proxy.Proxy, []string, map[string][]string) {
|
||||
var proxies []proxy.Proxy
|
||||
var tags = make(map[string][]string)
|
||||
for _, node := range servers {
|
||||
switch node.RelayMode {
|
||||
case server.RelayModeAll:
|
||||
@@ -179,8 +157,20 @@ func adapterProxies(servers []*server.Server) []proxy.Proxy {
|
||||
continue
|
||||
}
|
||||
if relay.Prefix != "" {
|
||||
n.Name = relay.Prefix + "-" + n.Name
|
||||
n.Name = relay.Prefix + n.Name
|
||||
}
|
||||
if node.Tags != "" {
|
||||
t := tool.RemoveDuplicateElements(strings.Split(node.Tags, ",")...)
|
||||
for _, tag := range t {
|
||||
if tag != "" {
|
||||
if _, ok := tags[tag]; !ok {
|
||||
tags[tag] = []string{}
|
||||
}
|
||||
tags[tag] = append(tags[tag], n.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
proxies = append(proxies, *n)
|
||||
}
|
||||
case server.RelayModeRandom:
|
||||
@@ -196,18 +186,46 @@ func adapterProxies(servers []*server.Server) []proxy.Proxy {
|
||||
continue
|
||||
}
|
||||
if relay.Prefix != "" {
|
||||
n.Name = relay.Prefix + " - " + node.Name
|
||||
n.Name = relay.Prefix + node.Name
|
||||
}
|
||||
if node.Tags != "" {
|
||||
t := tool.RemoveDuplicateElements(strings.Split(node.Tags, ",")...)
|
||||
for _, tag := range t {
|
||||
if tag != "" {
|
||||
if _, ok := tags[tag]; !ok {
|
||||
tags[tag] = []string{}
|
||||
}
|
||||
tags[tag] = append(tags[tag], n.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
proxies = append(proxies, *n)
|
||||
default:
|
||||
logger.Info("Not Relay Mode", logger.Field("node", node.Name), logger.Field("relayMode", node.RelayMode))
|
||||
n := addNode(node, node.ServerAddr, 0)
|
||||
if n != nil {
|
||||
if node.Tags != "" {
|
||||
t := tool.RemoveDuplicateElements(strings.Split(node.Tags, ",")...)
|
||||
for _, tag := range t {
|
||||
if tag != "" {
|
||||
if _, ok := tags[tag]; !ok {
|
||||
tags[tag] = []string{}
|
||||
}
|
||||
tags[tag] = append(tags[tag], n.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
proxies = append(proxies, *n)
|
||||
}
|
||||
}
|
||||
}
|
||||
return proxies
|
||||
|
||||
var nodes []string
|
||||
for _, p := range proxies {
|
||||
nodes = append(nodes, p.Name)
|
||||
}
|
||||
|
||||
return proxies, tool.RemoveDuplicateElements(nodes...), tags
|
||||
}
|
||||
|
||||
// RemoveEmptyString 切片去除空值
|
||||
@@ -221,61 +239,61 @@ func RemoveEmptyString(arr []string) []string {
|
||||
return result
|
||||
}
|
||||
|
||||
// RemoveEmptyGroup removes empty groups from the provided slice of proxy groups.
|
||||
func RemoveEmptyGroup(arr []proxy.Group) []proxy.Group {
|
||||
var result []proxy.Group
|
||||
var removeNames []string
|
||||
for _, group := range arr {
|
||||
if group.Name == "手动选择" {
|
||||
group.Proxies = tool.RemoveStringElement(group.Proxies, removeNames...)
|
||||
}
|
||||
if len(group.Proxies) > 0 {
|
||||
result = append(result, group)
|
||||
} else {
|
||||
removeNames = append(removeNames, group.Name)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// FindDefaultGroup finds the default rule group from a list of rule groups.
|
||||
func FindDefaultGroup(groups []*server.RuleGroup) string {
|
||||
for _, group := range groups {
|
||||
if group.Default {
|
||||
return group.Name
|
||||
}
|
||||
}
|
||||
return AutoSelect
|
||||
}
|
||||
|
||||
// SortGroups sorts the provided slice of proxy groups by their names.
|
||||
func SortGroups(groups []proxy.Group, defaultName string) []proxy.Group {
|
||||
func SortGroups(groups []proxy.Group, nodes []string, tags map[string][]string, defaultName string) []proxy.Group {
|
||||
var sortedGroups []proxy.Group
|
||||
var selectedGroup proxy.Group
|
||||
var defaultGroup, autoSelectGroup proxy.Group
|
||||
// 在所有分组找到默认分组并将他放到第一个
|
||||
for _, group := range groups {
|
||||
if group.Name == "" || group.Name == "DIRECT" || group.Name == "REJECT" {
|
||||
continue
|
||||
}
|
||||
if group.Name == defaultName {
|
||||
group.Proxies = tool.RemoveStringElement(group.Proxies, defaultName, "REJECT")
|
||||
sortedGroups = append([]proxy.Group{group}, sortedGroups...)
|
||||
continue
|
||||
} else if group.Name == Selection {
|
||||
group.Proxies = tool.RemoveStringElement(group.Proxies, defaultName)
|
||||
selectedGroup = group
|
||||
continue
|
||||
} else if group.Name == AutoSelect {
|
||||
group.Proxies = tool.RemoveStringElement(group.Proxies, defaultName, group.Name)
|
||||
sortedGroups = append([]proxy.Group{group}, sortedGroups...)
|
||||
// 如果是默认分组
|
||||
if group.Default {
|
||||
group.Proxies = append([]string{AutoSelect}, nodes...)
|
||||
group.Proxies = append(group.Proxies, "DIRECT")
|
||||
defaultGroup = group
|
||||
continue
|
||||
}
|
||||
if group.Reject || group.Direct {
|
||||
if defaultName != AutoSelect {
|
||||
group.Proxies = append(group.Proxies, defaultName)
|
||||
}
|
||||
sortedGroups = append(sortedGroups, group)
|
||||
continue
|
||||
}
|
||||
|
||||
if group.Name == AutoSelect {
|
||||
group.Proxies = nodes
|
||||
autoSelectGroup = group
|
||||
continue
|
||||
}
|
||||
// Tags 分组
|
||||
if len(group.Tags) > 0 {
|
||||
var proxies []string
|
||||
for _, tag := range group.Tags {
|
||||
if node, ok := tags[tag]; ok {
|
||||
proxies = append(proxies, node...)
|
||||
}
|
||||
}
|
||||
group.Proxies = append(tool.RemoveDuplicateElements(proxies...), AutoSelect, "DIRECT")
|
||||
sortedGroups = append(sortedGroups, group)
|
||||
continue
|
||||
}
|
||||
|
||||
group.Proxies = append([]string{AutoSelect}, nodes...)
|
||||
group.Proxies = append(group.Proxies, "DIRECT")
|
||||
group.Proxies = tool.RemoveElementBySlice(group.Proxies, group.Name)
|
||||
sortedGroups = append(sortedGroups, group)
|
||||
}
|
||||
// 将手动选择分组放到最后
|
||||
if selectedGroup.Name != "" {
|
||||
sortedGroups = append(sortedGroups, selectedGroup)
|
||||
|
||||
if defaultGroup.Name != "" {
|
||||
sortedGroups = append([]proxy.Group{defaultGroup}, sortedGroups...)
|
||||
}
|
||||
if autoSelectGroup.Name != "" && autoSelectGroup.Name != defaultGroup.Name {
|
||||
sortedGroups = append(sortedGroups, autoSelectGroup)
|
||||
}
|
||||
|
||||
return sortedGroups
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user