server/pkg/adapter/uilts.go
shanshanzhong 602ecc31bc feat(proxy): 新增混合模式和中继直连模式支持
添加 RelayModeMixed、RelayModeAllDirect 和 RelayModeRandomDirect 三种中继模式
修复 README.md 中的拼写错误
更新 JWT 密钥和日志模式配置
优化流量日志查询排序
统一邮件验证码主题格式
2025-09-01 02:22:16 -07:00

464 lines
13 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package adapter
import (
"encoding/json"
"fmt"
"log"
"strings"
"github.com/perfect-panel/server/internal/model/server"
"github.com/perfect-panel/server/pkg/adapter/proxy"
"github.com/perfect-panel/server/pkg/logger"
"github.com/perfect-panel/server/pkg/random"
"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":
var ss proxy.Shadowsocks
if err := json.Unmarshal([]byte(data.Config), &ss); err != nil {
return nil
}
if port == 0 {
node.Port = ss.Port
}
option = ss
case "vless":
var vless proxy.Vless
if err := json.Unmarshal([]byte(data.Config), &vless); err != nil {
return nil
}
if port == 0 {
node.Port = vless.Port
}
option = vless
case "vmess":
var vmess proxy.Vmess
if err := json.Unmarshal([]byte(data.Config), &vmess); err != nil {
return nil
}
if port == 0 {
node.Port = vmess.Port
}
option = vmess
case "trojan":
var trojan proxy.Trojan
if err := json.Unmarshal([]byte(data.Config), &trojan); err != nil {
return nil
}
if port == 0 {
node.Port = trojan.Port
}
option = trojan
case "hysteria2":
var hysteria2 proxy.Hysteria2
if err := json.Unmarshal([]byte(data.Config), &hysteria2); err != nil {
return nil
}
if port == 0 {
node.Port = hysteria2.Port
}
option = hysteria2
case "tuic":
var tuic proxy.Tuic
if err := json.Unmarshal([]byte(data.Config), &tuic); err != nil {
return nil
}
if port == 0 {
node.Port = tuic.Port
}
option = tuic
case "anytls":
var anytls proxy.AnyTLS
if err := json.Unmarshal([]byte(data.Config), &anytls); err != nil {
logger.Errorw("解析AnyTLS配置失败", logger.Field("error", err.Error()), logger.Field("node", data.Name))
return nil
}
if port == 0 {
node.Port = anytls.Port
}
option = anytls
logger.Infow("成功处理AnyTLS节点", logger.Field("node", data.Name), logger.Field("port", anytls.Port))
default:
fmt.Printf("[Error] 不支持的协议: %s", data.Protocol)
return nil
}
node.Option = option
return &node
}
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.RuleGroupTypeReject:
proxyGroup = append(proxyGroup, proxy.Group{
Name: group.Name,
Type: proxy.GroupTypeSelect,
Proxies: []string{"REJECT", "DIRECT", AutoSelect},
Reject: true,
})
case server.RuleGroupTypeDirect:
proxyGroup = append(proxyGroup, proxy.Group{
Name: group.Name,
Type: proxy.GroupTypeSelect,
Proxies: []string{"DIRECT", AutoSelect},
Direct: true,
})
default:
proxyGroup = append(proxyGroup, proxy.Group{
Name: group.Name,
Type: proxy.GroupTypeSelect,
Proxies: []string{},
Tags: RemoveEmptyString(strings.Split(group.Tags, ",")),
Default: group.Default,
})
}
rules = append(rules, strings.Split(group.Rules, "\n")...)
}
log.Printf("[Dapter] 生成规则组: %d", len(proxyGroup))
return proxyGroup, tool.RemoveDuplicateElements(rules...), defaultGroup
}
// 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,
Proxies: make([]string, 0),
URL: "https://www.gstatic.com/generate_204",
Interval: 300,
})
return proxyGroup
}
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:
var relays []server.NodeRelay
if err := json.Unmarshal([]byte(node.RelayNode), &relays); err != nil {
logger.Errorw("Unmarshal RelayNode", logger.Field("error", err.Error()), logger.Field("node", node.Name), logger.Field("relayNode", node.RelayNode))
continue
}
for _, relay := range relays {
n := addNode(node, relay.Host, relay.Port)
if n == nil {
continue
}
if relay.Prefix != "" {
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:
var relays []server.NodeRelay
if err := json.Unmarshal([]byte(node.RelayNode), &relays); err != nil {
logger.Errorw("Unmarshal RelayNode", logger.Field("error", err.Error()), logger.Field("node", node.Name), logger.Field("relayNode", node.RelayNode))
continue
}
randNum := random.RandomInRange(0, len(relays)-1)
relay := relays[randNum]
n := addNode(node, relay.Host, relay.Port)
if n == nil {
continue
}
if relay.Prefix != "" {
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)
case server.RelayModeMixed:
// 生成直连节点
n := addNode(node, node.ServerAddr, 0)
if n != nil {
directName := "直连-" + node.Name
n.Name = directName
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)
}
// 生成一个随机中继节点类似random模式但和直连共存
var relays []server.NodeRelay
if err := json.Unmarshal([]byte(node.RelayNode), &relays); err != nil {
logger.Errorw("Unmarshal RelayNode", logger.Field("error", err.Error()), logger.Field("node", node.Name), logger.Field("relayNode", node.RelayNode))
continue
}
if len(relays) > 0 {
randNum := random.RandomInRange(0, len(relays)-1)
relay := relays[randNum]
n := addNode(node, relay.Host, relay.Port)
if n == nil {
continue
}
relayName := "中继-" + node.Name
if relay.Prefix != "" {
relayName = relay.Prefix + node.Name
}
n.Name = relayName
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.RelayModeAllDirect:
// 生成直连节点
n := addNode(node, node.ServerAddr, 0)
if n != nil {
directName := "直连-" + node.Name
n.Name = directName
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)
}
// 生成所有中继节点
var relays []server.NodeRelay
if err := json.Unmarshal([]byte(node.RelayNode), &relays); err != nil {
logger.Errorw("Unmarshal RelayNode", logger.Field("error", err.Error()), logger.Field("node", node.Name), logger.Field("relayNode", node.RelayNode))
continue
}
for _, relay := range relays {
n := addNode(node, relay.Host, relay.Port)
if n == nil {
continue
}
relayName := "中继-" + node.Name
if relay.Prefix != "" {
relayName = relay.Prefix + node.Name
}
n.Name = relayName
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.RelayModeRandomDirect:
// 生成直连节点
n := addNode(node, node.ServerAddr, 0)
if n != nil {
directName := "直连-" + node.Name
n.Name = directName
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)
}
// 随机选择一个中继节点
var relays []server.NodeRelay
if err := json.Unmarshal([]byte(node.RelayNode), &relays); err != nil {
logger.Errorw("Unmarshal RelayNode", logger.Field("error", err.Error()), logger.Field("node", node.Name), logger.Field("relayNode", node.RelayNode))
continue
}
if len(relays) > 0 {
randNum := random.RandomInRange(0, len(relays)-1)
relay := relays[randNum]
n := addNode(node, relay.Host, relay.Port)
if n == nil {
continue
}
relayName := "中继-" + node.Name
if relay.Prefix != "" {
relayName = relay.Prefix + node.Name
}
n.Name = relayName
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)
}
}
}
var nodes []string
for _, p := range proxies {
nodes = append(nodes, p.Name)
}
return proxies, tool.RemoveDuplicateElements(nodes...), tags
}
// RemoveEmptyString 切片去除空值
func RemoveEmptyString(arr []string) []string {
var result []string
for _, str := range arr {
if str != "" {
result = append(result, str)
}
}
return result
}
// SortGroups sorts the provided slice of proxy groups by their names.
func SortGroups(groups []proxy.Group, nodes []string, tags map[string][]string, defaultName string) []proxy.Group {
var sortedGroups []proxy.Group
var defaultGroup, autoSelectGroup proxy.Group
// 在所有分组找到默认分组并将他放到第一个
for _, group := range groups {
if group.Name == "" || group.Name == "DIRECT" || group.Name == "REJECT" {
continue
}
// 如果是默认分组
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 defaultGroup.Name != "" {
sortedGroups = append([]proxy.Group{defaultGroup}, sortedGroups...)
}
if autoSelectGroup.Name != "" && autoSelectGroup.Name != defaultGroup.Name {
sortedGroups = append(sortedGroups, autoSelectGroup)
}
return sortedGroups
}