This commit is contained in:
2025-06-12 02:17:31 +10:00
parent b5fcbab1a5
commit da9a17201b
61 changed files with 1362 additions and 1106 deletions

View File

@ -17,6 +17,7 @@ func GetSupportProxyTypes(clashType ClashType) map[string]bool {
"socks5": true,
}
}
if clashType == ClashMeta {
return map[string]bool{
"ss": true,
@ -30,5 +31,6 @@ func GetSupportProxyTypes(clashType ClashType) map[string]bool {
"anytls": true,
}
}
return nil
}

View File

@ -1,12 +0,0 @@
package model
type Tags []struct {
Name string `json:"name"`
ZipballUrl string `json:"zipball_url"`
TarballUrl string `json:"tarball_url"`
Commit struct {
Sha string `json:"sha"`
Url string `json:"url"`
}
NodeId string `json:"node_id"`
}

View File

@ -1,4 +1,4 @@
package model
package proxy
type Anytls struct {
Type string `yaml:"type"`
@ -35,3 +35,17 @@ func ProxyToAnytls(p Proxy) Anytls {
MinIdleSession: p.MinIdleSession,
}
}
type AnytlsMarshaler struct{}
func (m *AnytlsMarshaler) GetType() string {
return "anytls"
}
func (m *AnytlsMarshaler) MarshalProxy(p Proxy) (interface{}, error) {
return ProxyToAnytls(p), nil
}
func init() {
RegisterMarshaler(&AnytlsMarshaler{})
}

View File

@ -1,4 +1,4 @@
package model
package proxy
type Hysteria struct {
Type string `yaml:"type"`
@ -56,3 +56,13 @@ func ProxyToHysteria(p Proxy) Hysteria {
HopInterval: p.HopInterval,
}
}
type HysteriaMarshaler struct{}
func (m *HysteriaMarshaler) GetType() string {
return "hysteria"
}
func (m *HysteriaMarshaler) MarshalProxy(p Proxy) (interface{}, error) {
return ProxyToHysteria(p), nil
}

View File

@ -1,4 +1,4 @@
package model
package proxy
type Hysteria2 struct {
Type string `yaml:"type"`
@ -39,3 +39,13 @@ func ProxyToHysteria2(p Proxy) Hysteria2 {
CWND: p.CWND,
}
}
type Hysteria2Marshaler struct{}
func (m *Hysteria2Marshaler) GetType() string {
return "hysteria2"
}
func (m *Hysteria2Marshaler) MarshalProxy(p Proxy) (interface{}, error) {
return ProxyToHysteria2(p), nil
}

View File

@ -1,4 +1,4 @@
package model
package proxy
type SmuxStruct struct {
Enabled bool `yaml:"enable"`
@ -86,6 +86,12 @@ type WireGuardPeerOption struct {
type _Proxy Proxy
func (p Proxy) MarshalYAML() (interface{}, error) {
// 尝试使用注册的序列化器
if marshaler, exists := GetMarshaler(p.Type); exists {
return marshaler.MarshalProxy(p)
}
// 保持向后兼容,对于未注册的类型使用原有逻辑
switch p.Type {
case "vmess":
return ProxyToVmess(p), nil

64
model/proxy/registry.go Normal file
View File

@ -0,0 +1,64 @@
package proxy
import (
"sync"
)
// ProxyMarshaler 代理YAML序列化接口
type ProxyMarshaler interface {
// MarshalProxy 将通用Proxy对象序列化为特定协议的YAML结构
MarshalProxy(p Proxy) (interface{}, error)
// GetType 返回支持的协议类型
GetType() string
}
// marshalerRegistry YAML序列化器注册中心
type marshalerRegistry struct {
mu sync.RWMutex
marshalers map[string]ProxyMarshaler // type -> marshaler
}
var yamlRegistry = &marshalerRegistry{
marshalers: make(map[string]ProxyMarshaler),
}
// RegisterMarshaler 注册YAML序列化器
func RegisterMarshaler(marshaler ProxyMarshaler) {
yamlRegistry.mu.Lock()
defer yamlRegistry.mu.Unlock()
yamlRegistry.marshalers[marshaler.GetType()] = marshaler
}
// GetMarshaler 根据协议类型获取序列化器
func GetMarshaler(proxyType string) (ProxyMarshaler, bool) {
yamlRegistry.mu.RLock()
defer yamlRegistry.mu.RUnlock()
marshaler, exists := yamlRegistry.marshalers[proxyType]
return marshaler, exists
}
// GetAllMarshalers 获取所有注册的序列化器
func GetAllMarshalers() map[string]ProxyMarshaler {
yamlRegistry.mu.RLock()
defer yamlRegistry.mu.RUnlock()
result := make(map[string]ProxyMarshaler)
for k, v := range yamlRegistry.marshalers {
result[k] = v
}
return result
}
// GetSupportedTypes 获取所有支持的协议类型
func GetSupportedTypes() []string {
yamlRegistry.mu.RLock()
defer yamlRegistry.mu.RUnlock()
types := make([]string, 0, len(yamlRegistry.marshalers))
for proxyType := range yamlRegistry.marshalers {
types = append(types, proxyType)
}
return types
}

View File

@ -1,4 +1,4 @@
package model
package proxy
type ShadowSocks struct {
Type string `yaml:"type"`
@ -31,3 +31,21 @@ func ProxyToShadowSocks(p Proxy) ShadowSocks {
ClientFingerprint: p.ClientFingerprint,
}
}
// ShadowsocksMarshaler Shadowsocks协议的YAML序列化器
type ShadowsocksMarshaler struct{}
// GetType 返回协议类型
func (m *ShadowsocksMarshaler) GetType() string {
return "ss"
}
// MarshalProxy 序列化Shadowsocks代理
func (m *ShadowsocksMarshaler) MarshalProxy(p Proxy) (interface{}, error) {
return ProxyToShadowSocks(p), nil
}
// 注册序列化器
func init() {
RegisterMarshaler(&ShadowsocksMarshaler{})
}

View File

@ -1,4 +1,4 @@
package model
package proxy
type ShadowSocksR struct {
Type string `yaml:"type"`
@ -29,3 +29,13 @@ func ProxyToShadowSocksR(p Proxy) ShadowSocksR {
UDP: p.UDP,
}
}
type ShadowsocksRMarshaler struct{}
func (m *ShadowsocksRMarshaler) GetType() string {
return "ssr"
}
func (m *ShadowsocksRMarshaler) MarshalProxy(p Proxy) (interface{}, error) {
return ProxyToShadowSocksR(p), nil
}

View File

@ -1,4 +1,4 @@
package model
package proxy
type Trojan struct {
Type string `yaml:"type"`
@ -37,3 +37,13 @@ func ProxyToTrojan(p Proxy) Trojan {
ClientFingerprint: p.ClientFingerprint,
}
}
type TrojanMarshaler struct{}
func (m *TrojanMarshaler) GetType() string {
return "trojan"
}
func (m *TrojanMarshaler) MarshalProxy(p Proxy) (interface{}, error) {
return ProxyToTrojan(p), nil
}

View File

@ -1,4 +1,4 @@
package model
package proxy
type Vless struct {
Type string `yaml:"type"`
@ -55,3 +55,13 @@ func ProxyToVless(p Proxy) Vless {
ClientFingerprint: p.ClientFingerprint,
}
}
type VlessMarshaler struct{}
func (m *VlessMarshaler) GetType() string {
return "vless"
}
func (m *VlessMarshaler) MarshalProxy(p Proxy) (interface{}, error) {
return ProxyToVless(p), nil
}

View File

@ -1,4 +1,4 @@
package model
package proxy
type HTTPOptions struct {
Method string `yaml:"method,omitempty"`
@ -102,3 +102,13 @@ func ProxyToVmess(p Proxy) Vmess {
ClientFingerprint: p.ClientFingerprint,
}
}
type VmessMarshaler struct{}
func (m *VmessMarshaler) GetType() string {
return "vmess"
}
func (m *VmessMarshaler) MarshalProxy(p Proxy) (interface{}, error) {
return ProxyToVmess(p), nil
}

158
model/sub_config.go Normal file
View File

@ -0,0 +1,158 @@
package model
import (
"crypto/sha256"
"encoding/hex"
"errors"
"net/url"
"regexp"
"strings"
"github.com/gin-gonic/gin"
)
type SubConfig struct {
Sub string `form:"sub" binding:""`
Subs []string `form:"-" binding:""`
Proxy string `form:"proxy" binding:""`
Proxies []string `form:"-" binding:""`
Refresh bool `form:"refresh,default=false" binding:""`
Template string `form:"template" binding:""`
RuleProvider string `form:"ruleProvider" binding:""`
RuleProviders []RuleProviderStruct `form:"-" binding:""`
Rule string `form:"rule" binding:""`
Rules []RuleStruct `form:"-" binding:""`
AutoTest bool `form:"autoTest,default=false" binding:""`
Lazy bool `form:"lazy,default=false" binding:""`
Sort string `form:"sort" binding:""`
Remove string `form:"remove" binding:""`
Replace string `form:"replace" binding:""`
ReplaceKeys []string `form:"-" binding:""`
ReplaceTo []string `form:"-" binding:""`
NodeListMode bool `form:"nodeList,default=false" binding:""`
IgnoreCountryGrooup bool `form:"ignoreCountryGroup,default=false" binding:""`
UserAgent string `form:"userAgent" binding:""`
}
type RuleProviderStruct struct {
Behavior string
Url string
Group string
Prepend bool
Name string
}
type RuleStruct struct {
Rule string
Prepend bool
}
func ParseSubQuery(c *gin.Context) (SubConfig, error) {
var query SubConfig
if err := c.ShouldBind(&query); err != nil {
return SubConfig{}, errors.New("参数错误: " + err.Error())
}
if query.Sub == "" && query.Proxy == "" {
return SubConfig{}, errors.New("参数错误: sub 和 proxy 不能同时为空")
}
if query.Sub != "" {
query.Subs = strings.Split(query.Sub, ",")
for i := range query.Subs {
if !strings.HasPrefix(query.Subs[i], "http") {
return SubConfig{}, errors.New("参数错误: sub 格式错误")
}
if _, err := url.ParseRequestURI(query.Subs[i]); err != nil {
return SubConfig{}, errors.New("参数错误: " + err.Error())
}
}
} else {
query.Subs = nil
}
if query.Proxy != "" {
query.Proxies = strings.Split(query.Proxy, ",")
} else {
query.Proxies = nil
}
if query.Template != "" {
if strings.HasPrefix(query.Template, "http") {
uri, err := url.ParseRequestURI(query.Template)
if err != nil {
return SubConfig{}, err
}
query.Template = uri.String()
}
}
if query.RuleProvider != "" {
reg := regexp.MustCompile(`\[(.*?)\]`)
ruleProviders := reg.FindAllStringSubmatch(query.RuleProvider, -1)
for i := range ruleProviders {
length := len(ruleProviders)
parts := strings.Split(ruleProviders[length-i-1][1], ",")
if len(parts) < 4 {
return SubConfig{}, errors.New("参数错误: ruleProvider 格式错误")
}
u := parts[1]
uri, err := url.ParseRequestURI(u)
if err != nil {
return SubConfig{}, errors.New("参数错误: " + err.Error())
}
u = uri.String()
if len(parts) == 4 {
hash := sha256.Sum224([]byte(u))
parts = append(parts, hex.EncodeToString(hash[:]))
}
query.RuleProviders = append(
query.RuleProviders, RuleProviderStruct{
Behavior: parts[0],
Url: u,
Group: parts[2],
Prepend: parts[3] == "true",
Name: parts[4],
},
)
}
names := make(map[string]bool)
for _, ruleProvider := range query.RuleProviders {
if _, ok := names[ruleProvider.Name]; ok {
return SubConfig{}, errors.New("参数错误: Rule-Provider 名称重复")
}
names[ruleProvider.Name] = true
}
} else {
query.RuleProviders = nil
}
if query.Rule != "" {
reg := regexp.MustCompile(`\[(.*?)\]`)
rules := reg.FindAllStringSubmatch(query.Rule, -1)
for i := range rules {
length := len(rules)
r := rules[length-1-i][1]
strings.LastIndex(r, ",")
parts := [2]string{}
parts[0] = r[:strings.LastIndex(r, ",")]
parts[1] = r[strings.LastIndex(r, ",")+1:]
query.Rules = append(
query.Rules, RuleStruct{
Rule: parts[0],
Prepend: parts[1] == "true",
},
)
}
} else {
query.Rules = nil
}
if strings.TrimSpace(query.Replace) != "" {
reg := regexp.MustCompile(`\[<(.*?)>,<(.*?)>\]`)
replaces := reg.FindAllStringSubmatch(query.Replace, -1)
for i := range replaces {
length := len(replaces[i])
if length != 3 {
return SubConfig{}, errors.New("参数错误: replace 格式错误")
}
query.ReplaceKeys = append(query.ReplaceKeys, replaces[i][1])
query.ReplaceTo = append(query.ReplaceTo, replaces[i][2])
}
}
return query, nil
}

View File

@ -1,7 +1,9 @@
package model
import "github.com/bestnite/sub2clash/model/proxy"
type NodeList struct {
Proxies []Proxy `yaml:"proxies,omitempty"`
Proxies []proxy.Proxy `yaml:"proxies,omitempty"`
}
type Subscription struct {
@ -57,7 +59,7 @@ type Subscription struct {
Experimental Experimental `yaml:"experimental,omitempty"`
Profile Profile `yaml:"profile,omitempty"`
GeoXUrl GeoXUrl `yaml:"geox-url,omitempty"`
Proxies []Proxy `yaml:"proxies,omitempty"`
Proxies []proxy.Proxy `yaml:"proxies,omitempty"`
ProxyGroups []ProxyGroup `yaml:"proxy-groups,omitempty"`
Rules []string `yaml:"rules,omitempty"`
SubRules map[string][]string `yaml:"sub-rules,omitempty"`