From 3fb23e3106538641fe6d81c9c2017a345ed9bfe5 Mon Sep 17 00:00:00 2001 From: Chang lue Tsen Date: Mon, 29 Sep 2025 11:22:36 -0400 Subject: [PATCH] refactor(adapter): delete old adapter --- pkg/adapter/adapter.go | 102 ------ pkg/adapter/adapter_test.go | 138 -------- pkg/adapter/clash/clash.go | 95 ------ pkg/adapter/clash/clash_test.go | 41 --- pkg/adapter/clash/default.go | 54 ---- pkg/adapter/clash/model.go | 131 -------- pkg/adapter/clash/parse.go | 207 ------------ pkg/adapter/clash/tool.go | 35 -- pkg/adapter/general/uri.go | 278 ---------------- pkg/adapter/general/uri_test.go | 26 -- pkg/adapter/loon/build.go | 61 ---- pkg/adapter/loon/default.tpl | 58 ---- pkg/adapter/loon/hysteria2.go | 34 -- pkg/adapter/loon/loon_test.go | 29 -- pkg/adapter/loon/shadowsocks.go | 34 -- pkg/adapter/loon/trojan.go | 43 --- pkg/adapter/loon/vless.go | 62 ---- pkg/adapter/loon/vmess.go | 53 ---- pkg/adapter/proxy/proxy.go | 137 -------- pkg/adapter/proxy/tool.go | 15 - pkg/adapter/quantumultx/build.go | 22 -- pkg/adapter/quantumultx/quantumux_test.go | 94 ------ pkg/adapter/quantumultx/shadowsocks.go | 30 -- pkg/adapter/quantumultx/trojan.go | 39 --- pkg/adapter/quantumultx/vmess.go | 45 --- pkg/adapter/shadowrocket/build.go | 48 --- pkg/adapter/shadowrocket/shadowrocket_test.go | 76 ----- pkg/adapter/shadowrocket/vmess.go | 57 ---- pkg/adapter/singbox/anytls.go | 42 --- pkg/adapter/singbox/build.go | 201 ------------ pkg/adapter/singbox/default.go | 100 ------ pkg/adapter/singbox/hysteria2.go | 76 ----- pkg/adapter/singbox/multiplex.go | 17 - pkg/adapter/singbox/rule.go | 130 -------- pkg/adapter/singbox/rule_test.go | 15 - pkg/adapter/singbox/shadowsocks.go | 45 --- pkg/adapter/singbox/singbox.go | 102 ------ pkg/adapter/singbox/singbox_test.go | 80 ----- pkg/adapter/singbox/tls.go | 87 ----- pkg/adapter/singbox/tool.go | 11 - pkg/adapter/singbox/trojan.go | 39 --- pkg/adapter/singbox/tuic.go | 42 --- pkg/adapter/singbox/v2rayTransport.go | 114 ------- pkg/adapter/singbox/vless.go | 44 --- pkg/adapter/singbox/vmess.go | 43 --- pkg/adapter/surfboard/build.go | 80 ----- pkg/adapter/surfboard/build_test.go | 24 -- pkg/adapter/surfboard/default.tpl | 62 ---- pkg/adapter/surfboard/model.go | 12 - pkg/adapter/surfboard/shadowsocks.go | 28 -- pkg/adapter/surfboard/shadowsocks_test.go | 28 -- pkg/adapter/surfboard/trojan.go | 41 --- pkg/adapter/surfboard/trojan_test.go | 36 --- pkg/adapter/surfboard/vmess.go | 45 --- pkg/adapter/surfboard/vmess_test.go | 33 -- pkg/adapter/surge/default.tpl | 79 ----- pkg/adapter/surge/hysteria2.go | 43 --- pkg/adapter/surge/hysteria2_test.go | 70 ---- pkg/adapter/surge/shadowsocks.go | 32 -- pkg/adapter/surge/surge.go | 101 ------ pkg/adapter/surge/surge_test.go | 97 ------ pkg/adapter/surge/trojan.go | 32 -- pkg/adapter/surge/vmess.go | 44 --- pkg/adapter/template/clash.tpl | 51 --- pkg/adapter/uilts.go | 299 ------------------ pkg/adapter/v2rayn/v2rayN.go | 40 --- 66 files changed, 4509 deletions(-) delete mode 100644 pkg/adapter/adapter.go delete mode 100644 pkg/adapter/adapter_test.go delete mode 100644 pkg/adapter/clash/clash.go delete mode 100644 pkg/adapter/clash/clash_test.go delete mode 100644 pkg/adapter/clash/default.go delete mode 100644 pkg/adapter/clash/model.go delete mode 100644 pkg/adapter/clash/parse.go delete mode 100644 pkg/adapter/clash/tool.go delete mode 100644 pkg/adapter/general/uri.go delete mode 100644 pkg/adapter/general/uri_test.go delete mode 100644 pkg/adapter/loon/build.go delete mode 100644 pkg/adapter/loon/default.tpl delete mode 100644 pkg/adapter/loon/hysteria2.go delete mode 100644 pkg/adapter/loon/loon_test.go delete mode 100644 pkg/adapter/loon/shadowsocks.go delete mode 100644 pkg/adapter/loon/trojan.go delete mode 100644 pkg/adapter/loon/vless.go delete mode 100644 pkg/adapter/loon/vmess.go delete mode 100644 pkg/adapter/proxy/proxy.go delete mode 100644 pkg/adapter/proxy/tool.go delete mode 100644 pkg/adapter/quantumultx/build.go delete mode 100644 pkg/adapter/quantumultx/quantumux_test.go delete mode 100644 pkg/adapter/quantumultx/shadowsocks.go delete mode 100644 pkg/adapter/quantumultx/trojan.go delete mode 100644 pkg/adapter/quantumultx/vmess.go delete mode 100644 pkg/adapter/shadowrocket/build.go delete mode 100644 pkg/adapter/shadowrocket/shadowrocket_test.go delete mode 100644 pkg/adapter/shadowrocket/vmess.go delete mode 100644 pkg/adapter/singbox/anytls.go delete mode 100644 pkg/adapter/singbox/build.go delete mode 100644 pkg/adapter/singbox/default.go delete mode 100644 pkg/adapter/singbox/hysteria2.go delete mode 100644 pkg/adapter/singbox/multiplex.go delete mode 100644 pkg/adapter/singbox/rule.go delete mode 100644 pkg/adapter/singbox/rule_test.go delete mode 100644 pkg/adapter/singbox/shadowsocks.go delete mode 100644 pkg/adapter/singbox/singbox.go delete mode 100644 pkg/adapter/singbox/singbox_test.go delete mode 100644 pkg/adapter/singbox/tls.go delete mode 100644 pkg/adapter/singbox/tool.go delete mode 100644 pkg/adapter/singbox/trojan.go delete mode 100644 pkg/adapter/singbox/tuic.go delete mode 100644 pkg/adapter/singbox/v2rayTransport.go delete mode 100644 pkg/adapter/singbox/vless.go delete mode 100644 pkg/adapter/singbox/vmess.go delete mode 100644 pkg/adapter/surfboard/build.go delete mode 100644 pkg/adapter/surfboard/build_test.go delete mode 100644 pkg/adapter/surfboard/default.tpl delete mode 100644 pkg/adapter/surfboard/model.go delete mode 100644 pkg/adapter/surfboard/shadowsocks.go delete mode 100644 pkg/adapter/surfboard/shadowsocks_test.go delete mode 100644 pkg/adapter/surfboard/trojan.go delete mode 100644 pkg/adapter/surfboard/trojan_test.go delete mode 100644 pkg/adapter/surfboard/vmess.go delete mode 100644 pkg/adapter/surfboard/vmess_test.go delete mode 100644 pkg/adapter/surge/default.tpl delete mode 100644 pkg/adapter/surge/hysteria2.go delete mode 100644 pkg/adapter/surge/hysteria2_test.go delete mode 100644 pkg/adapter/surge/shadowsocks.go delete mode 100644 pkg/adapter/surge/surge.go delete mode 100644 pkg/adapter/surge/surge_test.go delete mode 100644 pkg/adapter/surge/trojan.go delete mode 100644 pkg/adapter/surge/vmess.go delete mode 100644 pkg/adapter/template/clash.tpl delete mode 100644 pkg/adapter/uilts.go delete mode 100644 pkg/adapter/v2rayn/v2rayN.go diff --git a/pkg/adapter/adapter.go b/pkg/adapter/adapter.go deleted file mode 100644 index be121b9..0000000 --- a/pkg/adapter/adapter.go +++ /dev/null @@ -1,102 +0,0 @@ -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" - "github.com/perfect-panel/server/pkg/adapter/loon" - "github.com/perfect-panel/server/pkg/adapter/proxy" - "github.com/perfect-panel/server/pkg/adapter/quantumultx" - "github.com/perfect-panel/server/pkg/adapter/shadowrocket" - "github.com/perfect-panel/server/pkg/adapter/singbox" - "github.com/perfect-panel/server/pkg/adapter/surfboard" - "github.com/perfect-panel/server/pkg/adapter/surge" - "github.com/perfect-panel/server/pkg/adapter/v2rayn" -) - -//go:embed template/* -var TemplateFS embed.FS - -var ( - AutoSelect = "Auto - UrlTest" -) - -type Config struct { - Nodes []*server.Server - Rules []*server.RuleGroup - Tags map[string][]*server.Server -} - -type Adapter struct { - proxy.Adapter -} - -func NewAdapter(cfg *Config) *Adapter { - // 转换服务器列表 - proxies, nodes, tags := adapterProxies(cfg.Nodes) - // 转换规则组 - g, r, d := adapterRules(cfg.Rules) - if d == "" { - d = AutoSelect - } - // 生成默认代理组 - proxyGroup := append(generateDefaultGroup(), g...) - // 合并代理组 - proxyGroup = SortGroups(proxyGroup, nodes, tags, d) - return &Adapter{ - Adapter: proxy.Adapter{ - 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) -} - -// BuildSurge generates a Surge configuration for the given UUID and site name. -func (m *Adapter) BuildSurge(siteName string, user surge.UserInfo) []byte { - return surge.NewSurge(m.Adapter).Build(siteName, user) -} diff --git a/pkg/adapter/adapter_test.go b/pkg/adapter/adapter_test.go deleted file mode 100644 index 73c86f6..0000000 --- a/pkg/adapter/adapter_test.go +++ /dev/null @@ -1,138 +0,0 @@ -package adapter - -import ( - "encoding/json" - "fmt" - "testing" - "time" - - "github.com/perfect-panel/server/internal/model/server" - "github.com/perfect-panel/server/pkg/adapter/surfboard" -) - -func createTestServer() []*server.Server { - c := server.Shadowsocks{ - Method: "aes-256-gcm", - Port: 10301, - ServerKey: "", - } - data, _ := json.Marshal(c) - - relays := creatRelayNode() - relay, _ := json.Marshal(relays) - enable := true - // 创建一个测试用的服务器列表 - return []*server.Server{ - { - Id: 1, - Name: "Test Server 1", - Tags: "", - Country: "CN", - City: "", - Latitude: "", - Longitude: "", - ServerAddr: "test1.example.com", - RelayMode: "random", - RelayNode: string(relay), - SpeedLimit: 0, - TrafficRatio: 0, - GroupId: 0, - Protocol: "shadowsocks", - Config: string(data), - Enable: &enable, - Sort: 0, - }, - } -} -func creatRelayNode() []*server.NodeRelay { - var nodes []*server.NodeRelay - for i := 0; i < 10; i++ { - port := 10301 + i - c := server.NodeRelay{ - Host: fmt.Sprintf("192.168.1.%d", i), - Port: port, - Prefix: fmt.Sprintf("relay-%d", i), - } - nodes = append(nodes, &c) - } - return nodes -} - -func TestNewAdapter(t *testing.T) { - nodes := createTestServer() - - rules := []*server.RuleGroup{ - { - Name: "Test Rule Group 1", - Tags: "", - Rules: "DOMAIN-SUFFIX,example.com,Test Rule Group 1", - }, - } - - adapter := NewAdapter(nodes, rules) - bytes, err := adapter.BuildClash("some-uuid") - if err != nil { - t.Errorf("Failed to build adapter: %v", err) - return - } - t.Logf("Adapter built successfully: %s", string(bytes)) -} - -func TestAdapter_BuildSingbox(t *testing.T) { - nodes := createTestServer() - - rules := []*server.RuleGroup{ - { - Name: "Test Rule Group 1", - Tags: "", - Rules: "DOMAIN-SUFFIX,example.com,Test Rule Group 1", - }, - } - - adapter := NewAdapter(nodes, rules) - bytes, err := adapter.BuildSingbox("some-uuid") - if err != nil { - t.Errorf("Failed to build adapter: %v", err) - return - } - var pretty map[string]interface{} - _ = json.Unmarshal(bytes, &pretty) - - if pretty == nil { - t.Errorf("Failed to parse Singbox config") - return - } - - prettyStr, err := json.MarshalIndent(pretty, "", " ") - if err != nil { - t.Errorf("Failed to format Singbox config: %v", err) - return - } - t.Logf("Adapter built successfully: \n %s", string(prettyStr)) -} - -func TestAdapter_BuildSurfboard(t *testing.T) { - nodes := createTestServer() - rules := []*server.RuleGroup{ - { - Name: "Test Rule Group 1", - Tags: "", - Rules: "DOMAIN-SUFFIX,example.com,Test Rule Group 1", - }, - } - adapter := NewAdapter(nodes, rules) - user := surfboard.UserInfo{ - UUID: "some-uuid", - Upload: 200, - Download: 13012, - TotalTraffic: 1024000, - ExpiredDate: time.Now().Add(24 * time.Hour), - SubscribeURL: "", - } - bytes := adapter.BuildSurfboard("test-site", user) - if bytes == nil { - t.Errorf("Failed to build adapter") - return - } - t.Logf("Adapter built successfully: %s", string(bytes)) -} diff --git a/pkg/adapter/clash/clash.go b/pkg/adapter/clash/clash.go deleted file mode 100644 index 0f9f35c..0000000 --- a/pkg/adapter/clash/clash.go +++ /dev/null @@ -1,95 +0,0 @@ -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" -) - -type Clash struct { - proxy.Adapter -} - -func NewClash(adapter proxy.Adapter) *Clash { - return &Clash{ - Adapter: adapter, - } -} - -func (c *Clash) Build(uuid string) ([]byte, error) { - var proxies []Proxy - for _, proxied := range c.Adapter.Proxies { - p, err := c.parseProxy(proxied, uuid) - if err != nil { - logger.Errorw("Failed to parse proxy", logger.Field("error", err), logger.Field("proxy", p.Name)) - continue - } - proxies = append(proxies, *p) - } - var groups []ProxyGroup - for _, group := range c.Adapter.Group { - groups = append(groups, ProxyGroup{ - Name: group.Name, - Type: string(group.Type), - Proxies: group.Proxies, - Url: group.URL, - Interval: group.Interval, - }) - } - 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) { - parseFuncs := map[string]func(proxy.Proxy, string) (*Proxy, error){ - "shadowsocks": parseShadowsocks, - "trojan": parseTrojan, - "vless": parseVless, - "vmess": parseVmess, - "hysteria2": parseHysteria2, - "tuic": parseTuic, - "anytls": parseAnyTLS, - } - - if parseFunc, exists := parseFuncs[p.Protocol]; exists { - return parseFunc(p, uuid) - } - - logger.Errorw("Unknown protocol", logger.Field("protocol", p.Protocol), logger.Field("server", p.Name)) - return nil, fmt.Errorf("unknown protocol: %s", p.Protocol) -} diff --git a/pkg/adapter/clash/clash_test.go b/pkg/adapter/clash/clash_test.go deleted file mode 100644 index 89c8d20..0000000 --- a/pkg/adapter/clash/clash_test.go +++ /dev/null @@ -1,41 +0,0 @@ -package clash - -import ( - "testing" - - "github.com/perfect-panel/server/pkg/adapter/proxy" - "github.com/stretchr/testify/assert" -) - -func TestClash_Build(t *testing.T) { - adapter := proxy.Adapter{ - Proxies: []proxy.Proxy{ - { - Name: "test-proxy", - Protocol: "shadowsocks", - Server: "1.2.3.4", - Port: 8388, - Option: proxy.Shadowsocks{ - Method: "aes-256-gcm", - }, - }, - }, - Group: []proxy.Group{ - { - Name: "test-group", - Type: "select", - Proxies: []string{"test-proxy"}, - }, - }, - Rules: []string{ - "DOMAIN-SUFFIX,example.com,DIRECT", - "GEOIP,CN,DIRECT", - "MATCH,DIRECT", - }, - } - clash := NewClash(adapter) - result, err := clash.Build("test-uuid") - assert.NoError(t, err) - assert.NotNil(t, result) - -} diff --git a/pkg/adapter/clash/default.go b/pkg/adapter/clash/default.go deleted file mode 100644 index 2c2ceb0..0000000 --- a/pkg/adapter/clash/default.go +++ /dev/null @@ -1,54 +0,0 @@ -package clash - -const DefaultTemplate = ` -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: - -proxy-groups: - -rules: -` diff --git a/pkg/adapter/clash/model.go b/pkg/adapter/clash/model.go deleted file mode 100644 index 9f9925d..0000000 --- a/pkg/adapter/clash/model.go +++ /dev/null @@ -1,131 +0,0 @@ -package clash - -type RawConfig struct { - Port int `yaml:"port" json:"port"` - SocksPort int `yaml:"socks-port" json:"socks-port"` - RedirPort int `yaml:"redir-port" json:"redir-port"` - TProxyPort int `yaml:"tproxy-port" json:"tproxy-port"` - MixedPort int `yaml:"mixed-port" json:"mixed-port"` - AllowLan bool `yaml:"allow-lan" json:"allow-lan"` - Mode string `yaml:"mode" json:"mode"` - LogLevel string `yaml:"log-level" json:"log-level"` - ExternalController string `yaml:"external-controller" json:"external-controller"` - Secret string `yaml:"secret" json:"secret"` - Proxies []Proxy `yaml:"proxies" json:"proxies"` - ProxyGroups []ProxyGroup `yaml:"proxy-groups" json:"proxy-groups"` - Rules []string `yaml:"rules" json:"rule"` -} - -type Proxy struct { - // 基础数据 - Name string `yaml:"name"` - Type string `yaml:"type"` - Server string `yaml:"server"` - Port int `yaml:"port,omitempty"` - // Shadowsocks - Password string `yaml:"password,omitempty"` - Cipher string `yaml:"cipher,omitempty"` - UDP bool `yaml:"udp,omitempty"` - Plugin string `yaml:"plugin,omitempty"` - PluginOpts map[string]any `yaml:"plugin-opts,omitempty"` - UDPOverTCP bool `yaml:"udp-over-tcp,omitempty"` - UDPOverTCPVersion int `yaml:"udp-over-tcp-version,omitempty"` - ClientFingerprint string `yaml:"client-fingerprint,omitempty"` - // Vmess - UUID string `yaml:"uuid,omitempty"` - AlterID *int `yaml:"alterId,omitempty"` - Network string `yaml:"network,omitempty"` - TLS bool `yaml:"tls,omitempty"` - ALPN []string `yaml:"alpn,omitempty"` - SkipCertVerify bool `yaml:"skip-cert-verify,omitempty"` - Fingerprint string `yaml:"fingerprint,omitempty"` - ServerName string `yaml:"servername,omitempty"` - RealityOpts RealityOptions `yaml:"reality-opts,omitempty"` - HTTPOpts HTTPOptions `yaml:"http-opts,omitempty"` - HTTP2Opts HTTP2Options `yaml:"h2-opts,omitempty"` - GrpcOpts GrpcOptions `yaml:"grpc-opts,omitempty"` - WSOpts WSOptions `yaml:"ws-opts,omitempty"` - PacketAddr bool `yaml:"packet-addr,omitempty"` - XUDP bool `yaml:"xudp,omitempty"` - PacketEncoding string `yaml:"packet-encoding,omitempty"` - GlobalPadding bool `yaml:"global-padding,omitempty"` - AuthenticatedLength bool `yaml:"authenticated-length,omitempty"` - // Vless - Flow string `yaml:"flow,omitempty"` - WSPath string `yaml:"ws-path,omitempty"` - WSHeaders map[string]string `yaml:"ws-headers,omitempty"` - // Trojan - SNI string `yaml:"sni,omitempty"` - SSOpts TrojanSSOption `yaml:"ss-opts,omitempty"` - // Hysteria2 - Ports string `yaml:"ports,omitempty"` - HopInterval int `yaml:"hop-interval,omitempty"` - Up string `yaml:"up,omitempty"` - Down string `yaml:"down,omitempty"` - Obfs string `yaml:"obfs,omitempty"` - ObfsPassword string `yaml:"obfs-password,omitempty"` - CustomCA string `yaml:"ca,omitempty"` - CustomCAString string `yaml:"ca-str,omitempty"` - CWND int `yaml:"cwnd,omitempty"` - UdpMTU int `yaml:"udp-mtu,omitempty"` - // Tuic - Token string `yaml:"token,omitempty"` - Ip string `yaml:"ip,omitempty"` - HeartbeatInterval int `yaml:"heartbeat-interval,omitempty"` - ReduceRtt bool `yaml:"reduce-rtt,omitempty"` - RequestTimeout int `yaml:"request-timeout,omitempty"` - UdpRelayMode string `yaml:"udp-relay-mode,omitempty"` - CongestionController string `yaml:"congestion-controller,omitempty"` - DisableSni bool `yaml:"disable-sni,omitempty"` - MaxUdpRelayPacketSize int `yaml:"max-udp-relay-packet-size,omitempty"` - FastOpen bool `yaml:"fast-open,omitempty"` - MaxOpenStreams int `yaml:"max-open-streams,omitempty"` - ReceiveWindowConn int `yaml:"recv-window-conn,omitempty"` - ReceiveWindow int `yaml:"recv-window,omitempty"` - DisableMTUDiscovery bool `yaml:"disable-mtu-discovery,omitempty"` - MaxDatagramFrameSize int `yaml:"max-datagram-frame-size,omitempty"` - UDPOverStream bool `yaml:"udp-over-stream,omitempty"` - UDPOverStreamVersion int `yaml:"udp-over-stream-version,omitempty"` -} -type ProxyGroup struct { - Name string `yaml:"name"` - Type string `yaml:"type"` - Proxies []string `yaml:"proxies"` - Url string `yaml:"url,omitempty"` - Interval int `yaml:"interval,omitempty"` -} - -type TrojanSSOption struct { - Enabled bool `yaml:"enabled,omitempty"` - Method string `yaml:"method,omitempty"` - Password string `yaml:"password,omitempty"` -} - -type RealityOptions struct { - PublicKey string `yaml:"public-key"` - ShortID string `yaml:"short-id"` -} - -type HTTPOptions struct { - Method string `yaml:"method,omitempty"` - Path []string `yaml:"path,omitempty"` - Headers map[string][]string `yaml:"headers,omitempty"` -} - -type HTTP2Options struct { - Host []string `yaml:"host,omitempty"` - Path string `yaml:"path,omitempty"` -} - -type GrpcOptions struct { - GrpcServiceName string `yaml:"grpc-service-name,omitempty"` -} - -type WSOptions struct { - Path string `yaml:"path,omitempty"` - Headers map[string]string `yaml:"headers,omitempty"` - MaxEarlyData int `yaml:"max-early-data,omitempty"` - EarlyDataHeaderName string `yaml:"early-data-header-name,omitempty"` - V2rayHttpUpgrade bool `yaml:"v2ray-http-upgrade,omitempty"` - V2rayHttpUpgradeFastOpen bool `yaml:"v2ray-http-upgrade-fast-open,omitempty"` -} diff --git a/pkg/adapter/clash/parse.go b/pkg/adapter/clash/parse.go deleted file mode 100644 index eb0cc95..0000000 --- a/pkg/adapter/clash/parse.go +++ /dev/null @@ -1,207 +0,0 @@ -package clash - -import ( - "fmt" - "strings" - - "github.com/perfect-panel/server/pkg/adapter/proxy" -) - -func parseShadowsocks(s proxy.Proxy, uuid string) (*Proxy, error) { - config, ok := s.Option.(proxy.Shadowsocks) - if !ok { - return nil, fmt.Errorf("invalid type for Shadowsocks") - } - p := &Proxy{ - Name: s.Name, - Type: "ss", - Server: s.Server, - Port: s.Port, - Cipher: config.Method, - Password: uuid, - UDP: true, - } - - if strings.Contains(p.Cipher, "2022") { - serverKey, userKey := proxy.GenerateShadowsocks2022Password(config, uuid) - p.Password = fmt.Sprintf("%s:%s", serverKey, userKey) - } - - return p, nil -} - -func parseTrojan(data proxy.Proxy, password string) (*Proxy, error) { - trojan, ok := data.Option.(proxy.Trojan) - if !ok { - return nil, fmt.Errorf("invalid type for Trojan") - } - p := &Proxy{ - Name: data.Name, - Type: "trojan", - Server: data.Server, - Port: data.Port, - Password: password, - SNI: trojan.SecurityConfig.SNI, - SkipCertVerify: trojan.SecurityConfig.AllowInsecure, - } - setTransportOptions(p, trojan.Transport, trojan.TransportConfig) - return p, nil -} - -func parseVless(data proxy.Proxy, uuid string) (*Proxy, error) { - vless, ok := data.Option.(proxy.Vless) - if !ok { - return nil, fmt.Errorf("invalid type for Vless") - } - p := &Proxy{ - Name: data.Name, - Type: "vless", - Server: data.Server, - Port: data.Port, - UUID: uuid, - Flow: vless.Flow, - UDP: true, - } - setSecurityOptions(p, vless.Security, vless.SecurityConfig) - clashTransport(p, vless.Transport, vless.TransportConfig) - return p, nil -} - -func parseVmess(data proxy.Proxy, uuid string) (*Proxy, error) { - vmess, ok := data.Option.(proxy.Vmess) - if !ok { - return nil, fmt.Errorf("invalid type for Vmess") - } - alterID := 0 - p := &Proxy{ - Name: data.Name, - Type: "vmess", - Server: data.Server, - Port: data.Port, - UUID: uuid, - AlterID: &alterID, - Cipher: "auto", - } - setSecurityOptions(p, vmess.Security, vmess.SecurityConfig) - clashTransport(p, vmess.Transport, vmess.TransportConfig) - return p, nil -} - -func parseHysteria2(data proxy.Proxy, uuid string) (*Proxy, error) { - hysteria2, ok := data.Option.(proxy.Hysteria2) - if !ok { - return nil, fmt.Errorf("invalid type for Hysteria2") - } - p := &Proxy{ - Name: data.Name, - Type: "hysteria2", - Server: data.Server, - Port: data.Port, - Ports: hysteria2.HopPorts, - Password: uuid, - HeartbeatInterval: hysteria2.HopInterval, - SkipCertVerify: hysteria2.SecurityConfig.AllowInsecure, - SNI: hysteria2.SecurityConfig.SNI, - } - if hysteria2.ObfsPassword != "" { - p.Obfs = "salamander" - p.ObfsPassword = hysteria2.ObfsPassword - } - - return p, nil -} - -func parseTuic(data proxy.Proxy, uuid string) (*Proxy, error) { - tuic, ok := data.Option.(proxy.Tuic) - if !ok { - return nil, fmt.Errorf("invalid type for Tuic") - } - - p := &Proxy{ - Name: data.Name, - Type: "tuic", - Server: data.Server, - Port: data.Port, - UUID: uuid, - Password: uuid, - ALPN: []string{"h3"}, - DisableSni: tuic.DisableSNI, - ReduceRtt: tuic.ReduceRtt, - CongestionController: tuic.CongestionController, - UdpRelayMode: tuic.UDPRelayMode, - SNI: tuic.SecurityConfig.SNI, - SkipCertVerify: tuic.SecurityConfig.AllowInsecure, - } - - return p, nil -} - -func parseAnyTLS(data proxy.Proxy, uuid string) (*Proxy, error) { - anyTLS, ok := data.Option.(proxy.AnyTLS) - if !ok { - return nil, fmt.Errorf("invalid type for AnyTLS") - } - - p := &Proxy{ - Name: data.Name, - Type: "anytls", - Server: data.Server, - Port: data.Port, - Password: uuid, - UDP: true, - ALPN: []string{ - "h2", - "http/1.1", - }, - } - - if anyTLS.SecurityConfig.SNI != "" { - p.SNI = anyTLS.SecurityConfig.SNI - } - if anyTLS.SecurityConfig.AllowInsecure { - p.SkipCertVerify = anyTLS.SecurityConfig.AllowInsecure - } - - return p, nil -} - -func setSecurityOptions(p *Proxy, security string, config proxy.SecurityConfig) { - switch security { - case "tls": - p.TLS = true - p.ServerName = config.SNI - p.ClientFingerprint = config.Fingerprint - p.SkipCertVerify = config.AllowInsecure - case "reality": - p.TLS = true - p.ServerName = config.SNI - p.ClientFingerprint = config.Fingerprint - p.RealityOpts = RealityOptions{ - PublicKey: config.RealityPublicKey, - ShortID: config.RealityShortId, - } - p.SkipCertVerify = config.AllowInsecure - default: - p.TLS = false - } -} - -func setTransportOptions(p *Proxy, transport string, config proxy.TransportConfig) { - switch transport { - case "websocket": - p.Network = "ws" - p.WSOpts = WSOptions{ - Path: config.Path, - Headers: map[string]string{ - "Host": config.Host, - }, - } - case "grpc": - p.Network = "grpc" - p.GrpcOpts = GrpcOptions{ - GrpcServiceName: config.ServiceName, - } - default: - p.Network = "tcp" - } -} diff --git a/pkg/adapter/clash/tool.go b/pkg/adapter/clash/tool.go deleted file mode 100644 index ee073fe..0000000 --- a/pkg/adapter/clash/tool.go +++ /dev/null @@ -1,35 +0,0 @@ -package clash - -import ( - "github.com/perfect-panel/server/pkg/adapter/proxy" -) - -func clashTransport(c *Proxy, transportType string, transportConfig proxy.TransportConfig) { - - switch transportType { - case "websocket", "httpupgrade": - if transportType == "websocket" { - c.Network = "ws" - } else { - c.Network = transportType - } - c.WSOpts = WSOptions{ - Path: transportConfig.Path, - Headers: map[string]string{}, - } - if transportConfig.Host != "" { - c.WSOpts.Headers["host"] = transportConfig.Host - } - if transportType == "httpupgrade" { - c.WSOpts.V2rayHttpUpgrade = true - } - case "grpc": - c.Network = "grpc" - c.GrpcOpts = GrpcOptions{ - GrpcServiceName: transportConfig.ServiceName, - } - case "tcp": - c.Network = "tcp" - } - -} diff --git a/pkg/adapter/general/uri.go b/pkg/adapter/general/uri.go deleted file mode 100644 index c1c925b..0000000 --- a/pkg/adapter/general/uri.go +++ /dev/null @@ -1,278 +0,0 @@ -package general - -import ( - "encoding/base64" - "encoding/json" - "fmt" - "net" - "net/url" - "strconv" - "strings" - - "github.com/perfect-panel/server/pkg/adapter/proxy" -) - -type v2rayShareLink struct { - Ps string `json:"ps"` - Add string `json:"add"` - Port string `json:"port"` - ID string `json:"id"` - Aid string `json:"aid"` - Net string `json:"net"` - Type string `json:"type"` - Host string `json:"host"` - SNI string `json:"sni"` - Path string `json:"path"` - TLS string `json:"tls"` - Flow string `json:"flow,omitempty"` - Alpn string `json:"alpn,omitempty"` - AllowInsecure bool `json:"allowInsecure"` - Fingerprint string `json:"fp,omitempty"` - PublicKey string `json:"pbk,omitempty"` - ShortId string `json:"sid,omitempty"` - SpiderX string `json:"spx,omitempty"` - V string `json:"v"` -} - -// GenerateBase64General will output node URLs split by '\n' and then encode into base64 -func GenerateBase64General(data []proxy.Proxy, uuid string) []byte { - var links []string - for _, v := range data { - p := buildProxy(v, uuid) - if p == "" { - continue - } - links = append(links, p) - } - var rsp []byte - rsp = base64.RawStdEncoding.AppendEncode(rsp, []byte(strings.Join(links, "\r\n"))) - return rsp -} - -func buildProxy(data proxy.Proxy, uuid string) string { - switch data.Protocol { - case "shadowsocks": - return ShadowsocksUri(data, uuid) - case "vmess": - return VmessUri(data, uuid) - case "vless": - return VlessUri(data, uuid) - case "trojan": - return TrojanUri(data, uuid) - case "hysteria2": - return Hysteria2Uri(data, uuid) - case "tuic": - return TuicUri(data, uuid) - default: - return "" - } -} - -func ShadowsocksUri(data proxy.Proxy, uuid string) string { - ss, ok := data.Option.(proxy.Shadowsocks) - if !ok { - return "" - } - - password := uuid - // SIP022 AEAD-2022 Ciphers - if strings.Contains(ss.Method, "2022") { - serverKey, userKey := proxy.GenerateShadowsocks2022Password(ss, uuid) - password = fmt.Sprintf("%s:%s", serverKey, userKey) - } - - u := &url.URL{ - Scheme: "ss", - User: url.User(strings.TrimSuffix(base64.URLEncoding.EncodeToString([]byte(ss.Method+":"+password)), "=")), - Host: net.JoinHostPort(data.Server, strconv.Itoa(data.Port)), - Fragment: data.Name, - } - return u.String() -} - -func VmessUri(data proxy.Proxy, uuid string) string { - vmess := data.Option.(proxy.Vmess) - - transport := vmess.TransportConfig - - securityConfig := vmess.SecurityConfig - - var s = v2rayShareLink{ - V: "2", - Add: data.Server, - Port: fmt.Sprint(data.Port), - ID: uuid, - Aid: "0", - Ps: data.Name, - Net: "tcp", - } - - switch vmess.Transport { - case "websocket": - s.Net = "ws" - s.Path = transport.Path - s.Host = transport.Host - case "grpc": - s.Net = "grpc" - s.Path = transport.ServiceName - case "httpupgrade": - s.Net = "http" - s.Path = transport.Path - s.Host = transport.Host - } - - if vmess.Security == "tls" { - s.TLS = "tls" - s.SNI = securityConfig.SNI - s.AllowInsecure = securityConfig.AllowInsecure - s.Fingerprint = securityConfig.Fingerprint - } - b, _ := json.Marshal(s) - return "vmess://" + strings.TrimSuffix(base64.StdEncoding.EncodeToString(b), "=") -} - -func VlessUri(data proxy.Proxy, uuid string) string { - vless := data.Option.(proxy.Vless) - transportConfig := vless.TransportConfig - securityConfig := vless.SecurityConfig - - var query = make(url.Values) - setQuery(&query, "flow", vless.Flow) - setQuery(&query, "security", vless.Security) - setQuery(&query, "encryption", "none") - - switch vless.Transport { - case "websocket": - setQuery(&query, "type", "ws") - setQuery(&query, "host", transportConfig.Host) - setQuery(&query, "path", transportConfig.Path) - - case "http2", "httpupgrade": - setQuery(&query, "type", "http") - setQuery(&query, "path", transportConfig.Path) - setQuery(&query, "host", transportConfig.Host) - case "grpc": - setQuery(&query, "type", "grpc") - setQuery(&query, "serviceName", transportConfig.ServiceName) - } - - if vless.Security == "tls" { - setQuery(&query, "sni", securityConfig.SNI) - setQuery(&query, "fp", securityConfig.Fingerprint) - } else if vless.Security == "reality" { - setQuery(&query, "pbk", securityConfig.RealityPublicKey) - setQuery(&query, "sid", securityConfig.RealityShortId) - setQuery(&query, "sni", securityConfig.SNI) - setQuery(&query, "fp", securityConfig.Fingerprint) - setQuery(&query, "servername", securityConfig.SNI) - setQuery(&query, "spx", "/") - - } - - u := url.URL{ - Scheme: "vless", - User: url.User(uuid), - Host: net.JoinHostPort(data.Server, fmt.Sprint(data.Port)), - RawQuery: query.Encode(), - Fragment: data.Name, - } - return u.String() -} - -func TrojanUri(data proxy.Proxy, uuid string) string { - trojan := data.Option.(proxy.Trojan) - transportConfig := trojan.TransportConfig - securityConfig := trojan.SecurityConfig - - var query = make(url.Values) - setQuery(&query, "security", trojan.Security) - - switch trojan.Transport { - case "websocket": - setQuery(&query, "type", "ws") - setQuery(&query, "path", transportConfig.Path) - setQuery(&query, "host", transportConfig.Host) - case "grpc": - setQuery(&query, "type", "grpc") - setQuery(&query, "serviceName", transportConfig.ServiceName) - default: - setQuery(&query, "type", "tcp") - setQuery(&query, "path", transportConfig.Path) - setQuery(&query, "host", transportConfig.Host) - } - - if securityConfig.AllowInsecure { - setQuery(&query, "allowInsecure", "1") - } - - u := &url.URL{ - Scheme: "trojan", - User: url.User(uuid), - Host: net.JoinHostPort(data.Server, strconv.Itoa(data.Port)), - RawQuery: query.Encode(), - Fragment: data.Name, - } - return u.String() -} - -func Hysteria2Uri(data proxy.Proxy, uuid string) string { - hysteria2 := data.Option.(proxy.Hysteria2) - - var query = make(url.Values) - - setQuery(&query, "sni", hysteria2.SecurityConfig.SNI) - - if hysteria2.SecurityConfig.AllowInsecure { - setQuery(&query, "insecure", "1") - } - - if hp := strings.TrimSpace(hysteria2.HopPorts); hp != "" { - setQuery(&query, "mport", hp) - } - - if hysteria2.ObfsPassword != "" { - setQuery(&query, "obfs", "salamander") - setQuery(&query, "obfs-password", hysteria2.ObfsPassword) - } - - u := &url.URL{ - Scheme: "hysteria2", - User: url.User(uuid), - Host: net.JoinHostPort(data.Server, strconv.Itoa(data.Port)), - RawQuery: query.Encode(), - Fragment: data.Name, - } - return u.String() -} - -func TuicUri(data proxy.Proxy, uuid string) string { - tuic := data.Option.(proxy.Tuic) - var query = make(url.Values) - - setQuery(&query, "congestion_control", tuic.CongestionController) - setQuery(&query, "udp_relay_mode", tuic.UDPRelayMode) - - if tuic.SecurityConfig.SNI != "" { - setQuery(&query, "sni", tuic.SecurityConfig.SNI) - } else { - setQuery(&query, "disable_sni", "1") - } - if tuic.SecurityConfig.AllowInsecure { - setQuery(&query, "allow_insecure", "1") - } - - u := &url.URL{ - Scheme: "tuic", - User: url.User(uuid + ":" + uuid), - Host: net.JoinHostPort(data.Server, strconv.Itoa(data.Port)), - RawQuery: query.Encode(), - Fragment: data.Name, - } - return u.String() -} - -func setQuery(q *url.Values, k, v string) { - if v != "" { - q.Set(k, v) - } -} diff --git a/pkg/adapter/general/uri_test.go b/pkg/adapter/general/uri_test.go deleted file mode 100644 index d2317ae..0000000 --- a/pkg/adapter/general/uri_test.go +++ /dev/null @@ -1,26 +0,0 @@ -package general - -import ( - "testing" - - "github.com/perfect-panel/server/pkg/adapter/proxy" -) - -func createServer() proxy.Proxy { - return proxy.Proxy{ - Name: "Meta", - Server: "127.0.0.1", - Port: 13092, - Protocol: "shadowsocks", - Option: proxy.Shadowsocks{ - Method: "aes-256-gcm", - ServerKey: "", - }, - } -} - -func TestGenerateBase64General(t *testing.T) { - s := createServer() - p := buildProxy(s, "935b33c7-e128-49f2-816b-71070469cac2") - t.Log(p) -} diff --git a/pkg/adapter/loon/build.go b/pkg/adapter/loon/build.go deleted file mode 100644 index ebbc642..0000000 --- a/pkg/adapter/loon/build.go +++ /dev/null @@ -1,61 +0,0 @@ -package loon - -import ( - "bytes" - "embed" - "strings" - "text/template" - - "github.com/perfect-panel/server/pkg/adapter/proxy" - "github.com/perfect-panel/server/pkg/logger" -) - -//go:embed *.tpl -var configFiles embed.FS - -func BuildLoon(servers []proxy.Proxy, uuid string) []byte { - uri := "" - nodes := make([]string, 0) - for _, s := range servers { - switch s.Protocol { - case "vmess": - nodes = append(nodes, s.Name) - uri += buildVMess(s, uuid) - case "shadowsocks": - nodes = append(nodes, s.Name) - uri += buildShadowsocks(s, uuid) - case "trojan": - nodes = append(nodes, s.Name) - uri += buildTrojan(s, uuid) - case "vless": - nodes = append(nodes, s.Name) - uri += buildVless(s, uuid) - case "hysteria2": - nodes = append(nodes, s.Name) - uri += buildHysteria2(s, uuid) - default: - continue - } - } - file, err := configFiles.ReadFile("default.tpl") - if err != nil { - logger.Errorf("read default surfboard config error: %v", err.Error()) - return nil - } - // replace template - tpl, err := template.New("default").Parse(string(file)) - if err != nil { - logger.Errorf("read default surfboard config error: %v", err.Error()) - return nil - } - var buf bytes.Buffer - if err = tpl.Execute(&buf, map[string]interface{}{ - "Proxies": uri, - "Nodes": strings.Join(nodes, ","), - }); err != nil { - logger.Errorf("Execute Loon template error: %v", err.Error()) - return nil - } - - return buf.Bytes() -} diff --git a/pkg/adapter/loon/default.tpl b/pkg/adapter/loon/default.tpl deleted file mode 100644 index bca1046..0000000 --- a/pkg/adapter/loon/default.tpl +++ /dev/null @@ -1,58 +0,0 @@ -[General] -ipv6-vif = auto -ip-mode = dual -skip-proxy = 192.168.0.0/16,10.0.0.0/8,172.16.0.0/12,127.0.0.0/8,localhost,*.local -bypass-tun = 192.168.0.0/16,10.0.0.0/8,172.16.0.0/12,127.0.0.0/8,localhost,*.local -dns-server = system,119.29.29.29,223.5.5.5 -hijack-dns = 8.8.8.8:53,8.8.4.4:53,1.1.1.1:53,1.0.0.1:53 -allow-wifi-access = true -wifi-access-http-port = 6888 -wifi-access-socks5-port = 6889 -proxy-test-url = http://bing.com/generate_204 -internet-test-url = http://wifi.vivo.com.cn/generate_204 -test-timeout = 5 -interface-mode = auto - -[Proxy] -{{.Proxies}} - -[Proxy Group] -🚀 Proxy = select,🌏 Auto,{{.Nodes}} -🌏 Auto = fallback,{{.Nodes}},interval = 600,max-timeout = 3000 -🍎 Apple = select,🚀 Proxy,🎯 Direct,{{.Nodes}} -🔍 Google = select,🚀 Proxy,{{.Nodes}} -🪟 Microsoft = select,🚀 Proxy,🎯 Direct,{{.Nodes}} -📠 X = select,🚀 Proxy,{{.Nodes}} -🤖 AI = select,🚀 Proxy,🎯 Direct,{{.Nodes}} -📟 Telegram = select,🚀 Proxy,{{.Nodes}} -📺 YouTube = select,🚀 Proxy,{{.Nodes}} -🇨🇳 China = select,🎯 Direct,🚀 Proxy,{{.Nodes}} -🐠 Final = select,🚀 Proxy,🎯 Direct,{{.Nodes}} -🎯 Direct = select,DIRECT - -[Remote Rule] -https://raw.githubusercontent.com/blackmatrix7/ios_rule_script/master/rule/Loon/Apple/Apple.list, policy=🍎 Apple, tag=Apple, enabled=true -https://raw.githubusercontent.com/blackmatrix7/ios_rule_script/master/rule/Loon/Apple/Apple_Domain.list, policy=🍎 Apple, tag=Apple_Domain, enabled=true -https://raw.githubusercontent.com/blackmatrix7/ios_rule_script/master/rule/Loon/Google/Google.list, policy=🔍 Google, tag=Google, enabled=true -https://raw.githubusercontent.com/blackmatrix7/ios_rule_script/master/rule/Loon/Microsoft/Microsoft.list, policy=🪟 Microsoft, tag=Microsoft, enabled=true -https://raw.githubusercontent.com/blackmatrix7/ios_rule_script/master/rule/Loon/Twitter/Twitter.list, policy=📠 X, tag=X, enabled=true -https://raw.githubusercontent.com/blackmatrix7/ios_rule_script/master/rule/Loon/OpenAI/OpenAI.list, policy=🤖 AI, tag=OpenAI, enabled=true -https://raw.githubusercontent.com/blackmatrix7/ios_rule_script/master/rule/Loon/Telegram/Telegram.list, policy=📟 Telegram, tag=Telegram, enabled=true -https://raw.githubusercontent.com/blackmatrix7/ios_rule_script/master/rule/Loon/YouTube/YouTube.list, policy=📺 YouTube, tag=YouTube, enabled=true -https://raw.githubusercontent.com/blackmatrix7/ios_rule_script/master/rule/Loon/YouTubeMusic/YouTubeMusic.list, policy=📺 YouTube, tag=YouTubeMusic, enabled=true -https://raw.githubusercontent.com/blackmatrix7/ios_rule_script/master/rule/Loon/Global/Global.list, policy=🚀 Proxy, tag=Global, enabled=true -https://raw.githubusercontent.com/blackmatrix7/ios_rule_script/master/rule/Loon/Global/Global_Domain.list, policy=🚀 Proxy, tag=Global_Domain, enabled=true -https://raw.githubusercontent.com/blackmatrix7/ios_rule_script/master/rule/Loon/ChinaMax/ChinaMax.list, policy=🇨🇳 China, tag=ChinaMax, enabled=true -https://raw.githubusercontent.com/blackmatrix7/ios_rule_script/master/rule/Loon/ChinaMax/ChinaMax_Domain.list, policy=🇨🇳 China, tag=ChinaMax_Domain, enabled=true -https://raw.githubusercontent.com/blackmatrix7/ios_rule_script/master/rule/Loon/Lan/Lan.list, policy=🎯 Direct, tag=LAN, enabled=true - -[Rule] -GEOIP,CN,🇨🇳 China -FINAL,🐠 Final - -[Rewrite] -# Redirect Google Service -^https?:\/\/(www.)?g\.cn 302 https://www.google.com -^https?:\/\/(www.)?google\.cn 302 https://www.google.com -# Redirect Githubusercontent -^https://.*\.githubusercontent\.com\/ header-replace Accept-Language en-us \ No newline at end of file diff --git a/pkg/adapter/loon/hysteria2.go b/pkg/adapter/loon/hysteria2.go deleted file mode 100644 index 9a39c96..0000000 --- a/pkg/adapter/loon/hysteria2.go +++ /dev/null @@ -1,34 +0,0 @@ -package loon - -import ( - "fmt" - "strconv" - "strings" - - "github.com/perfect-panel/server/pkg/adapter/proxy" -) - -func buildHysteria2(data proxy.Proxy, password string) string { - hysteria2 := data.Option.(proxy.Hysteria2) - - configs := []string{ - fmt.Sprintf("%s=Hysteria2", data.Name), - data.Server, - strconv.Itoa(data.Port), - password, - "udp=true", - } - if hysteria2.ObfsPassword != "" { - configs = append(configs, "obfs=salamander", fmt.Sprintf("salamander-password=%s", hysteria2.ObfsPassword)) - } - if hysteria2.SecurityConfig.SNI != "" { - configs = append(configs, fmt.Sprintf("sni=%s", hysteria2.SecurityConfig.SNI)) - if hysteria2.SecurityConfig.AllowInsecure { - configs = append(configs, "skip-cert-verify=true") - } else { - configs = append(configs, "skip-cert-verify=false") - } - } - uri := strings.Join(configs, ",") - return uri + "\r\n" -} diff --git a/pkg/adapter/loon/loon_test.go b/pkg/adapter/loon/loon_test.go deleted file mode 100644 index 25fdb2c..0000000 --- a/pkg/adapter/loon/loon_test.go +++ /dev/null @@ -1,29 +0,0 @@ -package loon - -import ( - "testing" - - "github.com/perfect-panel/server/pkg/adapter/proxy" -) - -func createSS() proxy.Proxy { - return proxy.Proxy{ - Name: "Shadowsocks", - Server: "127.0.0.1", - Port: 10301, - Protocol: "shadowsocks", - Option: proxy.Shadowsocks{ - Method: "aes-256-gcm", - ServerKey: "", - }, - } - -} - -func TestBuildSS(t *testing.T) { - s := createSS() - - password := "f0d0237d-193a-4cf5-99dd-b02207beaea6" - uri := buildShadowsocks(s, password) - t.Log(uri) -} diff --git a/pkg/adapter/loon/shadowsocks.go b/pkg/adapter/loon/shadowsocks.go deleted file mode 100644 index 3f1d8e8..0000000 --- a/pkg/adapter/loon/shadowsocks.go +++ /dev/null @@ -1,34 +0,0 @@ -package loon - -import ( - "fmt" - "strconv" - "strings" - - "github.com/perfect-panel/server/pkg/adapter/proxy" -) - -func buildShadowsocks(data proxy.Proxy, password string) string { - shadowsocks := data.Option.(proxy.Shadowsocks) - // If the method is 2022-blake3-chacha20-poly1305, it means that the server is a relay server - if shadowsocks.Method == "2022-blake3-chacha20-poly1305" { - return "" - } - - if strings.Contains(shadowsocks.Method, "2022") { - serverKey, userKey := proxy.GenerateShadowsocks2022Password(shadowsocks, password) - password = fmt.Sprintf("%s:%s", serverKey, userKey) - } - - configs := []string{ - fmt.Sprintf("%s=Shadowsocks", data.Name), - data.Server, - strconv.Itoa(data.Port), - shadowsocks.Method, - password, - "fast-open=false", - "udp=true", - } - uri := strings.Join(configs, ",") - return uri + "\r\n" -} diff --git a/pkg/adapter/loon/trojan.go b/pkg/adapter/loon/trojan.go deleted file mode 100644 index 5630992..0000000 --- a/pkg/adapter/loon/trojan.go +++ /dev/null @@ -1,43 +0,0 @@ -package loon - -import ( - "fmt" - "strings" - - "github.com/perfect-panel/server/pkg/adapter/proxy" -) - -func buildTrojan(data proxy.Proxy, password string) string { - trojan := data.Option.(proxy.Trojan) - - configs := []string{ - fmt.Sprintf("%s=trojan", data.Name), - data.Server, - fmt.Sprintf("%d", data.Port), - password, - "fast-open=false", - "udp=true", - } - - if trojan.SecurityConfig.SNI != "" { - configs = append(configs, fmt.Sprintf("sni=%s", trojan.SecurityConfig.SNI)) - } - if trojan.SecurityConfig.AllowInsecure { - configs = append(configs, "skip-cert-verify=true") - } else { - configs = append(configs, "skip-cert-verify=false") - } - - if trojan.Transport == "websocket" { - configs = append(configs, "transport=ws") - if trojan.TransportConfig.Path != "" { - configs = append(configs, fmt.Sprintf("path=%s", trojan.TransportConfig.Path)) - } - if trojan.TransportConfig.Host != "" { - configs = append(configs, fmt.Sprintf("host=%s", trojan.TransportConfig.Host)) - } - } - - uri := strings.Join(configs, ",") - return uri + "\r\n" -} diff --git a/pkg/adapter/loon/vless.go b/pkg/adapter/loon/vless.go deleted file mode 100644 index 355f147..0000000 --- a/pkg/adapter/loon/vless.go +++ /dev/null @@ -1,62 +0,0 @@ -package loon - -import ( - "fmt" - "strconv" - "strings" - - "github.com/perfect-panel/server/pkg/adapter/proxy" - - "github.com/perfect-panel/server/pkg/logger" -) - -func buildVless(data proxy.Proxy, password string) string { - vless := data.Option.(proxy.Vless) - // If flow is not empty, it means that the server is a relay server - if vless.Flow != "" { - return "" - } - - configs := []string{ - fmt.Sprintf("%s=vless", data.Name), - data.Server, - strconv.Itoa(data.Port), - "auto", - password, - "fast-open=false", - "udp=true", - "alterId=0", - } - - switch vless.Transport { - case "tcp": - configs = append(configs, "transport=tcp") - case "websocket": - configs = append(configs, "transport=ws") - if vless.TransportConfig.Path != "" { - configs = append(configs, fmt.Sprintf("path=%s", vless.TransportConfig.Path)) - } - if vless.TransportConfig.Host != "" { - configs = append(configs, fmt.Sprintf("host=%s", vless.TransportConfig.Host)) - } - default: - logger.Info("Loon Unknown transport type: ", logger.Field("transport", vless.Transport)) - return "" - } - - if vless.Security == "tls" { - configs = append(configs, "over-tls=true", fmt.Sprintf("tls-name=%s", vless.SecurityConfig.SNI)) - if vless.SecurityConfig.AllowInsecure { - configs = append(configs, "skip-cert-verify=true") - } else { - configs = append(configs, "skip-cert-verify=false") - } - } else if vless.Security == "reality" { - // Loon does not support reality security - logger.Info("Loon Unknown security type: ", logger.Field("security", vless.Security)) - return "" - } - - uri := strings.Join(configs, ",") - return uri + "\r\n" -} diff --git a/pkg/adapter/loon/vmess.go b/pkg/adapter/loon/vmess.go deleted file mode 100644 index 04e7e6d..0000000 --- a/pkg/adapter/loon/vmess.go +++ /dev/null @@ -1,53 +0,0 @@ -package loon - -import ( - "fmt" - "strings" - - "github.com/perfect-panel/server/pkg/adapter/proxy" - "github.com/perfect-panel/server/pkg/logger" -) - -func buildVMess(data proxy.Proxy, password string) string { - vmess := data.Option.(proxy.Vmess) - - configs := []string{ - fmt.Sprintf("%s=vmess", data.Name), - data.Server, - fmt.Sprintf("%d", data.Port), - "auto", - password, - "fast-open=false", - "udp=true", - "alterId=0", - } - - switch vmess.Transport { - case "tcp": - configs = append(configs, "transport=tcp") - case "websocket": - configs = append(configs, "transport=ws") - if vmess.TransportConfig.Path != "" { - configs = append(configs, fmt.Sprintf("path=%s", vmess.TransportConfig.Path)) - } - if vmess.TransportConfig.Host != "" { - configs = append(configs, fmt.Sprintf("host=%s", vmess.TransportConfig.Host)) - } - default: - logger.Info("Loon Unknown transport type: ", logger.Field("transport", vmess.Transport)) - return "" - } - - if vmess.Security == "tls" { - configs = append(configs, "over-tls=true", fmt.Sprintf("tls-name=%s", vmess.SecurityConfig.SNI)) - if vmess.SecurityConfig.AllowInsecure { - configs = append(configs, "skip-cert-verify=true") - } else { - configs = append(configs, "skip-cert-verify=false") - } - - } - - uri := strings.Join(configs, ",") - return uri + "\r\n" -} diff --git a/pkg/adapter/proxy/proxy.go b/pkg/adapter/proxy/proxy.go deleted file mode 100644 index 75760ff..0000000 --- a/pkg/adapter/proxy/proxy.go +++ /dev/null @@ -1,137 +0,0 @@ -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 - TemplateFS *embed.FS // Template file system -} - -// Proxy represents a proxy server -type Proxy struct { - 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 -type Group struct { - Name string - Type GroupType - Proxies []string - URL string - Interval int - Reject bool // Reject group - Direct bool // Direct group - Tags []string // Tags for the group - Default bool // Default group -} - -type GroupType string - -const ( - GroupTypeSelect GroupType = "select" - GroupTypeURLTest GroupType = "url-test" - GroupTypeFallback GroupType = "fallback" -) - -// Shadowsocks represents a Shadowsocks proxy configuration -type Shadowsocks struct { - Port int `json:"port"` - Method string `json:"method"` - ServerKey string `json:"server_key"` -} - -// Vless represents a Vless proxy configuration -type Vless struct { - Port int `json:"port"` - Flow string `json:"flow"` - Transport string `json:"transport"` - TransportConfig TransportConfig `json:"transport_config"` - Security string `json:"security"` - SecurityConfig SecurityConfig `json:"security_config"` -} - -// Vmess represents a Vmess proxy configuration -type Vmess struct { - Port int `json:"port"` - Flow string `json:"flow"` - Transport string `json:"transport"` - TransportConfig TransportConfig `json:"transport_config"` - Security string `json:"security"` - SecurityConfig SecurityConfig `json:"security_config"` -} - -// Trojan represents a Trojan proxy configuration -type Trojan struct { - Port int `json:"port"` - Flow string `json:"flow"` - Transport string `json:"transport"` - TransportConfig TransportConfig `json:"transport_config"` - Security string `json:"security"` - SecurityConfig SecurityConfig `json:"security_config"` -} - -// Hysteria2 represents a Hysteria2 proxy configuration -type Hysteria2 struct { - Port int `json:"port"` - HopPorts string `json:"hop_ports"` - HopInterval int `json:"hop_interval"` - ObfsPassword string `json:"obfs_password"` - SecurityConfig SecurityConfig `json:"security_config"` -} - -// Tuic represents a Tuic proxy configuration -type Tuic struct { - Port int `json:"port"` - DisableSNI bool `json:"disable_sni"` - ReduceRtt bool `json:"reduce_rtt"` - UDPRelayMode string `json:"udp_relay_mode"` - CongestionController string `json:"congestion_controller"` - SecurityConfig SecurityConfig `json:"security_config"` -} - -// AnyTLS represents an AnyTLS proxy configuration -type AnyTLS struct { - Port int `json:"port"` - SecurityConfig SecurityConfig `json:"security_config"` -} - -// TransportConfig represents the transport configuration for a proxy -type TransportConfig struct { - Path string `json:"path,omitempty"` // ws/httpupgrade - Host string `json:"host,omitempty"` - ServiceName string `json:"service_name"` // grpc - DisableSNI bool `json:"disable_sni"` // Disable SNI for the transport(tuic) - ReduceRtt bool `json:"reduce_rtt"` // Reduce RTT for the transport(tuic) - UDPRelayMode string `json:"udp_relay_mode"` // UDP relay mode for the transport(tuic) - CongestionController string `json:"congestion_controller"` // Congestion controller for the transport(tuic) -} - -// SecurityConfig represents the security configuration for a proxy -type SecurityConfig struct { - SNI string `json:"sni"` - AllowInsecure bool `json:"allow_insecure"` - Fingerprint string `json:"fingerprint"` - RealityServerAddr string `json:"reality_server_addr"` - RealityServerPort int `json:"reality_server_port"` - RealityPrivateKey string `json:"reality_private_key"` - RealityPublicKey string `json:"reality_public_key"` - RealityShortId string `json:"reality_short_id"` -} - -// Relay represents a relay configuration -type Relay struct { - RelayHost string - DispatchMode string - Prefix string -} diff --git a/pkg/adapter/proxy/tool.go b/pkg/adapter/proxy/tool.go deleted file mode 100644 index 014c581..0000000 --- a/pkg/adapter/proxy/tool.go +++ /dev/null @@ -1,15 +0,0 @@ -package proxy - -import ( - "encoding/base64" - "github.com/perfect-panel/server/pkg/uuidx" -) - -func GenerateShadowsocks2022Password(ss Shadowsocks, password string) (string, string) { - if ss.Method == "2022-blake3-aes-128-gcm" { - password = uuidx.UUIDToBase64(password, 16) - } else { - password = uuidx.UUIDToBase64(password, 32) - } - return base64.StdEncoding.EncodeToString([]byte(ss.ServerKey)), password -} diff --git a/pkg/adapter/quantumultx/build.go b/pkg/adapter/quantumultx/build.go deleted file mode 100644 index 52898a9..0000000 --- a/pkg/adapter/quantumultx/build.go +++ /dev/null @@ -1,22 +0,0 @@ -package quantumultx - -import ( - "encoding/base64" - - "github.com/perfect-panel/server/pkg/adapter/proxy" -) - -func BuildQuantumultX(servers []proxy.Proxy, uuid string) string { - var uri string - for _, s := range servers { - switch s.Protocol { - case "vmess": - uri += buildVmess(s, uuid) - case "shadowsocks": - uri += buildShadowsocks(s, uuid) - case "trojan": - uri += buildTrojan(s, uuid) - } - } - return base64.StdEncoding.EncodeToString([]byte(uri)) -} diff --git a/pkg/adapter/quantumultx/quantumux_test.go b/pkg/adapter/quantumultx/quantumux_test.go deleted file mode 100644 index 35e7f88..0000000 --- a/pkg/adapter/quantumultx/quantumux_test.go +++ /dev/null @@ -1,94 +0,0 @@ -package quantumultx - -import ( - "testing" - - "github.com/perfect-panel/server/pkg/adapter/proxy" -) - -func createVMess() proxy.Proxy { - - return proxy.Proxy{ - Name: "Vmess", - Server: "test.xxxx.com", - Port: 13002, - Protocol: "vmess", - Option: proxy.Vmess{ - Port: 13002, - Transport: "websocket", - TransportConfig: proxy.TransportConfig{ - Path: "/ws", - Host: "test.xx.com", - }, - Security: "none", - }, - } -} - -func createSS() proxy.Proxy { - return proxy.Proxy{ - Name: "Shadowsocks", - Server: "test.xxxx.com", - Port: 10301, - Protocol: "shadowsocks", - Option: proxy.Shadowsocks{ - Port: 10301, - Method: "aes-256-gcm", - ServerKey: "123456", - }, - } -} - -func createTrojan() proxy.Proxy { - - return proxy.Proxy{ - Name: "Trojan", - Server: "test.xxxx.com", - Port: 13002, - Protocol: "trojan", - Option: proxy.Trojan{ - Port: 13002, - Transport: "websocket", - TransportConfig: proxy.TransportConfig{ - Path: "/ws", - Host: "baidu.com", - }, - SecurityConfig: proxy.SecurityConfig{ - SNI: "baidu.com", - AllowInsecure: true, - }, - }, - } -} -func TestVmess(t *testing.T) { - s := createVMess() - vmess := buildVmess(s, "uuid") - t.Log(vmess) - // output: - // vmess=127.0.0.1:13002,method=chacha20-poly1305,password=uuid,fast-open=true,udp-relay=true,tag=Vmess,tls-verification=true,obfs-uri=/ws,obfs-host=baidu.com -} - -func TestShadowsocks(t *testing.T) { - s := createSS() - shadowsocks := buildShadowsocks(s, "uuid") - t.Log(shadowsocks) - // output: - // shadowsocks=127.0.0.1:10301,method=aes-256-gcm,password=uuid,fast-open=true,udp-relay=true,tag=Shadowsocks -} - -func TestTrojan(t *testing.T) { - s := createTrojan() - trojan := buildTrojan(s, "password") - t.Log(trojan) - // output: - // trojan=192.168.0.1:13002,password=password,fast-open=true,udp-relay=true,tag=Trojan,obfs=wss,obfs-uri=ws,obfs-host=baidu.com -} - -func TestBuildQuantumultX(t *testing.T) { - var servers []proxy.Proxy - uri := BuildQuantumultX(servers, "uuid") - t.Log(uri) - - // output: - // c2hhZG93c29ja3M9MTI3LjAuMC4xOjEwMzAxLG1ldGhvZD1hZXMtMjU2LWdjbSxwYXNzd29yZD11dWlkLGZhc3Qtb3Blbj10cnVlLHVkcC1yZWxheT10cnVlLHRhZz1TaGFkb3dzb2Nrcw0KdHJvamFuPTE5Mi4xNjguMC4xOjEzMDAyLHBhc3N3b3JkPXV1aWQsZmFzdC1vcGVuPXRydWUsdWRwLXJlbGF5PXRydWUsdGFnPVRyb2phbixvYmZzPXdzcyxvYmZzLXVyaT13cyxvYmZzLWhvc3Q9YmFpZHUuY29tDQp2bWVzcz0xMjcuMC4wLjE6MTMwMDIsbWV0aG9kPWNoYWNoYTIwLXBvbHkxMzA1LHBhc3N3b3JkPXV1aWQsZmFzdC1vcGVuPXRydWUsdWRwLXJlbGF5PXRydWUsdGFnPVZtZXNzLHRscy12ZXJpZmljYXRpb249dHJ1ZSxvYmZzLXVyaT0vd3Msb2Jmcy1ob3N0PWJhaWR1LmNvbQ0K -} diff --git a/pkg/adapter/quantumultx/shadowsocks.go b/pkg/adapter/quantumultx/shadowsocks.go deleted file mode 100644 index ca1f253..0000000 --- a/pkg/adapter/quantumultx/shadowsocks.go +++ /dev/null @@ -1,30 +0,0 @@ -package quantumultx - -import ( - "fmt" - "strings" - - "github.com/perfect-panel/server/pkg/adapter/proxy" -) - -func buildShadowsocks(data proxy.Proxy, uuid string) string { - ss := data.Option.(proxy.Shadowsocks) - addr := fmt.Sprintf("%s:%d", data.Server, data.Port) - - password := uuid - - if strings.Contains(ss.Method, "2022") { - serverKey, userKey := proxy.GenerateShadowsocks2022Password(ss, uuid) - password = fmt.Sprintf("%s:%s", serverKey, userKey) - } - - config := []string{ - addr, - fmt.Sprintf("method=%s", ss.Method), - fmt.Sprintf("password=%s", password), - "fast-open=true", - "udp-relay=true", - fmt.Sprintf("tag=%s", data.Name), - } - return strings.Join(config, ",") + "\r\n" -} diff --git a/pkg/adapter/quantumultx/trojan.go b/pkg/adapter/quantumultx/trojan.go deleted file mode 100644 index 7a701aa..0000000 --- a/pkg/adapter/quantumultx/trojan.go +++ /dev/null @@ -1,39 +0,0 @@ -package quantumultx - -import ( - "fmt" - "strings" - - "github.com/perfect-panel/server/pkg/adapter/proxy" -) - -// 生成 Trojan 配置 -func buildTrojan(data proxy.Proxy, password string) string { - trojan := data.Option.(proxy.Trojan) - - addr := fmt.Sprintf("trojan=%s:%d", data.Server, data.Port) - config := []string{ - addr, - fmt.Sprintf("password=%s", password), - "fast-open=true", - "udp-relay=true", - fmt.Sprintf("tag=%s", data.Name), - } - - if trojan.Transport == "websocket" { - config = append(config, "obfs=wss") - if trojan.TransportConfig.Path != "" { - config = append(config, fmt.Sprintf("obfs-uri=%s", trojan.TransportConfig.Path)) - } - if trojan.TransportConfig.Host != "" { - config = append(config, fmt.Sprintf("obfs-host=%s", trojan.TransportConfig.Host)) - } - } else { - config = append(config, "over-tls=true") - if trojan.SecurityConfig.SNI != "" { - config = append(config, fmt.Sprintf("tls-host=%s", trojan.SecurityConfig.SNI)) - } - } - - return strings.Join(config, ",") + "\r\n" -} diff --git a/pkg/adapter/quantumultx/vmess.go b/pkg/adapter/quantumultx/vmess.go deleted file mode 100644 index d448a94..0000000 --- a/pkg/adapter/quantumultx/vmess.go +++ /dev/null @@ -1,45 +0,0 @@ -package quantumultx - -import ( - "fmt" - "strings" - - "github.com/perfect-panel/server/pkg/adapter/proxy" -) - -func buildVmess(data proxy.Proxy, uuid string) string { - - vmess := data.Option.(proxy.Vmess) - addr := fmt.Sprintf("vmess=%s:%d", data.Server, data.Port) - var host string - uriConfig := []string{ - addr, - "method=chacha20-poly1305", - fmt.Sprintf("password=%s", uuid), - "fast-open=true", - "udp-relay=true", - fmt.Sprintf("tag=%s", data.Name), - } - if vmess.Security == "tls" { - if vmess.Transport == "tcp" { - uriConfig = append(uriConfig, "obfs=over-tls") - } - if vmess.SecurityConfig.AllowInsecure { - uriConfig = append(uriConfig, "tls-verification=true") - } else { - uriConfig = append(uriConfig, "tls-verification=false") - } - if vmess.SecurityConfig.SNI != "" { - host = vmess.SecurityConfig.SNI - } - } - - if vmess.Transport == "websocket" { - uriConfig = append(uriConfig, fmt.Sprintf("obfs-uri=%s", vmess.TransportConfig.Path)) - host = vmess.TransportConfig.Host - } - if host != "" { - uriConfig = append(uriConfig, fmt.Sprintf("obfs-host=%s", host)) - } - return strings.Join(uriConfig, ",") + "\r\n" -} diff --git a/pkg/adapter/shadowrocket/build.go b/pkg/adapter/shadowrocket/build.go deleted file mode 100644 index b4b61a5..0000000 --- a/pkg/adapter/shadowrocket/build.go +++ /dev/null @@ -1,48 +0,0 @@ -package shadowrocket - -import ( - "fmt" - "time" - - "github.com/perfect-panel/server/pkg/adapter/general" - - "encoding/base64" - - "github.com/perfect-panel/server/pkg/adapter/proxy" - "github.com/perfect-panel/server/pkg/traffic" -) - -type UserInfo struct { - Upload int64 - Download int64 - TotalTraffic int64 - ExpiredDate time.Time -} - -func BuildShadowrocket(servers []proxy.Proxy, uuid string, userinfo UserInfo) []byte { - upload := traffic.AutoConvert(userinfo.Upload, false) - download := traffic.AutoConvert(userinfo.Download, false) - total := traffic.AutoConvert(userinfo.TotalTraffic, false) - expiredAt := userinfo.ExpiredDate.Format("2006-01-02 15:04:05") - uri := fmt.Sprintf("STATUS=🚀↑:%s,↓:%s,TOT:%s💡Expires:%s\r\n", upload, download, total, expiredAt) - for _, s := range servers { - switch s.Protocol { - case "vmess": - uri += buildVmess(s, uuid) - case "shadowsocks": - uri += general.ShadowsocksUri(s, uuid) + "\r\n" - case "trojan": - uri += general.TrojanUri(s, uuid) + "\r\n" - case "vless": - uri += general.VlessUri(s, uuid) + "\r\n" - case "hysteria2": - uri += general.Hysteria2Uri(s, uuid) + "\r\n" - case "tuic": - uri += general.TuicUri(s, uuid) + "\r\n" - default: - continue - } - } - - return []byte(base64.StdEncoding.EncodeToString([]byte(uri))) -} diff --git a/pkg/adapter/shadowrocket/shadowrocket_test.go b/pkg/adapter/shadowrocket/shadowrocket_test.go deleted file mode 100644 index c4266ce..0000000 --- a/pkg/adapter/shadowrocket/shadowrocket_test.go +++ /dev/null @@ -1,76 +0,0 @@ -package shadowrocket - -import ( - "testing" - "time" - - "github.com/perfect-panel/server/pkg/adapter/proxy" -) - -func createVMess() proxy.Proxy { - return proxy.Proxy{ - Name: "Vmess", - Server: "test.xxxx.com", - Port: 13002, - Protocol: "vmess", - Option: proxy.Vmess{ - Port: 13002, - Transport: "websocket", - TransportConfig: proxy.TransportConfig{ - Path: "/ws", - Host: "test.xx.com", - }, - Security: "none", - }, - } -} - -func createSS() proxy.Proxy { - return proxy.Proxy{ - Name: "Shadowsocks", - Server: "test.xxxx.com", - Port: 10301, - Protocol: "shadowsocks", - Option: proxy.Shadowsocks{ - Port: 10301, - Method: "aes-256-gcm", - ServerKey: "123456", - }, - } -} - -func createTrojan() proxy.Proxy { - - return proxy.Proxy{ - Name: "Trojan", - Server: "test.xxxx.com", - Port: 13002, - Protocol: "trojan", - Option: proxy.Trojan{ - Port: 13002, - Transport: "websocket", - TransportConfig: proxy.TransportConfig{ - Path: "/ws", - Host: "baidu.com", - }, - SecurityConfig: proxy.SecurityConfig{ - SNI: "baidu.com", - AllowInsecure: true, - }, - }, - } -} -func TestBuildShadowrocket(t *testing.T) { - s := []proxy.Proxy{ - createVMess(), - createSS(), - createTrojan(), - } - uri := BuildShadowrocket(s, "uuid", UserInfo{ - Upload: 1024, - Download: 1024, - TotalTraffic: 2048, - ExpiredDate: time.Now().AddDate(0, 0, 1), - }) - t.Log(string(uri)) -} diff --git a/pkg/adapter/shadowrocket/vmess.go b/pkg/adapter/shadowrocket/vmess.go deleted file mode 100644 index 45ae526..0000000 --- a/pkg/adapter/shadowrocket/vmess.go +++ /dev/null @@ -1,57 +0,0 @@ -package shadowrocket - -import ( - "fmt" - "strings" - - "encoding/base64" - - "github.com/perfect-panel/server/pkg/adapter/proxy" -) - -func buildVmess(data proxy.Proxy, uuid string) string { - vmess := data.Option.(proxy.Vmess) - - userinfo := fmt.Sprintf("auto:%s@%s:%d", uuid, data.Server, data.Port) - // 准备 config,使用默认值 - config := map[string]interface{}{ - "tfo": 1, - "remark": data.Name, - "alterId": 0, - } - - // tls 配置 - if vmess.Security == "tls" { - config["tls"] = 1 - if vmess.SecurityConfig.AllowInsecure { - config["allowInsecure"] = 1 - } - if vmess.SecurityConfig.SNI != "" { - config["peer"] = vmess.SecurityConfig.SNI - } - } - - // transport 配置 - switch vmess.Transport { - case "websocket": - config["obfs"] = "websocket" - if vmess.TransportConfig.Path != "" { - config["path"] = vmess.TransportConfig.Path - } - if vmess.TransportConfig.Host != "" { - config["obfsParam"] = vmess.TransportConfig.Host - } - case "grpc": - config["obfs"] = "grpc" - if vmess.TransportConfig.ServiceName != "" { - config["path"] = vmess.TransportConfig.ServiceName - } - } - query := make([]string, 0) - for k, v := range config { - query = append(query, fmt.Sprintf("%s=%v", k, v)) - } - queryStr := strings.Join(query, "&") - uri := fmt.Sprintf("vmess://%s?%s\r\n", base64.StdEncoding.EncodeToString([]byte(userinfo)), queryStr) - return uri -} diff --git a/pkg/adapter/singbox/anytls.go b/pkg/adapter/singbox/anytls.go deleted file mode 100644 index 700f0d9..0000000 --- a/pkg/adapter/singbox/anytls.go +++ /dev/null @@ -1,42 +0,0 @@ -package singbox - -import "github.com/perfect-panel/server/pkg/adapter/proxy" - -type AnyTLSOutboundOptions struct { - ServerOptions - OutboundTLSOptionsContainer - Password string `json:"password,omitempty"` -} - -func ParseAnyTLS(data proxy.Proxy, password string) (*Proxy, error) { - anyTLS := data.Option.(proxy.AnyTLS) - - config := &AnyTLSOutboundOptions{ - ServerOptions: ServerOptions{ - Tag: data.Name, - Type: AnyTLS, - Server: data.Server, - ServerPort: data.Port, - }, - OutboundTLSOptionsContainer: OutboundTLSOptionsContainer{ - TLS: &OutboundTLSOptions{ - Enabled: true, - ALPN: []string{"h2", "http/1.1"}, - Insecure: anyTLS.SecurityConfig.AllowInsecure, - }, - }, - Password: password, - } - - if anyTLS.SecurityConfig.SNI != "" { - config.OutboundTLSOptionsContainer.TLS.ServerName = anyTLS.SecurityConfig.SNI - } - - p := &Proxy{ - Tag: data.Name, - Type: AnyTLS, - AnyTLSOptions: config, - } - - return p, nil -} diff --git a/pkg/adapter/singbox/build.go b/pkg/adapter/singbox/build.go deleted file mode 100644 index 8962c2b..0000000 --- a/pkg/adapter/singbox/build.go +++ /dev/null @@ -1,201 +0,0 @@ -package singbox - -import ( - "encoding/json" - - "github.com/perfect-panel/server/pkg/adapter/proxy" - "github.com/perfect-panel/server/pkg/logger" -) - -func BuildSingbox(adapter proxy.Adapter, uuid string) ([]byte, error) { - // build outbounds type is Proxy - var proxies []Proxy - // build outbound group - for _, group := range adapter.Group { - if group.Type == proxy.GroupTypeSelect { - selector := Proxy{ - Type: Selector, - Tag: group.Name, - SelectorOptions: &SelectorOutboundOptions{ - OutboundOptions: OutboundOptions{ - Tag: group.Name, - Type: Selector, - }, - Outbounds: group.Proxies, - Default: group.Proxies[0], - InterruptExistConnections: false, - }, - } - proxies = append(proxies, selector) - } else if group.Type == proxy.GroupTypeURLTest { - selector := Proxy{ - Type: URLTest, - Tag: group.Name, - URLTestOptions: &URLTestOutboundOptions{ - OutboundOptions: OutboundOptions{ - Tag: group.Name, - Type: URLTest, - }, - Outbounds: group.Proxies, - URL: group.URL, - }, - } - proxies = append(proxies, selector) - } else { - logger.Errorf("[sing-box] Unknown group type: %s, group name: %s", group.Type, group.Name) - } - } - - // build outbounds - for _, data := range adapter.Proxies { - p := buildProxy(data, uuid) - if p == nil { - continue - } - proxies = append(proxies, *p) - } - - // add direct outbound - direct := Proxy{ - Type: Direct, - Tag: "DIRECT", - } - // add block outbound - block := Proxy{ - Type: Block, - Tag: "block", - } - // add dns outbound - dns := Proxy{ - Type: DNS, - Tag: "dns-out", - } - proxies = append(proxies, direct, block, dns) - - var rawConfig map[string]any - if err := json.Unmarshal([]byte(DefaultTemplate), &rawConfig); err != nil { - return nil, err - } - - rawConfig["outbounds"] = proxies - route := RouteOptions{ - Final: adapter.Default, - Rules: []Rule{ - { - Inbound: []string{ - "tun-in", - "mixed-in", - }, - Action: "sniff", - }, - { - Type: "logical", - Mode: "or", - Rules: []Rule{ - { - Port: []uint16{53}, - }, - { - Protocol: []string{"dns"}, - }, - }, - Action: "hijack-dns", - }, - { - RuleSet: []string{ - "geosite-category-ads-all", - }, - ClashMode: "rule", - Action: "reject", - }, - { - ClashMode: "direct", - Outbound: "DIRECT", - }, - { - ClashMode: "global", - Outbound: adapter.Default, - }, - { - IPIsPrivate: true, - Outbound: "DIRECT", - }, - { - RuleSet: []string{ - "geosite-private", - }, - Outbound: "DIRECT", - }, - }, - RuleSet: []RuleSet{ - { - Tag: "geoip-cn", - Type: "remote", - Format: "binary", - URL: "https://testingcf.jsdelivr.net/gh/MetaCubeX/meta-rules-dat@sing/geo/geoip/cn.srs", - DownloadDetour: "DIRECT", - }, - { - Tag: "geosite-cn", - Type: "remote", - Format: "binary", - URL: "https://testingcf.jsdelivr.net/gh/MetaCubeX/meta-rules-dat@sing/geo/geosite/cn.srs", - DownloadDetour: "DIRECT", - }, - { - Tag: "geosite-private", - Type: "remote", - Format: "binary", - URL: "https://testingcf.jsdelivr.net/gh/MetaCubeX/meta-rules-dat@sing/geo/geosite/private.srs", - DownloadDetour: "DIRECT", - }, - { - Tag: "geosite-category-ads-all", - Type: "remote", - Format: "binary", - URL: "https://testingcf.jsdelivr.net/gh/MetaCubeX/meta-rules-dat@sing/geo/geosite/category-ads-all.srs", - DownloadDetour: "DIRECT", - }, - { - Tag: "geosite-geolocation-!cn", - Type: "remote", - Format: "binary", - URL: "https://testingcf.jsdelivr.net/gh/MetaCubeX/meta-rules-dat@sing/geo/geosite/geolocation-!cn.srs", - DownloadDetour: "DIRECT", - }, - }, - AutoDetectInterface: true, - } - route.Rules = append(route.Rules, adapterToSingboxRule(adapter.Rules)...) - rawConfig["route"] = route - return json.Marshal(rawConfig) -} - -func buildProxy(data proxy.Proxy, uuid string) *Proxy { - var p *Proxy - var err error - switch data.Protocol { - case VLESS: - p, err = ParseVless(data, uuid) - case Shadowsocks: - p, err = ParseShadowsocks(data, uuid) - case Trojan: - p, err = ParseTrojan(data, uuid) - case VMess: - p, err = ParseVMess(data, uuid) - - case Hysteria2: - p, err = ParseHysteria2(data, uuid) - - case TUIC: - p, err = ParseTUIC(data, uuid) - - default: - logger.Error("Unknown protocol", logger.Field("protocol", data.Protocol), logger.Field("server", data.Name)) - } - if err != nil { - logger.Error("ParseVless", logger.Field("error", err.Error()), logger.Field("server", data.Name), logger.Field("protocol", data.Protocol)) - return nil - } - return p -} diff --git a/pkg/adapter/singbox/default.go b/pkg/adapter/singbox/default.go deleted file mode 100644 index d73b236..0000000 --- a/pkg/adapter/singbox/default.go +++ /dev/null @@ -1,100 +0,0 @@ -package singbox - -const DefaultTemplate = ` -{ - "log": { - "level": "info", - "timestamp": true - }, - "experimental": { - "clash_api": { - "external_controller": "127.0.0.1:9090", - "external_ui": "ui", - "secret": "", - "external_ui_download_url": "https://mirror.ghproxy.com/https://github.com/MetaCubeX/Yacd-meta/archive/gh-pages.zip", - "external_ui_download_detour": "direct", - "default_mode": "rule" - }, - "cache_file": { - "enabled": true, - "store_fakeip": false - } - }, - "dns": { - "servers": [ - { - "tag": "dns_proxy", - "address": "tls://8.8.8.8", - "detour": "手动选择" - }, - { - "tag": "dns_direct", - "address": "https://223.5.5.5/dns-query", - "detour": "DIRECT" - } - ], - "rules": [ - { - "outbound": "any", - "server": "dns_direct", - "disable_cache": true - }, - { - "rule_set": "geosite-cn", - "server": "dns_direct" - }, - { - "clash_mode": "direct", - "server": "dns_direct" - }, - { - "clash_mode": "global", - "server": "dns_proxy" - }, - { - "rule_set": "geosite-geolocation-!cn", - "server": "dns_proxy" - } - ], - "final": "dns_direct", - "strategy": "ipv4_only" - }, - "route": { - "rules": [ - { - "action": "sniff" - }, - { - "protocol": "dns", - "action": "hijack-dns" - } - ] - }, - "inbounds": [ - { - "tag": "tun-in", - "type": "tun", - "address": [ - "172.18.0.1/30", - "fdfe:dcba:9876::1/126" - ], - "auto_route": true, - "strict_route": true, - "stack": "system", - "platform": { - "http_proxy": { - "enabled": true, - "server": "127.0.0.1", - "server_port": 7890 - } - } - }, - { - "tag": "mixed-in", - "type": "mixed", - "listen": "127.0.0.1", - "listen_port": 7890 - } - ] -} -` diff --git a/pkg/adapter/singbox/hysteria2.go b/pkg/adapter/singbox/hysteria2.go deleted file mode 100644 index b5ab2ff..0000000 --- a/pkg/adapter/singbox/hysteria2.go +++ /dev/null @@ -1,76 +0,0 @@ -package singbox - -import ( - "strings" - - "github.com/perfect-panel/server/pkg/adapter/proxy" -) - -type Hysteria2Obfs struct { - Type string `json:"type,omitempty"` - Password string `json:"password,omitempty"` -} - -type Hysteria2OutboundOptions struct { - ServerOptions - ServerPorts []string `json:"server_ports,omitempty"` - HopInterval int `json:"hop_interval,omitempty"` - UpMbps int `json:"up_mbps,omitempty"` - DownMbps int `json:"down_mbps,omitempty"` - Obfs *Hysteria2Obfs `json:"obfs,omitempty"` - Password string `json:"password,omitempty"` - Network string `json:"network,omitempty"` - OutboundTLSOptionsContainer - Multiplex *OutboundMultiplexOptions `json:"multiplex,omitempty"` - Transport *V2RayTransportOptions `json:"transport,omitempty"` -} - -func ParseHysteria2(data proxy.Proxy, password string) (*Proxy, error) { - hysteria2 := data.Option.(proxy.Hysteria2) - - p := &Proxy{ - Tag: data.Name, - Type: Hysteria2, - Hysteria2Options: &Hysteria2OutboundOptions{ - ServerOptions: ServerOptions{ - Tag: data.Name, - Type: Hysteria2, - Server: data.Server, - }, - Password: password, - }, - } - - var ports []string - - if hysteria2.HopPorts != "" { - ps := strings.Split(hysteria2.HopPorts, ",") - for _, port := range ps { - // 舍弃单个端口,只保留端口范围 - if len(strings.Split(port, "-")) > 1 { - tmp := strings.Split(port, "-") - ports = append(ports, strings.Join(tmp, ":")) - } - } - - } - if len(ports) > 0 { - p.Hysteria2Options.ServerPorts = ports - p.Hysteria2Options.HopInterval = hysteria2.HopInterval - } else { - p.Hysteria2Options.ServerPort = data.Port - } - - if hysteria2.ObfsPassword != "" { - p.Hysteria2Options.Obfs = &Hysteria2Obfs{ - Type: "salamander", - Password: hysteria2.ObfsPassword, - } - } - var tls *OutboundTLSOptions - if hysteria2.SecurityConfig.SNI != "" { - tls = NewOutboundTLSOptions("tls", hysteria2.SecurityConfig) - } - p.Hysteria2Options.TLS = tls - return p, nil -} diff --git a/pkg/adapter/singbox/multiplex.go b/pkg/adapter/singbox/multiplex.go deleted file mode 100644 index 7188f95..0000000 --- a/pkg/adapter/singbox/multiplex.go +++ /dev/null @@ -1,17 +0,0 @@ -package singbox - -type OutboundMultiplexOptions struct { - Enabled bool `json:"enabled,omitempty"` - Protocol string `json:"protocol,omitempty"` - MaxConnections int `json:"max_connections,omitempty"` - MinStreams int `json:"min_streams,omitempty"` - MaxStreams int `json:"max_streams,omitempty"` - Padding bool `json:"padding,omitempty"` - Brutal *BrutalOptions `json:"brutal,omitempty"` -} - -type BrutalOptions struct { - Enabled bool `json:"enabled,omitempty"` - UpMbps int `json:"up_mbps,omitempty"` - DownMbps int `json:"down_mbps,omitempty"` -} diff --git a/pkg/adapter/singbox/rule.go b/pkg/adapter/singbox/rule.go deleted file mode 100644 index a6446b1..0000000 --- a/pkg/adapter/singbox/rule.go +++ /dev/null @@ -1,130 +0,0 @@ -package singbox - -import ( - "strconv" - - "github.com/perfect-panel/server/pkg/logger" - "github.com/perfect-panel/server/pkg/rules" -) - -type Rule struct { - Outbound string `json:"outbound,omitempty"` - ClashMode string `json:"clash_mode,omitempty"` - RuleSet []string `json:"rule_set,omitempty"` - Domain []string `json:"domain,omitempty"` - DomainSuffix []string `json:"domain_suffix,omitempty"` - DomainKeyword []string `json:"domain_keyword,omitempty"` - DomainRegex []string `json:"domain_regex,omitempty"` - GeoIP []string `json:"geoip,omitempty"` - IPCIDR []string `json:"ip_cidr,omitempty"` - IPIsPrivate bool `json:"ip_is_private,omitempty"` - SourceIPCIDR []string `json:"source_ip_cidr,omitempty"` - ProcessName []string `json:"process_name,omitempty"` - ProcessPath []string `json:"process_path,omitempty"` - SourcePort []uint16 `json:"source_port,omitempty"` - Protocol []string `json:"protocol,omitempty"` - Port []uint16 `json:"port,omitempty"` - Action string `json:"action,omitempty"` - Inbound []string `json:"inbound,omitempty"` - Rules []Rule `json:"rules,omitempty"` - Type string `json:"type,omitempty"` - Mode string `json:"mode,omitempty"` -} - -type RuleSet struct { - Tag string `json:"tag,omitempty"` - Type string `json:"type,omitempty"` - Format string `json:"format,omitempty"` - URL string `json:"url,omitempty"` - DownloadDetour string `json:"download_detour,omitempty"` -} - -func adapterToSingboxRule(texts []string) []Rule { - var rulesList []Rule - for _, rule := range texts { - r := rules.NewRule(rule, "") - if r == nil { - continue - } - rulesList = addRuleToItem(rulesList, r.Target, *r) - } - return rulesList -} - -func addRuleToItem(group []Rule, outbound string, rule rules.Rule) []Rule { - for i := range group { - if group[i].Outbound == outbound { - switch rules.ParseRuleType(rule.Type) { - case rules.Domain: - group[i].Domain = append(group[i].Domain, rule.Payload) - return group - case rules.DomainSuffix: - group[i].DomainSuffix = append(group[i].DomainSuffix, rule.Payload) - return group - case rules.DomainKeyword: - group[i].DomainKeyword = append(group[i].DomainKeyword, rule.Payload) - return group - case rules.IPCIDR: - group[i].IPCIDR = append(group[i].IPCIDR, rule.Payload) - return group - case rules.SrcIPCIDR: - group[i].SourceIPCIDR = append(group[i].SourceIPCIDR, rule.Payload) - return group - case rules.SrcPort: - port, err := strconv.ParseUint(rule.Payload, 10, 16) - if err != nil { - logger.Errorf("[adapterToSingboxRule] failed to parse port %s to uint16", rule.Payload) - return group - } - group[i].SourcePort = append(group[i].SourcePort, uint16(port)) - return group - case rules.GEOIP: - group[i].GeoIP = append(group[i].GeoIP, rule.Payload) - return group - case rules.Process: - group[i].ProcessName = append(group[i].ProcessName, rule.Payload) - return group - case rules.ProcessPath: - group[i].ProcessPath = append(group[i].ProcessPath, rule.Payload) - return group - default: - logger.Errorf("[adapterToSingboxRule] unknown rule type %s", rule.Type) - return group - } - } - } - newRule := Rule{ - Outbound: outbound, - } - - switch rules.ParseRuleType(rule.Type) { - case rules.Domain: - newRule.Domain = []string{rule.Payload} - case rules.DomainSuffix: - newRule.DomainSuffix = []string{rule.Payload} - case rules.DomainKeyword: - newRule.DomainKeyword = []string{rule.Payload} - case rules.IPCIDR: - newRule.IPCIDR = []string{rule.Payload} - case rules.SrcIPCIDR: - newRule.SourceIPCIDR = []string{rule.Payload} - case rules.SrcPort: - port, err := strconv.ParseUint(rule.Payload, 10, 16) - if err != nil { - logger.Errorf("[adapterToSingboxRule] failed to parse port %s to uint16", rule.Payload) - return group - } - newRule.SourcePort = []uint16{uint16(port)} - case rules.GEOIP: - newRule.GeoIP = []string{rule.Payload} - case rules.Process: - newRule.ProcessName = []string{rule.Payload} - case rules.ProcessPath: - newRule.ProcessPath = []string{rule.Payload} - default: - logger.Errorf("[adapterToSingboxRule] unknown rule type %s", rule.Type) - return group - } - group = append(group, newRule) - return group -} diff --git a/pkg/adapter/singbox/rule_test.go b/pkg/adapter/singbox/rule_test.go deleted file mode 100644 index 95f8ce3..0000000 --- a/pkg/adapter/singbox/rule_test.go +++ /dev/null @@ -1,15 +0,0 @@ -package singbox - -import ( - "fmt" - "testing" -) - -func TestAdapterToSingboxRule(t *testing.T) { - rules := []string{ - "DOMAIN,example.com,DIRECT", - "DOMAIN-SUFFIX,google.com,智能线路", - } - result := adapterToSingboxRule(rules) - fmt.Printf("TestAdapterToSingboxRule: result: %+v\n", result) -} diff --git a/pkg/adapter/singbox/shadowsocks.go b/pkg/adapter/singbox/shadowsocks.go deleted file mode 100644 index fbc8db8..0000000 --- a/pkg/adapter/singbox/shadowsocks.go +++ /dev/null @@ -1,45 +0,0 @@ -package singbox - -import ( - "fmt" - "strings" - - "github.com/perfect-panel/server/pkg/adapter/proxy" -) - -type ShadowsocksOptions struct { - ServerOptions - Method string `json:"method,omitempty"` - Password string `json:"password,omitempty"` - Plugin string `json:"plugin,omitempty"` - PluginOptions string `json:"plugin_opts,omitempty"` - Network string `json:"network,omitempty"` -} - -func ParseShadowsocks(data proxy.Proxy, uuid string) (*Proxy, error) { - ss := data.Option.(proxy.Shadowsocks) - - password := uuid - // SIP022 AEAD-2022 Ciphers - if strings.Contains(ss.Method, "2022") { - serverKey, userKey := proxy.GenerateShadowsocks2022Password(ss, uuid) - password = fmt.Sprintf("%s:%s", serverKey, userKey) - } - - p := &Proxy{ - Tag: data.Name, - Type: Shadowsocks, - ShadowsocksOptions: &ShadowsocksOptions{ - ServerOptions: ServerOptions{ - Tag: data.Name, - Type: Shadowsocks, - Server: data.Server, - ServerPort: data.Port, - }, - Method: ss.Method, - Password: password, - Network: "tcp", - }, - } - return p, nil -} diff --git a/pkg/adapter/singbox/singbox.go b/pkg/adapter/singbox/singbox.go deleted file mode 100644 index 7957f48..0000000 --- a/pkg/adapter/singbox/singbox.go +++ /dev/null @@ -1,102 +0,0 @@ -package singbox - -import ( - "encoding/json" - "fmt" -) - -const ( - Trojan = "trojan" - VLESS = "vless" - VMess = "vmess" - TUIC = "tuic" - Hysteria2 = "hysteria2" - AnyTLS = "anytls" - Shadowsocks = "shadowsocks" - Selector = "selector" - URLTest = "urltest" - Direct = "direct" - Block = "block" - DNS = "dns" -) - -type Proxy struct { - Tag string `json:"tag,omitempty"` - Type string `json:"type"` - ShadowsocksOptions *ShadowsocksOptions `json:"-"` - TUICOptions *TUICOutboundOptions `json:"-"` - TrojanOptions *TrojanOutboundOptions `json:"-"` - VLESSOptions *VLESSOutboundOptions `json:"-"` - VMessOptions *VMessOutboundOptions `json:"-"` - AnyTLSOptions *AnyTLSOutboundOptions `json:"-"` - Hysteria2Options *Hysteria2OutboundOptions `json:"-"` - SelectorOptions *SelectorOutboundOptions `json:"-"` - URLTestOptions *URLTestOutboundOptions `json:"-"` -} - -type ServerOptions struct { - Tag string `json:"tag"` - Type string `json:"type"` - Server string `json:"server"` - ServerPort int `json:"server_port,omitempty"` -} -type OutboundOptions struct { - Tag string `json:"tag"` - Type string `json:"type"` -} -type SelectorOutboundOptions struct { - OutboundOptions - Outbounds []string `json:"outbounds"` - Default string `json:"default,omitempty"` - InterruptExistConnections bool `json:"interrupt_exist_connections,omitempty"` -} - -type URLTestOutboundOptions struct { - OutboundOptions - Outbounds []string `json:"outbounds"` - URL string `json:"url,omitempty"` - Interval Duration `json:"interval,omitempty"` - Tolerance uint16 `json:"tolerance,omitempty"` - IdleTimeout Duration `json:"idle_timeout,omitempty"` - InterruptExistConnections bool `json:"interrupt_exist_connections,omitempty"` -} - -type RouteOptions struct { - Rules []Rule `json:"rules,omitempty"` - Final string `json:"final,omitempty"` - RuleSet []RuleSet `json:"rule_set,omitempty"` - AutoDetectInterface bool `json:"auto_detect_interface,omitempty"` -} - -func (p Proxy) MarshalJSON() ([]byte, error) { - type Alias Proxy - aux := struct { - Alias - }{ - Alias: (Alias)(p), - } - switch p.Type { - case Shadowsocks: - return json.Marshal(p.ShadowsocksOptions) - case TUIC: - return json.Marshal(p.TUICOptions) - case Trojan: - return json.Marshal(p.TrojanOptions) - case VLESS: - return json.Marshal(p.VLESSOptions) - case VMess: - return json.Marshal(p.VMessOptions) - case Hysteria2: - return json.Marshal(p.Hysteria2Options) - case AnyTLS: - return json.Marshal(p.AnyTLSOptions) - case Selector: - return json.Marshal(p.SelectorOptions) - case URLTest: - return json.Marshal(p.URLTestOptions) - case Direct, Block, DNS: - return json.Marshal(aux.Alias) - default: - return nil, fmt.Errorf("[sing-box] MarshalJSON unknown type: %s", p.Type) - } -} diff --git a/pkg/adapter/singbox/singbox_test.go b/pkg/adapter/singbox/singbox_test.go deleted file mode 100644 index e93584e..0000000 --- a/pkg/adapter/singbox/singbox_test.go +++ /dev/null @@ -1,80 +0,0 @@ -package singbox - -import ( - "testing" - - "github.com/perfect-panel/server/pkg/adapter/proxy" - - "github.com/stretchr/testify/assert" -) - -func createSS() proxy.Proxy { - c := proxy.Shadowsocks{ - Method: "aes-256-gcm", - Port: 10301, - ServerKey: "", - } - return proxy.Proxy{ - Name: "Shadowsocks", - Server: "127.0.0.1", - Port: 10301, - Protocol: "shadowsocks", - Option: c, - } -} - -func createVLESS() proxy.Proxy { - c := proxy.Vless{ - Port: 10301, - Flow: "xtls-rprx-direct", - Transport: "websocket", - TransportConfig: proxy.TransportConfig{ - Path: "/ws", - Host: "baidu.com", - }, - Security: "tls", - SecurityConfig: proxy.SecurityConfig{ - SNI: "baidu.com", - Fingerprint: "chrome", - AllowInsecure: true, - }, - } - s := proxy.Proxy{ - Name: "VLESS", - Server: "test.xxx.com", - Port: 10301, - Protocol: "vless", - Option: c, - } - return s -} - -func TestSingboxShadowsocks(t *testing.T) { - s := createSS() - p, err := ParseShadowsocks(s, "uuid") - if err != nil { - t.Fatal(err) - } - data, err := p.MarshalJSON() - if err != nil { - t.Fatal(err) - } - assert.NotEqual(t, 0, len(data)) - - // Output: - // proxy: proxy: {"tag":"Shadowsocks","type":"shadowsocks","server":"127.0.0.1","server_port":10301,"method":"aes-256-gcm","password":"uuid","network":"tcp"} - -} - -func TestSingboxVless(t *testing.T) { - s := createVLESS() - p, err := ParseVless(s, "uuid") - if err != nil { - t.Fatal(err) - } - data, err := p.MarshalJSON() - if err != nil { - t.Fatal(err) - } - assert.NotEqual(t, 0, len(data)) -} diff --git a/pkg/adapter/singbox/tls.go b/pkg/adapter/singbox/tls.go deleted file mode 100644 index 84d1713..0000000 --- a/pkg/adapter/singbox/tls.go +++ /dev/null @@ -1,87 +0,0 @@ -package singbox - -import ( - "github.com/perfect-panel/server/pkg/adapter/proxy" -) - -type OutboundTLSOptions struct { - Enabled bool `json:"enabled,omitempty"` - DisableSNI bool `json:"disable_sni,omitempty"` - ServerName string `json:"server_name,omitempty"` - Insecure bool `json:"insecure,omitempty"` - ALPN Listable[string] `json:"alpn,omitempty"` - MinVersion string `json:"min_version,omitempty"` - MaxVersion string `json:"max_version,omitempty"` - CipherSuites Listable[string] `json:"cipher_suites,omitempty"` - Certificate Listable[string] `json:"certificate,omitempty"` - CertificatePath string `json:"certificate_path,omitempty"` - ECH *OutboundECHOptions `json:"ech,omitempty"` - UTLS *OutboundUTLSOptions `json:"utls,omitempty"` - Reality *OutboundRealityOptions `json:"reality,omitempty"` -} - -func NewOutboundTLSOptions(security string, cfg proxy.SecurityConfig) *OutboundTLSOptions { - var tls = &OutboundTLSOptions{} - switch security { - case "none": - return nil - case "tls": - tls.Enabled = true - if cfg.SNI != "" { - tls.ServerName = cfg.SNI - } else { - tls.DisableSNI = true - } - tls.Insecure = cfg.AllowInsecure - if cfg.Fingerprint != "" { - tls.UTLS = &OutboundUTLSOptions{ - Enabled: true, - Fingerprint: cfg.Fingerprint, - } - } - case "reality": - tls.Enabled = true - if cfg.SNI != "" { - tls.ServerName = cfg.SNI - } else { - tls.DisableSNI = true - } - tls.Insecure = cfg.AllowInsecure - if cfg.Fingerprint != "" { - tls.UTLS = &OutboundUTLSOptions{ - Enabled: true, - Fingerprint: cfg.Fingerprint, - } - } - tls.Reality = &OutboundRealityOptions{ - Enabled: true, - PublicKey: cfg.RealityPublicKey, - ShortID: cfg.RealityShortId, - } - } - return tls -} - -type OutboundECHOptions struct { - Enabled bool `json:"enabled,omitempty"` - PQSignatureSchemesEnabled bool `json:"pq_signature_schemes_enabled,omitempty"` - DynamicRecordSizingDisabled bool `json:"dynamic_record_sizing_disabled,omitempty"` - Config Listable[string] `json:"config,omitempty"` - ConfigPath string `json:"config_path,omitempty"` -} - -type OutboundRealityOptions struct { - Enabled bool `json:"enabled,omitempty"` - PublicKey string `json:"public_key,omitempty"` - ShortID string `json:"short_id,omitempty"` -} - -type OutboundUTLSOptions struct { - Enabled bool `json:"enabled,omitempty"` - Fingerprint string `json:"fingerprint,omitempty"` -} -type Listable[T any] []T - -type OutboundTLSOptionsContainer struct { - TLS *OutboundTLSOptions `json:"tls,omitempty"` -} diff --git a/pkg/adapter/singbox/tool.go b/pkg/adapter/singbox/tool.go deleted file mode 100644 index 535dbe8..0000000 --- a/pkg/adapter/singbox/tool.go +++ /dev/null @@ -1,11 +0,0 @@ -package singbox - -import "encoding/json" - -func mergeOptions(target map[string]any, options any) error { - optionsJSON, err := json.Marshal(options) - if err != nil { - return err - } - return json.Unmarshal(optionsJSON, &target) -} diff --git a/pkg/adapter/singbox/trojan.go b/pkg/adapter/singbox/trojan.go deleted file mode 100644 index 45eb756..0000000 --- a/pkg/adapter/singbox/trojan.go +++ /dev/null @@ -1,39 +0,0 @@ -package singbox - -import ( - "github.com/perfect-panel/server/pkg/adapter/proxy" -) - -type TrojanOutboundOptions struct { - ServerOptions - Password string `json:"password"` - Network string `json:"network,omitempty"` - OutboundTLSOptionsContainer - Multiplex *OutboundMultiplexOptions `json:"multiplex,omitempty"` - Transport *V2RayTransportOptions `json:"transport,omitempty"` -} - -func ParseTrojan(data proxy.Proxy, uuid string) (*Proxy, error) { - trojan := data.Option.(proxy.Trojan) - p := &Proxy{ - Tag: data.Name, - Type: Trojan, - TrojanOptions: &TrojanOutboundOptions{ - ServerOptions: ServerOptions{ - Tag: data.Name, - Type: Trojan, - Server: data.Server, - ServerPort: data.Port, - }, - Password: uuid, - }, - } - // Transport options - transport := NewV2RayTransportOptions(trojan.Transport, trojan.TransportConfig) - - p.TrojanOptions.Transport = transport - // Security options - p.TrojanOptions.TLS = NewOutboundTLSOptions(trojan.Security, trojan.SecurityConfig) - return p, nil - -} diff --git a/pkg/adapter/singbox/tuic.go b/pkg/adapter/singbox/tuic.go deleted file mode 100644 index 8267d40..0000000 --- a/pkg/adapter/singbox/tuic.go +++ /dev/null @@ -1,42 +0,0 @@ -package singbox - -import ( - "github.com/perfect-panel/server/pkg/adapter/proxy" -) - -type TUICOutboundOptions struct { - ServerOptions - UUID string `json:"uuid,omitempty"` - Password string `json:"password,omitempty"` - CongestionControl string `json:"congestion_control,omitempty"` - UDPRelayMode string `json:"udp_relay_mode,omitempty"` - UDPOverStream bool `json:"udp_over_stream,omitempty"` - ZeroRTTHandshake bool `json:"zero_rtt_handshake,omitempty"` - Heartbeat string `json:"heartbeat,omitempty"` - Network string `json:"network,omitempty"` - OutboundTLSOptionsContainer -} - -func ParseTUIC(data proxy.Proxy, uuid string) (*Proxy, error) { - tuic := data.Option.(proxy.Tuic) - p := &Proxy{ - Tag: data.Name, - Type: TUIC, - TUICOptions: &TUICOutboundOptions{ - ServerOptions: ServerOptions{ - Tag: data.Name, - Type: TUIC, - Server: data.Server, - ServerPort: data.Port, - }, - UUID: uuid, - Password: uuid, - CongestionControl: tuic.CongestionController, - UDPRelayMode: tuic.UDPRelayMode, - ZeroRTTHandshake: tuic.ReduceRtt, - }, - } - // Security options - p.TUICOptions.TLS = NewOutboundTLSOptions("tls", tuic.SecurityConfig) - return p, nil -} diff --git a/pkg/adapter/singbox/v2rayTransport.go b/pkg/adapter/singbox/v2rayTransport.go deleted file mode 100644 index 7d6b0a0..0000000 --- a/pkg/adapter/singbox/v2rayTransport.go +++ /dev/null @@ -1,114 +0,0 @@ -package singbox - -import ( - "encoding/json" - "time" - - "github.com/perfect-panel/server/pkg/adapter/proxy" -) - -type V2RayTransportOptions struct { - Type string `json:"type"` - HTTPOptions V2RayHTTPOptions `json:"-"` - WebsocketOptions V2RayWebsocketOptions `json:"-"` - QUICOptions V2RayQUICOptions `json:"-"` - GRPCOptions V2RayGRPCOptions `json:"-"` - HTTPUpgradeOptions V2RayHTTPUpgradeOptions `json:"-"` -} - -func (v V2RayTransportOptions) MarshalJSON() ([]byte, error) { - var v2rayTransportOptions any - data := map[string]any{ - "type": v.Type, - } - switch v.Type { - case "http": - v2rayTransportOptions = v.HTTPOptions - case "ws": - v2rayTransportOptions = v.WebsocketOptions - case "quic": - v2rayTransportOptions = v.QUICOptions - case "grpc": - v2rayTransportOptions = v.GRPCOptions - case "httpupgrade": - v2rayTransportOptions = v.HTTPUpgradeOptions - } - if err := mergeOptions(data, v2rayTransportOptions); err != nil { - return nil, err - } - return json.Marshal(data) -} - -func NewV2RayTransportOptions(network string, transport proxy.TransportConfig) *V2RayTransportOptions { - var t *V2RayTransportOptions = nil - switch network { - case "websocket": - t = &V2RayTransportOptions{ - Type: "ws", - WebsocketOptions: V2RayWebsocketOptions{ - Path: transport.Path, - Headers: map[string]Listable[string]{ - "Host": []string{transport.Host}, - }, - MaxEarlyData: 2048, - EarlyDataHeaderName: "Sec-WebSocket-Protocol", - }, - } - case "httpupgrade": - t = &V2RayTransportOptions{ - Type: "httpupgrade", - HTTPOptions: V2RayHTTPOptions{ - Path: transport.Path, - Host: []string{transport.Host}, - Headers: map[string]Listable[string]{ - "Host": []string{transport.Host}, - }, - }, - } - - case "grpc": - t = &V2RayTransportOptions{ - Type: "grpc", - GRPCOptions: V2RayGRPCOptions{ - ServiceName: transport.ServiceName, - }, - } - } - return t -} - -type V2RayHTTPOptions struct { - Host Listable[string] `json:"host,omitempty"` - Path string `json:"path,omitempty"` - Method string `json:"method,omitempty"` - Headers HTTPHeader `json:"headers,omitempty"` - IdleTimeout Duration `json:"idle_timeout,omitempty"` - PingTimeout Duration `json:"ping_timeout,omitempty"` -} - -type V2RayWebsocketOptions struct { - Path string `json:"path,omitempty"` - Headers HTTPHeader `json:"headers,omitempty"` - MaxEarlyData uint32 `json:"max_early_data,omitempty"` - EarlyDataHeaderName string `json:"early_data_header_name,omitempty"` -} - -type V2RayQUICOptions struct{} - -type V2RayGRPCOptions struct { - ServiceName string `json:"service_name,omitempty"` - IdleTimeout string `json:"idle_timeout,omitempty"` - PingTimeout string `json:"ping_timeout,omitempty"` - PermitWithoutStream bool `json:"permit_without_stream,omitempty"` - ForceLite bool `json:"-"` // for test -} - -type V2RayHTTPUpgradeOptions struct { - Host string `json:"host,omitempty"` - Path string `json:"path,omitempty"` - Headers HTTPHeader `json:"headers,omitempty"` -} - -type HTTPHeader map[string]Listable[string] - -type Duration time.Duration diff --git a/pkg/adapter/singbox/vless.go b/pkg/adapter/singbox/vless.go deleted file mode 100644 index 6370767..0000000 --- a/pkg/adapter/singbox/vless.go +++ /dev/null @@ -1,44 +0,0 @@ -package singbox - -import ( - "github.com/perfect-panel/server/pkg/adapter/proxy" -) - -type VLESSOutboundOptions struct { - ServerOptions - OutboundTLSOptionsContainer - UUID string `json:"uuid"` - Flow string `json:"flow,omitempty"` - Network string `json:"network,omitempty"` - Multiplex *OutboundMultiplexOptions `json:"multiplex,omitempty"` - Transport *V2RayTransportOptions `json:"transport,omitempty"` - PacketEncoding *string `json:"packet_encoding,omitempty"` -} - -func ParseVless(data proxy.Proxy, uuid string) (*Proxy, error) { - vless := data.Option.(proxy.Vless) - packetEncoding := "xudp" - p := &Proxy{ - Tag: data.Name, - Type: VLESS, - VLESSOptions: &VLESSOutboundOptions{ - ServerOptions: ServerOptions{ - Tag: data.Name, - Type: VLESS, - Server: data.Server, - ServerPort: data.Port, - }, - UUID: uuid, - Flow: vless.Flow, - PacketEncoding: &packetEncoding, - }, - } - // Transport options - transport := NewV2RayTransportOptions(vless.Transport, vless.TransportConfig) - p.VLESSOptions.Transport = transport - - // Security options - p.VLESSOptions.TLS = NewOutboundTLSOptions(vless.Security, vless.SecurityConfig) - - return p, nil -} diff --git a/pkg/adapter/singbox/vmess.go b/pkg/adapter/singbox/vmess.go deleted file mode 100644 index c9f316b..0000000 --- a/pkg/adapter/singbox/vmess.go +++ /dev/null @@ -1,43 +0,0 @@ -package singbox - -import ( - "github.com/perfect-panel/server/pkg/adapter/proxy" -) - -type VMessOutboundOptions struct { - ServerOptions - UUID string `json:"uuid"` - Security string `json:"security"` - AlterId int `json:"alter_id,omitempty"` - GlobalPadding bool `json:"global_padding,omitempty"` - AuthenticatedLength bool `json:"authenticated_length,omitempty"` - Network string `json:"network,omitempty"` - PacketEncoding string `json:"packet_encoding,omitempty"` - Multiplex *OutboundMultiplexOptions `json:"multiplex,omitempty"` - Transport *V2RayTransportOptions `json:"transport,omitempty"` - OutboundTLSOptionsContainer -} - -func ParseVMess(data proxy.Proxy, uuid string) (*Proxy, error) { - vmess := data.Option.(proxy.Vmess) - p := &Proxy{ - Type: VMess, - VMessOptions: &VMessOutboundOptions{ - ServerOptions: ServerOptions{ - Tag: data.Name, - Type: VMess, - Server: data.Server, - ServerPort: data.Port, - }, - UUID: uuid, - Security: "auto", - AlterId: 0, - }, - } - // Transport options - p.VMessOptions.Transport = NewV2RayTransportOptions(vmess.Transport, vmess.TransportConfig) - // Security options - p.VMessOptions.TLS = NewOutboundTLSOptions(vmess.Security, vmess.SecurityConfig) - - return p, nil -} diff --git a/pkg/adapter/surfboard/build.go b/pkg/adapter/surfboard/build.go deleted file mode 100644 index a481755..0000000 --- a/pkg/adapter/surfboard/build.go +++ /dev/null @@ -1,80 +0,0 @@ -package surfboard - -import ( - "bytes" - "embed" - "fmt" - "strings" - "text/template" - "time" - - "github.com/perfect-panel/server/pkg/adapter/proxy" - - "github.com/perfect-panel/server/pkg/logger" - "github.com/perfect-panel/server/pkg/tool" - "github.com/perfect-panel/server/pkg/traffic" -) - -//go:embed *.tpl -var configFiles embed.FS -var shadowsocksSupportMethod = []string{"aes-128-gcm", "aes-192-gcm", "aes-256-gcm", "chacha20-ietf-poly1305"} - -func BuildSurfboard(servers proxy.Adapter, siteName string, user UserInfo) []byte { - var proxies, proxyGroup string - var removed []string - var ps []string - - for _, p := range servers.Proxies { - switch p.Protocol { - case "shadowsocks": - proxies += buildShadowsocks(p, user.UUID) - case "trojan": - proxies += buildTrojan(p, user.UUID) - case "vmess": - proxies += buildVMess(p, user.UUID) - default: - removed = append(removed, p.Name) - } - ps = append(ps, p.Name) - } - - file, err := configFiles.ReadFile("default.tpl") - if err != nil { - logger.Errorf("read default surfboard config error: %v", err.Error()) - return nil - } - // replace template - tpl, err := template.New("default").Parse(string(file)) - if err != nil { - logger.Errorf("read default surfboard config error: %v", err.Error()) - return nil - } - var buf bytes.Buffer - - var expiredAt string - if user.ExpiredDate.Before(time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC)) { - expiredAt = "长期有效" - } else { - expiredAt = user.ExpiredDate.Format("2006-01-02 15:04:05") - } - - ps = tool.RemoveStringElement(ps, removed...) - proxyGroup = strings.Join(ps, ",") - - // convert traffic - upload := traffic.AutoConvert(user.Upload, false) - download := traffic.AutoConvert(user.Download, false) - total := traffic.AutoConvert(user.TotalTraffic, false) - unusedTraffic := traffic.AutoConvert(user.TotalTraffic-user.Upload-user.Download, false) - // query Host - if err = tpl.Execute(&buf, map[string]interface{}{ - "Proxies": proxies, - "ProxyGroup": proxyGroup, - "SubscribeURL": user.SubscribeURL, - "SubscribeInfo": fmt.Sprintf("title=%s订阅信息, content=上传流量:%s\\n下载流量:%s\\n剩余流量: %s\\n套餐流量:%s\\n到期时间:%s", siteName, upload, download, unusedTraffic, total, expiredAt), - }); err != nil { - logger.Errorf("build Surge config error: %v", err.Error()) - return nil - } - return buf.Bytes() -} diff --git a/pkg/adapter/surfboard/build_test.go b/pkg/adapter/surfboard/build_test.go deleted file mode 100644 index 92bc086..0000000 --- a/pkg/adapter/surfboard/build_test.go +++ /dev/null @@ -1,24 +0,0 @@ -package surfboard - -import ( - "testing" - "time" - - "github.com/perfect-panel/server/pkg/adapter/proxy" - - "github.com/perfect-panel/server/pkg/uuidx" -) - -func TestBuildSurfboard(t *testing.T) { - siteName := "test" - user := UserInfo{ - UUID: uuidx.NewUUID().String(), - Upload: 0, - Download: 0, - TotalTraffic: 0, - ExpiredDate: time.Now().AddDate(0, 1, 1), - SubscribeURL: "https://test.com", - } - conf := BuildSurfboard(proxy.Adapter{}, siteName, user) - t.Log(string(conf)) -} diff --git a/pkg/adapter/surfboard/default.tpl b/pkg/adapter/surfboard/default.tpl deleted file mode 100644 index 89c48fe..0000000 --- a/pkg/adapter/surfboard/default.tpl +++ /dev/null @@ -1,62 +0,0 @@ -#!MANAGED-CONFIG {{.SubscribeURL}} interval=43200 strict=true - -[General] -dns-server = system, 119.29.29.29, 223.5.5.5 -skip-proxy = 192.168.0.0/16, 10.0.0.0/8, 172.16.0.0/12, 127.0.0.0/8, localhost, *.local -always-real-ip = *.lan, lens.l.google.com, *.srv.nintendo.net, *.stun.playstation.net, *.xboxlive.com, xbox.*.*.microsoft.com, *.msftncsi.com, *.msftconnecttest.com -proxy-test-url = http://www.gstatic.com/generate_204 -internet-test-url = http://connectivitycheck.platform.hicloud.com/generate_204 -test-timeout = 5 -http-listen = 0.0.0.0:6088 -socks5-listen = 0.0.0.0:6089 - -[Panel] -SubscribeInfo = {{.SubscribeInfo}}, style=info - -[Proxy] -{{.Proxies}} - -[Proxy Group] -🚀 Proxy = select, 🌏 Auto, 🎯 Direct, include-other-group=🇺🇳 Nodes -🍎 Apple = select, 🚀 Proxy, 🎯 Direct, include-other-group=🇺🇳 Nodes -🔍 Google = select, 🚀 Proxy, 🎯 Direct, include-other-group=🇺🇳 Nodes -🪟 Microsoft = select, 🚀 Proxy, 🎯 Direct, include-other-group=🇺🇳 Nodes -📺 GlobalMedia = select, 🚀 Proxy, 🎯 Direct, include-other-group=🇺🇳 Nodes -🤖 AI = select, 🚀 Proxy, 🎯 Direct, include-other-group=🇺🇳 Nodes -🪙 Crypto = select, 🚀 Proxy, 🎯 Direct, include-other-group=🇺🇳 Nodes -🎮 Game = select, 🚀 Proxy, 🎯 Direct, include-other-group=🇺🇳 Nodes -📟 Telegram = select, 🚀 Proxy, 🎯 Direct, include-other-group=🇺🇳 Nodes -🇨🇳 China = select, 🎯 Direct, 🚀 Proxy, include-other-group=🇺🇳 Nodes -🐠 Final = select, 🎯 Direct, 🚀 Proxy, include-other-group=🇺🇳 Nodes -🌏 Auto = fallback, include-other-group=🇺🇳 Nodes, url=http://www.gstatic.com/generate_204, interval=600, timeout=5 -🎯 Direct = select, DIRECT, hidden=1 -🇺🇳 Nodes = select, {{.ProxyGroup}}, hidden=1 - -[Rule] -RULE-SET, https://raw.githubusercontent.com/blackmatrix7/ios_rule_script/refs/heads/master/rule/Surge/Apple/Apple_All.list, 🍎 Apple -RULE-SET, https://raw.githubusercontent.com/blackmatrix7/ios_rule_script/refs/heads/master/rule/Surge/Google/Google.list, 🔍 Google -RULE-SET, https://raw.githubusercontent.com/blackmatrix7/ios_rule_script/refs/heads/master/rule/Surge/GitHub/GitHub.list, 🪟 Microsoft -RULE-SET, https://raw.githubusercontent.com/blackmatrix7/ios_rule_script/refs/heads/master/rule/Surge/Microsoft/Microsoft.list, 🪟 Microsoft -RULE-SET, https://raw.githubusercontent.com/blackmatrix7/ios_rule_script/refs/heads/master/rule/Surge/HBO/HBO.list, 📺 GlobalMedia -RULE-SET, https://raw.githubusercontent.com/blackmatrix7/ios_rule_script/refs/heads/master/rule/Surge/Disney/Disney.list, 📺 GlobalMedia -RULE-SET, https://raw.githubusercontent.com/blackmatrix7/ios_rule_script/refs/heads/master/rule/Surge/TikTok/TikTok.list, 📺 GlobalMedia -RULE-SET, https://raw.githubusercontent.com/blackmatrix7/ios_rule_script/refs/heads/master/rule/Surge/Netflix/Netflix.list, 📺 GlobalMedia -RULE-SET, https://raw.githubusercontent.com/blackmatrix7/ios_rule_script/refs/heads/master/rule/Surge/GlobalMedia/GlobalMedia_All_No_Resolve.list, 📺 GlobalMedia -RULE-SET, https://raw.githubusercontent.com/blackmatrix7/ios_rule_script/refs/heads/master/rule/Surge/Telegram/Telegram.list, 📟 Telegram -RULE-SET, https://raw.githubusercontent.com/blackmatrix7/ios_rule_script/refs/heads/master/rule/Surge/OpenAI/OpenAI.list, 🤖 AI -RULE-SET, https://raw.githubusercontent.com/blackmatrix7/ios_rule_script/refs/heads/master/rule/Surge/Gemini/Gemini.list, 🤖 AI -RULE-SET, https://raw.githubusercontent.com/blackmatrix7/ios_rule_script/refs/heads/master/rule/Surge/Copilot/Copilot.list, 🤖 AI -RULE-SET, https://raw.githubusercontent.com/blackmatrix7/ios_rule_script/refs/heads/master/rule/Surge/Claude/Claude.list, 🤖 AI -RULE-SET, https://raw.githubusercontent.com/blackmatrix7/ios_rule_script/refs/heads/master/rule/Surge/Crypto/Crypto.list, 🪙 Crypto -RULE-SET, https://raw.githubusercontent.com/blackmatrix7/ios_rule_script/refs/heads/master/rule/Surge/Cryptocurrency/Cryptocurrency.list, 🪙 Crypto -RULE-SET, https://raw.githubusercontent.com/blackmatrix7/ios_rule_script/refs/heads/master/rule/Surge/Game/Game.list, 🎮 Game -RULE-SET, https://raw.githubusercontent.com/blackmatrix7/ios_rule_script/refs/heads/master/rule/Surge/Global/Global_All_No_Resolve.list, 🚀 Proxy -RULE-SET, https://raw.githubusercontent.com/blackmatrix7/ios_rule_script/refs/heads/master/rule/Surge/ChinaMax/ChinaMax_All_No_Resolve.list, 🇨🇳 China -RULE-SET, https://raw.githubusercontent.com/blackmatrix7/ios_rule_script/refs/heads/master/rule/Surge/Lan/Lan.list, 🎯 Direct - -GEOIP, CN, 🇨🇳 China -FINAL, 🐠 Final, dns-failed - -[URL Rewrite] -^https?:\/\/(www.)?g\.cn https://www.google.com 302 -^https?:\/\/(www.)?google\.cn https://www.google.com 302 \ No newline at end of file diff --git a/pkg/adapter/surfboard/model.go b/pkg/adapter/surfboard/model.go deleted file mode 100644 index 29d53ba..0000000 --- a/pkg/adapter/surfboard/model.go +++ /dev/null @@ -1,12 +0,0 @@ -package surfboard - -import "time" - -type UserInfo struct { - UUID string - Upload int64 - Download int64 - TotalTraffic int64 - ExpiredDate time.Time - SubscribeURL string -} diff --git a/pkg/adapter/surfboard/shadowsocks.go b/pkg/adapter/surfboard/shadowsocks.go deleted file mode 100644 index 41f4808..0000000 --- a/pkg/adapter/surfboard/shadowsocks.go +++ /dev/null @@ -1,28 +0,0 @@ -package surfboard - -import ( - "fmt" - "strings" - - "github.com/perfect-panel/server/pkg/adapter/proxy" -) - -func buildShadowsocks(data proxy.Proxy, uuid string) string { - ss, ok := data.Option.(proxy.Shadowsocks) - if !ok { - return "" - } - // Not supporting SIP022 AEAD-2022 Ciphers - if strings.Contains(ss.Method, "2022") { - return "" - } - addr := fmt.Sprintf("%s=ss, %s, %d", data.Name, data.Server, data.Port) - config := []string{ - addr, - fmt.Sprintf("encrypt-method=%s", ss.Method), - fmt.Sprintf("password=%s", uuid), - "tfo=true", - "udp-relay=true", - } - return strings.Join(config, ",") + "\r\n" -} diff --git a/pkg/adapter/surfboard/shadowsocks_test.go b/pkg/adapter/surfboard/shadowsocks_test.go deleted file mode 100644 index 924bfd0..0000000 --- a/pkg/adapter/surfboard/shadowsocks_test.go +++ /dev/null @@ -1,28 +0,0 @@ -package surfboard - -import ( - "testing" - - "github.com/perfect-panel/server/pkg/adapter/proxy" -) - -func createSS() proxy.Proxy { - return proxy.Proxy{ - Name: "Shadowsocks", - Server: "test.xxxx.com", - Port: 10301, - Protocol: "shadowsocks", - Option: proxy.Shadowsocks{ - Port: 10301, - Method: "aes-256-gcm", - ServerKey: "123456", - }, - } -} - -func TestShadowsocks(t *testing.T) { - node := createSS() - uuid := "123456" - shadowsocks := buildShadowsocks(node, uuid) - t.Log(shadowsocks) -} diff --git a/pkg/adapter/surfboard/trojan.go b/pkg/adapter/surfboard/trojan.go deleted file mode 100644 index a0bbc88..0000000 --- a/pkg/adapter/surfboard/trojan.go +++ /dev/null @@ -1,41 +0,0 @@ -package surfboard - -import ( - "strconv" - "strings" - - "github.com/perfect-panel/server/pkg/adapter/proxy" -) - -func buildTrojan(data proxy.Proxy, uuid string) string { - // $config = [ - // "{$server['name']}=trojan", - // "{$server['host']}", - // "{$server['port']}", - // "password={$password}", - // $protocol_settings['server_name'] ? "sni={$protocol_settings['server_name']}" : "", - // 'tfo=true', - // 'udp-relay=true' - //]; - trojan, ok := data.Option.(proxy.Trojan) - if !ok { - return "" - } - config := []string{ - data.Name + "=trojan", - data.Server, - strconv.Itoa(data.Port), - "password=" + uuid, - "tfo=true", - "udp-relay=true", - } - if trojan.SecurityConfig.SNI != "" { - config = append(config, "sni="+trojan.SecurityConfig.SNI) - } - if trojan.SecurityConfig.AllowInsecure { - config = append(config, "skip-cert-verify=true") - } else { - config = append(config, "skip-cert-verify=false") - } - return strings.Join(config, ",") + "\r\n" -} diff --git a/pkg/adapter/surfboard/trojan_test.go b/pkg/adapter/surfboard/trojan_test.go deleted file mode 100644 index 728910f..0000000 --- a/pkg/adapter/surfboard/trojan_test.go +++ /dev/null @@ -1,36 +0,0 @@ -package surfboard - -import ( - "testing" - - "github.com/perfect-panel/server/pkg/adapter/proxy" -) - -func createTrojan() proxy.Proxy { - - return proxy.Proxy{ - Name: "Trojan", - Server: "test.xxxx.com", - Port: 13002, - Protocol: "trojan", - Option: proxy.Trojan{ - Port: 13002, - Transport: "websocket", - TransportConfig: proxy.TransportConfig{ - Path: "/ws", - Host: "baidu.com", - }, - SecurityConfig: proxy.SecurityConfig{ - SNI: "baidu.com", - AllowInsecure: true, - }, - }, - } -} - -func TestTrojan(t *testing.T) { - node := createTrojan() - uuid := "123456" - trojan := buildTrojan(node, uuid) - t.Log(trojan) -} diff --git a/pkg/adapter/surfboard/vmess.go b/pkg/adapter/surfboard/vmess.go deleted file mode 100644 index 931afb1..0000000 --- a/pkg/adapter/surfboard/vmess.go +++ /dev/null @@ -1,45 +0,0 @@ -package surfboard - -import ( - "fmt" - "strings" - - "github.com/perfect-panel/server/pkg/adapter/proxy" -) - -func buildVMess(data proxy.Proxy, uuid string) string { - vmess, ok := data.Option.(proxy.Vmess) - if !ok { - return "" - } - addr := fmt.Sprintf("%s=vmess, %s, %d", data.Name, data.Server, data.Port) - uriConfig := []string{ - addr, - fmt.Sprintf("username=%s", uuid), - "vmess-aead=true", - "tfo=true", - "udp-relay=true", - } - if vmess.Security == "tls" { - uriConfig = append(uriConfig, "tls=true") - if vmess.SecurityConfig.AllowInsecure { - uriConfig = append(uriConfig, "skip-cert-verify=true") - } else { - uriConfig = append(uriConfig, "skip-cert-verify=false") - } - if vmess.SecurityConfig.SNI != "" { - uriConfig = append(uriConfig, fmt.Sprintf("sni=%s", vmess.SecurityConfig.SNI)) - } - } - if vmess.Transport == "websocket" { - uriConfig = append(uriConfig, "ws=true") - if vmess.TransportConfig.Path != "" { - uriConfig = append(uriConfig, fmt.Sprintf("ws-path=%s", vmess.TransportConfig.Path)) - } - if vmess.TransportConfig.Host != "" { - uriConfig = append(uriConfig, fmt.Sprintf("ws-headers=Host:%s", vmess.TransportConfig.Host)) - } - } - - return strings.Join(uriConfig, ",") + "\r\n" -} diff --git a/pkg/adapter/surfboard/vmess_test.go b/pkg/adapter/surfboard/vmess_test.go deleted file mode 100644 index 1feb773..0000000 --- a/pkg/adapter/surfboard/vmess_test.go +++ /dev/null @@ -1,33 +0,0 @@ -package surfboard - -import ( - "testing" - - "github.com/perfect-panel/server/pkg/adapter/proxy" -) - -func createVMess() proxy.Proxy { - - return proxy.Proxy{ - Name: "Vmess", - Server: "test.xxxx.com", - Port: 13002, - Protocol: "vmess", - Option: proxy.Vmess{ - Port: 13002, - Transport: "websocket", - TransportConfig: proxy.TransportConfig{ - Path: "/ws", - Host: "test.xx.com", - }, - Security: "none", - }, - } -} - -func TestVMess(t *testing.T) { - node := createVMess() - uuid := "123456" - p := buildVMess(node, uuid) - t.Log(p) -} diff --git a/pkg/adapter/surge/default.tpl b/pkg/adapter/surge/default.tpl deleted file mode 100644 index 6b70844..0000000 --- a/pkg/adapter/surge/default.tpl +++ /dev/null @@ -1,79 +0,0 @@ -#!MANAGED-CONFIG {{.SubscribeURL}} interval=43200 strict=true - -[General] -loglevel = notify -external-controller-access = purinio@0.0.0.0:6170 -exclude-simple-hostnames = true -show-error-page-for-reject = true -udp-priority = true -udp-policy-not-supported-behaviour = reject -ipv6 = true -ipv6-vif = auto -proxy-test-url = http://www.gstatic.com/generate_204 -internet-test-url = http://connectivitycheck.platform.hicloud.com/generate_204 -test-timeout = 5 -dns-server = system, 119.29.29.29, 223.5.5.5 -hijack-dns = 8.8.8.8:53, 8.8.4.4:53, 1.1.1.1:53, 1.0.0.1:53 -skip-proxy = 192.168.0.0/16, 10.0.0.0/8, 172.16.0.0/12, 127.0.0.0/8, localhost, *.local -always-real-ip = *.lan, lens.l.google.com, *.srv.nintendo.net, *.stun.playstation.net, *.xboxlive.com, xbox.*.*.microsoft.com, *.msftncsi.com, *.msftconnecttest.com - -# > Surge Mac Parameters -http-listen = 0.0.0.0:6088 -socks5-listen = 0.0.0.0:6089 - -# > Surge iOS Parameters -allow-wifi-access = true -allow-hotspot-access = true -wifi-access-http-port = 6088 -wifi-access-socks5-port = 6089 - -[Panel] -SubscribeInfo = {{.SubscribeInfo}}, style=info - -[Proxy] -{{.Proxies}} - -[Proxy Group] -🚀 Proxy = select, 🌏 Auto, 🎯 Direct, include-other-group=🇺🇳 Nodes -🍎 Apple = select, 🚀 Proxy, 🎯 Direct, include-other-group=🇺🇳 Nodes -🔍 Google = select, 🚀 Proxy, 🎯 Direct, include-other-group=🇺🇳 Nodes -🪟 Microsoft = select, 🚀 Proxy, 🎯 Direct, include-other-group=🇺🇳 Nodes -📺 GlobalMedia = select, 🚀 Proxy, 🎯 Direct, include-other-group=🇺🇳 Nodes -🤖 AI = select, 🚀 Proxy, 🎯 Direct, include-other-group=🇺🇳 Nodes -🪙 Crypto = select, 🚀 Proxy, 🎯 Direct, include-other-group=🇺🇳 Nodes -🎮 Game = select, 🚀 Proxy, 🎯 Direct, include-other-group=🇺🇳 Nodes -📟 Telegram = select, 🚀 Proxy, 🎯 Direct, include-other-group=🇺🇳 Nodes -🇨🇳 China = select, 🎯 Direct, 🚀 Proxy, include-other-group=🇺🇳 Nodes -🐠 Final = select, 🚀 Proxy, 🎯 Direct, include-other-group=🇺🇳 Nodes -🌏 Auto = smart, include-other-group=🇺🇳 Nodes -🎯 Direct = select, DIRECT, hidden=1 -🇺🇳 Nodes = select, {{.ProxyGroup}}, hidden=1 - -[Rule] -RULE-SET, https://raw.githubusercontent.com/blackmatrix7/ios_rule_script/refs/heads/master/rule/Surge/Apple/Apple_All.list, 🍎 Apple -RULE-SET, https://raw.githubusercontent.com/blackmatrix7/ios_rule_script/refs/heads/master/rule/Surge/Google/Google.list, 🔍 Google -RULE-SET, https://raw.githubusercontent.com/blackmatrix7/ios_rule_script/refs/heads/master/rule/Surge/GitHub/GitHub.list, 🪟 Microsoft -RULE-SET, https://raw.githubusercontent.com/blackmatrix7/ios_rule_script/refs/heads/master/rule/Surge/Microsoft/Microsoft.list, 🪟 Microsoft -RULE-SET, https://raw.githubusercontent.com/blackmatrix7/ios_rule_script/refs/heads/master/rule/Surge/HBO/HBO.list, 📺 GlobalMedia -RULE-SET, https://raw.githubusercontent.com/blackmatrix7/ios_rule_script/refs/heads/master/rule/Surge/Disney/Disney.list, 📺 GlobalMedia -RULE-SET, https://raw.githubusercontent.com/blackmatrix7/ios_rule_script/refs/heads/master/rule/Surge/TikTok/TikTok.list, 📺 GlobalMedia -RULE-SET, https://raw.githubusercontent.com/blackmatrix7/ios_rule_script/refs/heads/master/rule/Surge/Netflix/Netflix.list, 📺 GlobalMedia -RULE-SET, https://raw.githubusercontent.com/blackmatrix7/ios_rule_script/refs/heads/master/rule/Surge/GlobalMedia/GlobalMedia_All_No_Resolve.list, 📺 GlobalMedia -RULE-SET, https://raw.githubusercontent.com/blackmatrix7/ios_rule_script/refs/heads/master/rule/Surge/Telegram/Telegram.list, 📟 Telegram -RULE-SET, https://raw.githubusercontent.com/blackmatrix7/ios_rule_script/refs/heads/master/rule/Surge/OpenAI/OpenAI.list, 🤖 AI -RULE-SET, https://raw.githubusercontent.com/blackmatrix7/ios_rule_script/refs/heads/master/rule/Surge/Gemini/Gemini.list, 🤖 AI -RULE-SET, https://raw.githubusercontent.com/blackmatrix7/ios_rule_script/refs/heads/master/rule/Surge/Copilot/Copilot.list, 🤖 AI -RULE-SET, https://raw.githubusercontent.com/blackmatrix7/ios_rule_script/refs/heads/master/rule/Surge/Claude/Claude.list, 🤖 AI -RULE-SET, https://raw.githubusercontent.com/blackmatrix7/ios_rule_script/refs/heads/master/rule/Surge/Crypto/Crypto.list, 🪙 Crypto -RULE-SET, https://raw.githubusercontent.com/blackmatrix7/ios_rule_script/refs/heads/master/rule/Surge/Cryptocurrency/Cryptocurrency.list, 🪙 Crypto -RULE-SET, https://raw.githubusercontent.com/blackmatrix7/ios_rule_script/refs/heads/master/rule/Surge/Game/Game.list, 🎮 Game -RULE-SET, https://raw.githubusercontent.com/blackmatrix7/ios_rule_script/refs/heads/master/rule/Surge/Global/Global_All_No_Resolve.list, 🚀 Proxy -RULE-SET, https://raw.githubusercontent.com/blackmatrix7/ios_rule_script/refs/heads/master/rule/Surge/ChinaMax/ChinaMax_All_No_Resolve.list, 🇨🇳 China -RULE-SET, https://raw.githubusercontent.com/blackmatrix7/ios_rule_script/refs/heads/master/rule/Surge/Lan/Lan.list, 🎯 Direct - -GEOIP, CN, 🇨🇳 China -FINAL, 🐠 Final, dns-failed - -[URL Rewrite] -^https?:\/\/(www.)?g\.cn https://www.google.com 302 -^https?:\/\/(www.)?google\.cn https://www.google.com 302 \ No newline at end of file diff --git a/pkg/adapter/surge/hysteria2.go b/pkg/adapter/surge/hysteria2.go deleted file mode 100644 index 778d395..0000000 --- a/pkg/adapter/surge/hysteria2.go +++ /dev/null @@ -1,43 +0,0 @@ -package surge - -import ( - "fmt" - "strconv" - "strings" - - "github.com/perfect-panel/server/pkg/adapter/proxy" -) - -func buildHysteria2(data proxy.Proxy, uuid string) string { - hysteria2, ok := data.Option.(proxy.Hysteria2) - if !ok { - return "" - } - - var port int - if hysteria2.HopPorts != "" { - ports := strings.Split(hysteria2.HopPorts, ",") - p := ports[0] - if len(strings.Split(p, "-")) > 1 { - p = strings.Split(p, "-")[0] - } - port, _ = strconv.Atoi(p) - } else { - port = data.Port - } - - config := []string{ - fmt.Sprintf("%s=hysteria2,%s,%d", data.Name, data.Server, port), - "password=" + uuid, - "udp-relay=true", - } - if hysteria2.SecurityConfig.SNI != "" { - config = append(config, "sni="+hysteria2.SecurityConfig.SNI) - } - if hysteria2.SecurityConfig.AllowInsecure { - config = append(config, "skip-cert-verify=true") - } else { - config = append(config, "skip-cert-verify=false") - } - return strings.Join(config, ",") + "\r\n" -} diff --git a/pkg/adapter/surge/hysteria2_test.go b/pkg/adapter/surge/hysteria2_test.go deleted file mode 100644 index 6e0803b..0000000 --- a/pkg/adapter/surge/hysteria2_test.go +++ /dev/null @@ -1,70 +0,0 @@ -package surge - -import ( - "testing" - - "github.com/perfect-panel/server/pkg/adapter/proxy" -) - -func TestBuildHysteria2(t *testing.T) { - tests := []struct { - name string - data proxy.Proxy - uuid string - expected string - }{ - { - name: "Valid Hysteria2 with HopPorts", - data: proxy.Proxy{ - Name: "test", - Server: "server.com", - Port: 443, - Option: proxy.Hysteria2{ - HopPorts: "1000-2000", - SecurityConfig: proxy.SecurityConfig{ - SNI: "example.com", - AllowInsecure: true, - }, - }, - }, - uuid: "test-uuid", - expected: "test=hysteria2,server.com,1000,password=test-uuid,udp-relay=true,sni=example.com,skip-cert-verify=true\r\n", - }, - { - name: "Valid Hysteria2 without HopPorts", - data: proxy.Proxy{ - Name: "test", - Server: "server.com", - Port: 443, - Option: proxy.Hysteria2{ - SecurityConfig: proxy.SecurityConfig{ - SNI: "example.com", - AllowInsecure: false, - }, - }, - }, - uuid: "test-uuid", - expected: "test=hysteria2,server.com,443,password=test-uuid,udp-relay=true,sni=example.com,skip-cert-verify=false\r\n", - }, - { - name: "Invalid Hysteria2 Option", - data: proxy.Proxy{ - Name: "test", - Server: "server.com", - Port: 443, - Option: nil, - }, - uuid: "test-uuid", - expected: "", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - result := buildHysteria2(tt.data, tt.uuid) - if result != tt.expected { - t.Errorf("expected %s, got %s", tt.expected, result) - } - }) - } -} diff --git a/pkg/adapter/surge/shadowsocks.go b/pkg/adapter/surge/shadowsocks.go deleted file mode 100644 index bab18be..0000000 --- a/pkg/adapter/surge/shadowsocks.go +++ /dev/null @@ -1,32 +0,0 @@ -package surge - -import ( - "fmt" - "strings" - - "github.com/perfect-panel/server/pkg/adapter/proxy" -) - -func buildShadowsocks(data proxy.Proxy, uuid string) string { - ss, ok := data.Option.(proxy.Shadowsocks) - if !ok { - return "" - } - - password := uuid - // SIP022 AEAD-2022 Ciphers - if strings.Contains(ss.Method, "2022") { - serverKey, userKey := proxy.GenerateShadowsocks2022Password(ss, uuid) - password = fmt.Sprintf("%s:%s", serverKey, userKey) - } - - addr := fmt.Sprintf("%s=ss, %s, %d", data.Name, data.Server, data.Port) - config := []string{ - addr, - fmt.Sprintf("encrypt-method=%s", ss.Method), - fmt.Sprintf("password=%s", password), - "tfo=true", - "udp-relay=true", - } - return strings.Join(config, ",") + "\r\n" -} diff --git a/pkg/adapter/surge/surge.go b/pkg/adapter/surge/surge.go deleted file mode 100644 index 5215389..0000000 --- a/pkg/adapter/surge/surge.go +++ /dev/null @@ -1,101 +0,0 @@ -package surge - -import ( - "bytes" - "embed" - "fmt" - "strings" - "text/template" - "time" - - "github.com/perfect-panel/server/pkg/adapter/proxy" - "github.com/perfect-panel/server/pkg/logger" - "github.com/perfect-panel/server/pkg/tool" - "github.com/perfect-panel/server/pkg/traffic" -) - -//go:embed *.tpl -var configFiles embed.FS - -type UserInfo struct { - UUID string - Upload int64 - Download int64 - TotalTraffic int64 - ExpiredDate time.Time - SubscribeURL string -} - -type Surge struct { - Adapter proxy.Adapter - UUID string - User UserInfo -} - -func NewSurge(adapter proxy.Adapter) *Surge { - return &Surge{ - Adapter: adapter, - } -} - -func (m *Surge) Build(siteName string, user UserInfo) []byte { - var proxies, proxyGroup string - var removed []string - var ps []string - - for _, p := range m.Adapter.Proxies { - switch p.Protocol { - case "shadowsocks": - proxies += buildShadowsocks(p, user.UUID) - case "trojan": - proxies += buildTrojan(p, user.UUID) - case "hysteria2": - proxies += buildHysteria2(p, user.UUID) - case "vmess": - proxies += buildVMess(p, user.UUID) - default: - removed = append(removed, p.Name) - } - ps = append(ps, p.Name) - } - - file, err := configFiles.ReadFile("default.tpl") - if err != nil { - logger.Errorf("read default surfboard config error: %v", err.Error()) - return nil - } - // replace template - tpl, err := template.New("default").Parse(string(file)) - if err != nil { - logger.Errorf("read default surfboard config error: %v", err.Error()) - return nil - } - var buf bytes.Buffer - - var expiredAt string - if user.ExpiredDate.Before(time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC)) { - expiredAt = "长期有效" - } else { - expiredAt = user.ExpiredDate.Format("2006-01-02 15:04:05") - } - - ps = tool.RemoveStringElement(ps, removed...) - proxyGroup = strings.Join(ps, ",") - - // convert traffic - upload := traffic.AutoConvert(user.Upload, false) - download := traffic.AutoConvert(user.Download, false) - total := traffic.AutoConvert(user.TotalTraffic, false) - unusedTraffic := traffic.AutoConvert(user.TotalTraffic-user.Upload-user.Download, false) - // query Host - if err := tpl.Execute(&buf, map[string]interface{}{ - "Proxies": proxies, - "ProxyGroup": proxyGroup, - "SubscribeURL": user.SubscribeURL, - "SubscribeInfo": fmt.Sprintf("title=%s订阅信息, content=上传流量:%s\\n下载流量:%s\\n剩余流量: %s\\n套餐流量:%s\\n到期时间:%s", siteName, upload, download, unusedTraffic, total, expiredAt), - }); err != nil { - logger.Errorf("build Surge config error: %v", err.Error()) - return nil - } - return buf.Bytes() -} diff --git a/pkg/adapter/surge/surge_test.go b/pkg/adapter/surge/surge_test.go deleted file mode 100644 index 8c7b4f3..0000000 --- a/pkg/adapter/surge/surge_test.go +++ /dev/null @@ -1,97 +0,0 @@ -package surge - -import ( - "strings" - "testing" - "time" - - "github.com/perfect-panel/server/pkg/adapter/proxy" -) - -func TestSurgeBuild(t *testing.T) { - adapter := proxy.Adapter{ - Proxies: []proxy.Proxy{ - { - Name: "test-shadowsocks", - Protocol: "shadowsocks", - Server: "1.2.3.4", - Port: 8388, - Option: proxy.Shadowsocks{ - Method: "aes-256-gcm", - }, - }, - { - Name: "test-trojan", - Protocol: "trojan", - Server: "5.6.7.8", - Port: 443, - Option: proxy.Trojan{ - SecurityConfig: proxy.SecurityConfig{ - SNI: "example.com", - AllowInsecure: true, - }, - }, - }, - { - Name: "test-hysteria", - Protocol: "hysteria2", - Server: "1.1.1.1", - Port: 443, - Option: proxy.Hysteria2{ - HopPorts: "8080-8090", - HopInterval: 320, - SecurityConfig: proxy.SecurityConfig{ - SNI: "example.com", - AllowInsecure: true, - }, - }, - }, - }, - Group: []proxy.Group{ - { - Name: "test-group", - Type: proxy.GroupTypeSelect, - Proxies: []string{"test-shadowsocks", "test-trojan", "test-hysteria"}, - }, - { - Name: "手动选择", - Type: proxy.GroupTypeSelect, - Proxies: []string{"test-shadowsocks", "test-trojan", "test-hysteria"}, - }, - }, - Rules: []string{ - "DOMAIN-SUFFIX,example.com,DIRECT", - }, - } - - user := UserInfo{ - UUID: "test-uuid", - Upload: 1024, - Download: 2048, - TotalTraffic: 4096, - ExpiredDate: time.Now().Add(24 * time.Hour), - SubscribeURL: "http://example.com/subscribe", - } - - surge := NewSurge(adapter) - config := surge.Build("test-uuid", "TestSite", user) - - if config == nil { - t.Fatal("Expected non-nil config") - } - - configStr := string(config) - t.Logf("configStr: %v", configStr) - if !strings.Contains(configStr, "test-shadowsocks=ss") { - t.Errorf("Expected config to contain test-shadowsocks proxy") - } - if !strings.Contains(configStr, "test-trojan=trojan") { - t.Errorf("Expected config to contain test-trojan proxy") - } - if !strings.Contains(configStr, "test-group = select") { - t.Errorf("Expected config to contain test-group proxy group") - } - if !strings.Contains(configStr, "DOMAIN-SUFFIX,example.com,DIRECT") { - t.Errorf("Expected config to contain rule for example.com") - } -} diff --git a/pkg/adapter/surge/trojan.go b/pkg/adapter/surge/trojan.go deleted file mode 100644 index f8fc7d2..0000000 --- a/pkg/adapter/surge/trojan.go +++ /dev/null @@ -1,32 +0,0 @@ -package surge - -import ( - "strconv" - "strings" - - "github.com/perfect-panel/server/pkg/adapter/proxy" -) - -func buildTrojan(data proxy.Proxy, uuid string) string { - trojan, ok := data.Option.(proxy.Trojan) - if !ok { - return "" - } - config := []string{ - data.Name + "=trojan", - data.Server, - strconv.Itoa(data.Port), - "password=" + uuid, - "tfo=true", - "udp-relay=true", - } - if trojan.SecurityConfig.SNI != "" { - config = append(config, "sni="+trojan.SecurityConfig.SNI) - } - if trojan.SecurityConfig.AllowInsecure { - config = append(config, "skip-cert-verify=true") - } else { - config = append(config, "skip-cert-verify=false") - } - return strings.Join(config, ",") + "\r\n" -} diff --git a/pkg/adapter/surge/vmess.go b/pkg/adapter/surge/vmess.go deleted file mode 100644 index 28ea9bd..0000000 --- a/pkg/adapter/surge/vmess.go +++ /dev/null @@ -1,44 +0,0 @@ -package surge - -import ( - "fmt" - "strings" - - "github.com/perfect-panel/server/pkg/adapter/proxy" -) - -func buildVMess(data proxy.Proxy, uuid string) string { - vmess, ok := data.Option.(proxy.Vmess) - if !ok { - return "" - } - addr := fmt.Sprintf("%s=vmess, %s, %d", data.Name, data.Server, data.Port) - uriConfig := []string{ - addr, - fmt.Sprintf("username=%s", uuid), - "vmess-aead=true", - "tfo=true", - "udp-relay=true", - } - if vmess.Security == "tls" { - uriConfig = append(uriConfig, "tls=true") - if vmess.SecurityConfig.AllowInsecure { - uriConfig = append(uriConfig, "skip-cert-verify=true") - } else { - uriConfig = append(uriConfig, "skip-cert-verify=false") - } - if vmess.SecurityConfig.SNI != "" { - uriConfig = append(uriConfig, fmt.Sprintf("sni=%s", vmess.SecurityConfig.SNI)) - } - } - if vmess.Transport == "websocket" { - uriConfig = append(uriConfig, "ws=true") - if vmess.TransportConfig.Path != "" { - uriConfig = append(uriConfig, fmt.Sprintf("ws-path=%s", vmess.TransportConfig.Path)) - } - if vmess.TransportConfig.Host != "" { - uriConfig = append(uriConfig, fmt.Sprintf("ws-headers=Host:%s", vmess.TransportConfig.Host)) - } - } - return strings.Join(uriConfig, ",") + "\r\n" -} diff --git a/pkg/adapter/template/clash.tpl b/pkg/adapter/template/clash.tpl deleted file mode 100644 index 6d8dd36..0000000 --- a/pkg/adapter/template/clash.tpl +++ /dev/null @@ -1,51 +0,0 @@ -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}} \ No newline at end of file diff --git a/pkg/adapter/uilts.go b/pkg/adapter/uilts.go deleted file mode 100644 index 04d9cde..0000000 --- a/pkg/adapter/uilts.go +++ /dev/null @@ -1,299 +0,0 @@ -package adapter - -import ( - "encoding/json" - "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 - default: - 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) - 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 - -} diff --git a/pkg/adapter/v2rayn/v2rayN.go b/pkg/adapter/v2rayn/v2rayN.go deleted file mode 100644 index be07d0a..0000000 --- a/pkg/adapter/v2rayn/v2rayN.go +++ /dev/null @@ -1,40 +0,0 @@ -package v2rayn - -import ( - "github.com/perfect-panel/server/pkg/adapter/general" - "github.com/perfect-panel/server/pkg/adapter/proxy" -) - -type v2rayShareLink struct { - Ps string `json:"ps"` - Add string `json:"add"` - Port string `json:"port"` - ID string `json:"id"` - Aid string `json:"aid"` - Net string `json:"net"` - Type string `json:"type"` - Host string `json:"host"` - SNI string `json:"sni"` - Path string `json:"path"` - TLS string `json:"tls"` - Flow string `json:"flow,omitempty"` - Alpn string `json:"alpn,omitempty"` - AllowInsecure bool `json:"allowInsecure,omitempty"` - Fingerprint string `json:"fp,omitempty"` - PublicKey string `json:"pbk,omitempty"` - ShortId string `json:"sid,omitempty"` - SpiderX string `json:"spx,omitempty"` - V string `json:"v"` -} -type V2rayN struct { - proxy.Adapter -} - -func NewV2rayN(adapter proxy.Adapter) *V2rayN { - return &V2rayN{ - Adapter: adapter, - } -} -func (m *V2rayN) Build(uuid string) []byte { - return general.GenerateBase64General(m.Adapter.Proxies, uuid) -}