feat(proxy): enhance proxy and group handling with new configuration options
This commit is contained in:
parent
82e447c55e
commit
224365ce79
8
go.mod
8
go.mod
@ -56,6 +56,7 @@ require (
|
|||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/Masterminds/sprig/v3 v3.3.0
|
||||||
github.com/fatih/color v1.18.0
|
github.com/fatih/color v1.18.0
|
||||||
github.com/goccy/go-json v0.10.4
|
github.com/goccy/go-json v0.10.4
|
||||||
github.com/golang-migrate/migrate/v4 v4.18.2
|
github.com/golang-migrate/migrate/v4 v4.18.2
|
||||||
@ -66,7 +67,10 @@ require (
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
cloud.google.com/go/compute/metadata v0.6.0 // indirect
|
cloud.google.com/go/compute/metadata v0.6.0 // indirect
|
||||||
|
dario.cat/mergo v1.0.1 // indirect
|
||||||
filippo.io/edwards25519 v1.1.0 // indirect
|
filippo.io/edwards25519 v1.1.0 // indirect
|
||||||
|
github.com/Masterminds/goutils v1.1.1 // indirect
|
||||||
|
github.com/Masterminds/semver/v3 v3.3.0 // indirect
|
||||||
github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.5 // indirect
|
github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.5 // indirect
|
||||||
github.com/alibabacloud-go/debug v1.0.1 // indirect
|
github.com/alibabacloud-go/debug v1.0.1 // indirect
|
||||||
github.com/alibabacloud-go/endpoint-util v1.1.0 // indirect
|
github.com/alibabacloud-go/endpoint-util v1.1.0 // indirect
|
||||||
@ -99,6 +103,7 @@ require (
|
|||||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect
|
||||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||||
|
github.com/huandu/xstrings v1.5.0 // indirect
|
||||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||||
github.com/jinzhu/now v1.1.5 // indirect
|
github.com/jinzhu/now v1.1.5 // indirect
|
||||||
@ -107,12 +112,15 @@ require (
|
|||||||
github.com/leodido/go-urn v1.4.0 // indirect
|
github.com/leodido/go-urn v1.4.0 // indirect
|
||||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
|
github.com/mitchellh/copystructure v1.2.0 // indirect
|
||||||
|
github.com/mitchellh/reflectwalk v1.0.2 // indirect
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
github.com/openzipkin/zipkin-go v0.4.2 // indirect
|
github.com/openzipkin/zipkin-go v0.4.2 // indirect
|
||||||
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
|
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||||
github.com/robfig/cron/v3 v3.0.1 // indirect
|
github.com/robfig/cron/v3 v3.0.1 // indirect
|
||||||
|
github.com/shopspring/decimal v1.4.0 // indirect
|
||||||
github.com/smartwalle/ncrypto v1.0.4 // indirect
|
github.com/smartwalle/ncrypto v1.0.4 // indirect
|
||||||
github.com/smartwalle/ngx v1.0.9 // indirect
|
github.com/smartwalle/ngx v1.0.9 // indirect
|
||||||
github.com/smartwalle/nsign v1.0.9 // indirect
|
github.com/smartwalle/nsign v1.0.9 // indirect
|
||||||
|
|||||||
16
go.sum
16
go.sum
@ -1,6 +1,8 @@
|
|||||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I=
|
cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I=
|
||||||
cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg=
|
cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg=
|
||||||
|
dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s=
|
||||||
|
dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
|
||||||
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||||
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||||
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0=
|
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0=
|
||||||
@ -8,6 +10,12 @@ github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg6
|
|||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
github.com/GUAIK-ORG/go-snowflake v0.0.0-20200116064823-220c4260e85f h1:RDkg3pyE1qGbBpRWmvSN9RNZC5nUrOaEPiEpEb8y2f0=
|
github.com/GUAIK-ORG/go-snowflake v0.0.0-20200116064823-220c4260e85f h1:RDkg3pyE1qGbBpRWmvSN9RNZC5nUrOaEPiEpEb8y2f0=
|
||||||
github.com/GUAIK-ORG/go-snowflake v0.0.0-20200116064823-220c4260e85f/go.mod h1:zA7AF9RTfpluCfz0omI4t5KCMaWHUMicsZoMccnaT44=
|
github.com/GUAIK-ORG/go-snowflake v0.0.0-20200116064823-220c4260e85f/go.mod h1:zA7AF9RTfpluCfz0omI4t5KCMaWHUMicsZoMccnaT44=
|
||||||
|
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
|
||||||
|
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
|
||||||
|
github.com/Masterminds/semver/v3 v3.3.0 h1:B8LGeaivUe71a5qox1ICM/JLl0NqZSW5CHyL+hmvYS0=
|
||||||
|
github.com/Masterminds/semver/v3 v3.3.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
|
||||||
|
github.com/Masterminds/sprig/v3 v3.3.0 h1:mQh0Yrg1XPo6vjYXgtf5OtijNAKJRNcTdOOGZe3tPhs=
|
||||||
|
github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSCzdgBfDb35Lz0=
|
||||||
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
|
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
|
||||||
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
||||||
github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.4/go.mod h1:sCavSAvdzOjul4cEqeVtvlSaSScfNsTQ+46HwlTL1hc=
|
github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.4/go.mod h1:sCavSAvdzOjul4cEqeVtvlSaSScfNsTQ+46HwlTL1hc=
|
||||||
@ -208,6 +216,8 @@ github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+l
|
|||||||
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
||||||
github.com/hibiken/asynq v0.24.1 h1:+5iIEAyA9K/lcSPvx3qoPtsKJeKI5u9aOIvUmSsazEw=
|
github.com/hibiken/asynq v0.24.1 h1:+5iIEAyA9K/lcSPvx3qoPtsKJeKI5u9aOIvUmSsazEw=
|
||||||
github.com/hibiken/asynq v0.24.1/go.mod h1:u5qVeSbrnfT+vtG5Mq8ZPzQu/BmCKMHvTGb91uy9Tts=
|
github.com/hibiken/asynq v0.24.1/go.mod h1:u5qVeSbrnfT+vtG5Mq8ZPzQu/BmCKMHvTGb91uy9Tts=
|
||||||
|
github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI=
|
||||||
|
github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
|
||||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||||
github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8=
|
github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8=
|
||||||
@ -249,6 +259,10 @@ github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D
|
|||||||
github.com/mattn/go-sqlite3 v1.14.3/go.mod h1:WVKg1VTActs4Qso6iwGbiFih2UIHo0ENGwNd0Lj+XmI=
|
github.com/mattn/go-sqlite3 v1.14.3/go.mod h1:WVKg1VTActs4Qso6iwGbiFih2UIHo0ENGwNd0Lj+XmI=
|
||||||
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
|
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
|
||||||
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||||
|
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
|
||||||
|
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
|
||||||
|
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
|
||||||
|
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||||
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
|
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
|
||||||
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
|
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
|
||||||
github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
|
github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
|
||||||
@ -287,6 +301,8 @@ github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzG
|
|||||||
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
|
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
|
||||||
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
|
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
|
||||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
|
github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
|
||||||
|
github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
|
||||||
github.com/smartwalle/alipay/v3 v3.2.23 h1:i1VwJeu70EmwpsXXz6GZZnMAtRx5MTfn2dPoql/L3zE=
|
github.com/smartwalle/alipay/v3 v3.2.23 h1:i1VwJeu70EmwpsXXz6GZZnMAtRx5MTfn2dPoql/L3zE=
|
||||||
github.com/smartwalle/alipay/v3 v3.2.23/go.mod h1:lVqFiupPf8YsAXaq5JXcwqnOUC2MCF+2/5vub+RlagE=
|
github.com/smartwalle/alipay/v3 v3.2.23/go.mod h1:lVqFiupPf8YsAXaq5JXcwqnOUC2MCF+2/5vub+RlagE=
|
||||||
github.com/smartwalle/ncrypto v1.0.4 h1:P2rqQxDepJwgeO5ShoC+wGcK2wNJDmcdBOWAksuIgx8=
|
github.com/smartwalle/ncrypto v1.0.4 h1:P2rqQxDepJwgeO5ShoC+wGcK2wNJDmcdBOWAksuIgx8=
|
||||||
|
|||||||
@ -9,11 +9,12 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
RelayModeNone = "none"
|
RelayModeNone = "none"
|
||||||
RelayModeAll = "all"
|
RelayModeAll = "all"
|
||||||
RelayModeRandom = "random"
|
RelayModeRandom = "random"
|
||||||
RuleGroupTypeBan = "ban"
|
RuleGroupTypeReject = "reject"
|
||||||
RuleGroupTypeAuto = "auto"
|
RuleGroupTypeDefault = "default"
|
||||||
|
RuleGroupTypeDirect = "direct"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ServerFilter struct {
|
type ServerFilter struct {
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
package adapter
|
package adapter
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"embed"
|
||||||
|
|
||||||
"github.com/perfect-panel/server/internal/model/server"
|
"github.com/perfect-panel/server/internal/model/server"
|
||||||
"github.com/perfect-panel/server/pkg/adapter/clash"
|
"github.com/perfect-panel/server/pkg/adapter/clash"
|
||||||
"github.com/perfect-panel/server/pkg/adapter/general"
|
"github.com/perfect-panel/server/pkg/adapter/general"
|
||||||
@ -13,9 +15,11 @@ import (
|
|||||||
"github.com/perfect-panel/server/pkg/adapter/v2rayn"
|
"github.com/perfect-panel/server/pkg/adapter/v2rayn"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
//go:embed template/*
|
||||||
|
var TemplateFS embed.FS
|
||||||
|
|
||||||
var (
|
var (
|
||||||
AutoSelect = "Auto - UrlTest"
|
AutoSelect = "Auto - UrlTest"
|
||||||
Selection = "Selection"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
@ -30,69 +34,63 @@ type Adapter struct {
|
|||||||
|
|
||||||
func NewAdapter(cfg *Config) *Adapter {
|
func NewAdapter(cfg *Config) *Adapter {
|
||||||
// 转换服务器列表
|
// 转换服务器列表
|
||||||
proxies := adapterProxies(cfg.Nodes)
|
proxies, nodes, tags := adapterProxies(cfg.Nodes)
|
||||||
defaultGroup := FindDefaultGroup(cfg.Rules)
|
|
||||||
// 转换规则组
|
// 转换规则组
|
||||||
g, r := adapterRules(cfg.Rules)
|
g, r, d := adapterRules(cfg.Rules)
|
||||||
|
if d == "" {
|
||||||
// 生成代理组
|
d = AutoSelect
|
||||||
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")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
// 生成默认代理组
|
||||||
|
proxyGroup := append(generateDefaultGroup(), g...)
|
||||||
// 合并代理组
|
// 合并代理组
|
||||||
proxyGroup = RemoveEmptyGroup(append(proxyGroup, g...))
|
proxyGroup = SortGroups(proxyGroup, nodes, tags, d)
|
||||||
// 处理标签
|
|
||||||
proxyGroup = adapterTags(cfg.Tags, proxyGroup)
|
|
||||||
|
|
||||||
return &Adapter{
|
return &Adapter{
|
||||||
Adapter: proxy.Adapter{
|
Adapter: proxy.Adapter{
|
||||||
Proxies: proxies,
|
Proxies: proxies,
|
||||||
Group: SortGroups(proxyGroup, defaultGroup),
|
Group: proxyGroup,
|
||||||
Rules: r,
|
Rules: r,
|
||||||
Nodes: nodes,
|
Nodes: nodes,
|
||||||
Default: defaultGroup,
|
Default: d,
|
||||||
|
TemplateFS: &TemplateFS,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BuildClash generates a Clash configuration for the given UUID.
|
||||||
func (m *Adapter) BuildClash(uuid string) ([]byte, error) {
|
func (m *Adapter) BuildClash(uuid string) ([]byte, error) {
|
||||||
client := clash.NewClash(m.Adapter)
|
client := clash.NewClash(m.Adapter)
|
||||||
return client.Build(uuid)
|
return client.Build(uuid)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BuildGeneral generates a general configuration for the given UUID.
|
||||||
func (m *Adapter) BuildGeneral(uuid string) []byte {
|
func (m *Adapter) BuildGeneral(uuid string) []byte {
|
||||||
return general.GenerateBase64General(m.Proxies, uuid)
|
return general.GenerateBase64General(m.Proxies, uuid)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BuildLoon generates a Loon configuration for the given UUID.
|
||||||
func (m *Adapter) BuildLoon(uuid string) []byte {
|
func (m *Adapter) BuildLoon(uuid string) []byte {
|
||||||
return loon.BuildLoon(m.Proxies, uuid)
|
return loon.BuildLoon(m.Proxies, uuid)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BuildQuantumultX generates a Quantumult X configuration for the given UUID.
|
||||||
func (m *Adapter) BuildQuantumultX(uuid string) string {
|
func (m *Adapter) BuildQuantumultX(uuid string) string {
|
||||||
return quantumultx.BuildQuantumultX(m.Proxies, uuid)
|
return quantumultx.BuildQuantumultX(m.Proxies, uuid)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BuildSingbox generates a Singbox configuration for the given UUID.
|
||||||
func (m *Adapter) BuildSingbox(uuid string) ([]byte, error) {
|
func (m *Adapter) BuildSingbox(uuid string) ([]byte, error) {
|
||||||
return singbox.BuildSingbox(m.Adapter, uuid)
|
return singbox.BuildSingbox(m.Adapter, uuid)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Adapter) BuildShadowrocket(uuid string, userInfo shadowrocket.UserInfo) []byte {
|
func (m *Adapter) BuildShadowrocket(uuid string, userInfo shadowrocket.UserInfo) []byte {
|
||||||
return shadowrocket.BuildShadowrocket(m.Proxies, uuid, userInfo)
|
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 {
|
func (m *Adapter) BuildSurfboard(siteName string, user surfboard.UserInfo) []byte {
|
||||||
return surfboard.BuildSurfboard(m.Adapter, siteName, user)
|
return surfboard.BuildSurfboard(m.Adapter, siteName, user)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BuildV2rayN generates a V2rayN configuration for the given UUID.
|
||||||
func (m *Adapter) BuildV2rayN(uuid string) []byte {
|
func (m *Adapter) BuildV2rayN(uuid string) []byte {
|
||||||
return v2rayn.NewV2rayN(m.Adapter).Build(uuid)
|
return v2rayn.NewV2rayN(m.Adapter).Build(uuid)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,8 +1,11 @@
|
|||||||
package clash
|
package clash
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"text/template"
|
||||||
|
|
||||||
|
"github.com/Masterminds/sprig/v3"
|
||||||
"github.com/perfect-panel/server/pkg/adapter/proxy"
|
"github.com/perfect-panel/server/pkg/adapter/proxy"
|
||||||
"github.com/perfect-panel/server/pkg/logger"
|
"github.com/perfect-panel/server/pkg/logger"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
@ -20,22 +23,16 @@ func NewClash(adapter proxy.Adapter) *Clash {
|
|||||||
|
|
||||||
func (c *Clash) Build(uuid string) ([]byte, error) {
|
func (c *Clash) Build(uuid string) ([]byte, error) {
|
||||||
var proxies []Proxy
|
var proxies []Proxy
|
||||||
for _, v := range c.Proxies {
|
for _, proxied := range c.Adapter.Proxies {
|
||||||
p, err := c.parseProxy(v, uuid)
|
p, err := c.parseProxy(proxied, uuid)
|
||||||
if err != nil {
|
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
|
continue
|
||||||
}
|
}
|
||||||
proxies = append(proxies, *p)
|
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
|
var groups []ProxyGroup
|
||||||
for _, group := range c.Group {
|
for _, group := range c.Adapter.Group {
|
||||||
groups = append(groups, ProxyGroup{
|
groups = append(groups, ProxyGroup{
|
||||||
Name: group.Name,
|
Name: group.Name,
|
||||||
Type: string(group.Type),
|
Type: string(group.Type),
|
||||||
@ -44,9 +41,38 @@ func (c *Clash) Build(uuid string) ([]byte, error) {
|
|||||||
Interval: group.Interval,
|
Interval: group.Interval,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
rawConfig.ProxyGroups = groups
|
var rules = append(c.Rules, fmt.Sprintf("MATCH,%s", c.Default))
|
||||||
rawConfig.Rules = append(c.Rules, fmt.Sprintf("MATCH,%s", c.Default))
|
|
||||||
return yaml.Marshal(&rawConfig)
|
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) {
|
func (c *Clash) parseProxy(p proxy.Proxy, uuid string) (*Proxy, error) {
|
||||||
|
|||||||
@ -1,31 +1,50 @@
|
|||||||
package clash
|
package clash
|
||||||
|
|
||||||
const DefaultTemplate = `
|
const DefaultTemplate = `
|
||||||
mixed-port: 7890
|
mode: rule
|
||||||
|
ipv6: true
|
||||||
allow-lan: true
|
allow-lan: true
|
||||||
bind-address: "*"
|
bind-address: "*"
|
||||||
mode: rule
|
mixed-port: 7890
|
||||||
log-level: info
|
log-level: error
|
||||||
external-controller: 127.0.0.1:9090
|
|
||||||
global-client-fingerprint: chrome
|
|
||||||
unified-delay: true
|
unified-delay: true
|
||||||
geox-url:
|
tcp-concurrent: true
|
||||||
mmdb: "https://testingcf.jsdelivr.net/gh/MetaCubeX/meta-rules-dat@release/geoip.metadb"
|
external-controller: 0.0.0.0:9090
|
||||||
|
|
||||||
|
tun:
|
||||||
|
enable: true
|
||||||
|
stack: system
|
||||||
|
auto-route: true
|
||||||
|
|
||||||
dns:
|
dns:
|
||||||
enable: true
|
enable: true
|
||||||
|
cache-algorithm: arc
|
||||||
|
listen: 0.0.0.0:1053
|
||||||
ipv6: true
|
ipv6: true
|
||||||
enhanced-mode: fake-ip
|
enhanced-mode: fake-ip
|
||||||
fake-ip-range: 198.18.0.1/16
|
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:
|
default-nameserver:
|
||||||
- 120.53.53.53
|
- 119.29.29.29
|
||||||
- 1.12.12.12
|
- 223.5.5.5
|
||||||
nameserver:
|
nameserver:
|
||||||
- https://120.53.53.53/dns-query#skip-cert-verify=true
|
- system
|
||||||
- tls://1.12.12.12#skip-cert-verify=true
|
- 119.29.29.29
|
||||||
proxy-server-nameserver:
|
- 223.5.5.5
|
||||||
- https://120.53.53.53/dns-query#skip-cert-verify=true
|
fallback:
|
||||||
- tls://1.12.12.12#skip-cert-verify=true
|
- 8.8.8.8
|
||||||
|
- 1.1.1.1
|
||||||
|
fallback-filter:
|
||||||
|
geoip: true
|
||||||
|
geoip-code: CN
|
||||||
|
|
||||||
proxies:
|
proxies:
|
||||||
|
|
||||||
|
|||||||
@ -1,22 +1,26 @@
|
|||||||
package proxy
|
package proxy
|
||||||
|
|
||||||
|
import "embed"
|
||||||
|
|
||||||
// Adapter represents a proxy adapter
|
// Adapter represents a proxy adapter
|
||||||
type Adapter struct {
|
type Adapter struct {
|
||||||
Proxies []Proxy
|
Proxies []Proxy
|
||||||
Group []Group
|
Group []Group
|
||||||
Rules []string // rule
|
Rules []string // rule
|
||||||
Nodes []string // all node
|
Nodes []string // all node
|
||||||
Default string // Default Node
|
Default string // Default Node
|
||||||
|
TemplateFS *embed.FS // Template file system
|
||||||
}
|
}
|
||||||
|
|
||||||
// Proxy represents a proxy server
|
// Proxy represents a proxy server
|
||||||
type Proxy struct {
|
type Proxy struct {
|
||||||
Name string
|
Name string // Name of the proxy
|
||||||
Server string
|
Server string // Server address of the proxy
|
||||||
Port int
|
Port int // Port of the proxy server
|
||||||
Protocol string
|
Protocol string // Protocol type (e.g., shadowsocks, vless, vmess, trojan, hysteria2, tuic, anytls)
|
||||||
Country string
|
Country string // Country of the proxy
|
||||||
Option any
|
Tags []string // Tags for the proxy
|
||||||
|
Option any // Additional options for the proxy configuration
|
||||||
}
|
}
|
||||||
|
|
||||||
// Group represents a group of proxies
|
// Group represents a group of proxies
|
||||||
@ -26,7 +30,10 @@ type Group struct {
|
|||||||
Proxies []string
|
Proxies []string
|
||||||
URL string
|
URL string
|
||||||
Interval int
|
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
|
type GroupType string
|
||||||
|
|||||||
51
pkg/adapter/template/clash.tpl
Normal file
51
pkg/adapter/template/clash.tpl
Normal file
@ -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}}
|
||||||
@ -2,6 +2,7 @@ package adapter
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"log"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/perfect-panel/server/internal/model/server"
|
"github.com/perfect-panel/server/internal/model/server"
|
||||||
@ -11,14 +12,21 @@ import (
|
|||||||
"github.com/perfect-panel/server/pkg/tool"
|
"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 {
|
func addNode(data *server.Server, host string, port int) *proxy.Proxy {
|
||||||
var option any
|
var option any
|
||||||
|
tags := strings.Split(data.Tags, ",")
|
||||||
|
if len(tags) > 0 {
|
||||||
|
tags = tool.RemoveDuplicateElements(tags...)
|
||||||
|
}
|
||||||
|
|
||||||
node := proxy.Proxy{
|
node := proxy.Proxy{
|
||||||
Name: data.Name,
|
Name: data.Name,
|
||||||
Server: host,
|
Server: host,
|
||||||
Port: port,
|
Port: port,
|
||||||
Country: data.Country,
|
Country: data.Country,
|
||||||
Protocol: data.Protocol,
|
Protocol: data.Protocol,
|
||||||
|
Tags: tags,
|
||||||
}
|
}
|
||||||
switch data.Protocol {
|
switch data.Protocol {
|
||||||
case "shadowsocks":
|
case "shadowsocks":
|
||||||
@ -82,64 +90,45 @@ func addNode(data *server.Server, host string, port int) *proxy.Proxy {
|
|||||||
return &node
|
return &node
|
||||||
}
|
}
|
||||||
|
|
||||||
func addProxyToGroup(proxyName, groupName string, groups []proxy.Group) []proxy.Group {
|
func adapterRules(groups []*server.RuleGroup) (proxyGroup []proxy.Group, rules []string, defaultGroup string) {
|
||||||
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) {
|
|
||||||
for _, group := range groups {
|
for _, group := range groups {
|
||||||
|
if group.Default {
|
||||||
|
log.Printf("[Debug] 规则组 %s 是默认组", group.Name)
|
||||||
|
defaultGroup = group.Name
|
||||||
|
}
|
||||||
switch group.Type {
|
switch group.Type {
|
||||||
case server.RuleGroupTypeBan:
|
case server.RuleGroupTypeReject:
|
||||||
proxyGroup = append(proxyGroup, proxy.Group{
|
proxyGroup = append(proxyGroup, proxy.Group{
|
||||||
Name: group.Name,
|
Name: group.Name,
|
||||||
Type: proxy.GroupTypeSelect,
|
Type: proxy.GroupTypeSelect,
|
||||||
Proxies: []string{"REJECT", "DIRECT"},
|
Proxies: []string{"REJECT", "DIRECT", AutoSelect},
|
||||||
Direct: true,
|
Reject: true,
|
||||||
})
|
})
|
||||||
case server.RuleGroupTypeAuto:
|
case server.RuleGroupTypeDirect:
|
||||||
proxyGroup = append(proxyGroup, proxy.Group{
|
proxyGroup = append(proxyGroup, proxy.Group{
|
||||||
Name: group.Name,
|
Name: group.Name,
|
||||||
Type: proxy.GroupTypeURLTest,
|
Type: proxy.GroupTypeSelect,
|
||||||
URL: "https://www.gstatic.com/generate_204",
|
Proxies: []string{"DIRECT", AutoSelect},
|
||||||
Proxies: RemoveEmptyString(strings.Split(group.Tags, ",")),
|
Direct: true,
|
||||||
})
|
})
|
||||||
default:
|
default:
|
||||||
proxyGroup = append(proxyGroup, proxy.Group{
|
proxyGroup = append(proxyGroup, proxy.Group{
|
||||||
Name: group.Name,
|
Name: group.Name,
|
||||||
Type: proxy.GroupTypeSelect,
|
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")...)
|
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) {
|
// generateDefaultGroup generates a default proxy group with auto-selection and manual selection options.
|
||||||
for tag, servers := range tags {
|
func generateDefaultGroup() (proxyGroup []proxy.Group) {
|
||||||
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) {
|
|
||||||
proxyGroup = append(proxyGroup, proxy.Group{
|
proxyGroup = append(proxyGroup, proxy.Group{
|
||||||
Name: AutoSelect,
|
Name: AutoSelect,
|
||||||
Type: proxy.GroupTypeURLTest,
|
Type: proxy.GroupTypeURLTest,
|
||||||
@ -148,23 +137,12 @@ func generateProxyGroup(servers []proxy.Proxy) (proxyGroup []proxy.Group, nodes
|
|||||||
Interval: 300,
|
Interval: 300,
|
||||||
})
|
})
|
||||||
|
|
||||||
// 设置手动选择分组
|
return proxyGroup
|
||||||
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...)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func adapterProxies(servers []*server.Server) []proxy.Proxy {
|
func adapterProxies(servers []*server.Server) ([]proxy.Proxy, []string, map[string][]string) {
|
||||||
var proxies []proxy.Proxy
|
var proxies []proxy.Proxy
|
||||||
|
var tags = make(map[string][]string)
|
||||||
for _, node := range servers {
|
for _, node := range servers {
|
||||||
switch node.RelayMode {
|
switch node.RelayMode {
|
||||||
case server.RelayModeAll:
|
case server.RelayModeAll:
|
||||||
@ -179,8 +157,20 @@ func adapterProxies(servers []*server.Server) []proxy.Proxy {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if relay.Prefix != "" {
|
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)
|
proxies = append(proxies, *n)
|
||||||
}
|
}
|
||||||
case server.RelayModeRandom:
|
case server.RelayModeRandom:
|
||||||
@ -196,18 +186,46 @@ func adapterProxies(servers []*server.Server) []proxy.Proxy {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if relay.Prefix != "" {
|
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)
|
proxies = append(proxies, *n)
|
||||||
default:
|
default:
|
||||||
logger.Info("Not Relay Mode", logger.Field("node", node.Name), logger.Field("relayMode", node.RelayMode))
|
logger.Info("Not Relay Mode", logger.Field("node", node.Name), logger.Field("relayMode", node.RelayMode))
|
||||||
n := addNode(node, node.ServerAddr, 0)
|
n := addNode(node, node.ServerAddr, 0)
|
||||||
if n != nil {
|
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)
|
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 切片去除空值
|
// RemoveEmptyString 切片去除空值
|
||||||
@ -221,61 +239,61 @@ func RemoveEmptyString(arr []string) []string {
|
|||||||
return result
|
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.
|
// 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 sortedGroups []proxy.Group
|
||||||
var selectedGroup proxy.Group
|
var defaultGroup, autoSelectGroup proxy.Group
|
||||||
// 在所有分组找到默认分组并将他放到第一个
|
// 在所有分组找到默认分组并将他放到第一个
|
||||||
for _, group := range groups {
|
for _, group := range groups {
|
||||||
if group.Name == "" || group.Name == "DIRECT" || group.Name == "REJECT" {
|
if group.Name == "" || group.Name == "DIRECT" || group.Name == "REJECT" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if group.Name == defaultName {
|
// 如果是默认分组
|
||||||
group.Proxies = tool.RemoveStringElement(group.Proxies, defaultName, "REJECT")
|
if group.Default {
|
||||||
sortedGroups = append([]proxy.Group{group}, sortedGroups...)
|
group.Proxies = append([]string{AutoSelect}, nodes...)
|
||||||
continue
|
group.Proxies = append(group.Proxies, "DIRECT")
|
||||||
} else if group.Name == Selection {
|
defaultGroup = group
|
||||||
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...)
|
|
||||||
continue
|
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)
|
sortedGroups = append(sortedGroups, group)
|
||||||
}
|
}
|
||||||
// 将手动选择分组放到最后
|
|
||||||
if selectedGroup.Name != "" {
|
if defaultGroup.Name != "" {
|
||||||
sortedGroups = append(sortedGroups, selectedGroup)
|
sortedGroups = append([]proxy.Group{defaultGroup}, sortedGroups...)
|
||||||
}
|
}
|
||||||
|
if autoSelectGroup.Name != "" && autoSelectGroup.Name != defaultGroup.Name {
|
||||||
|
sortedGroups = append(sortedGroups, autoSelectGroup)
|
||||||
|
}
|
||||||
|
|
||||||
return sortedGroups
|
return sortedGroups
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user