[subclash] support optional default groups and inject all #1155
Some checks failed
Release S-UI / build-frontend (push) Has been cancelled
Build S-UI for Windows / build-frontend (push) Has been cancelled
Release S-UI / build-386 (push) Has been cancelled
Release S-UI / build-amd64 (push) Has been cancelled
Release S-UI / build-armv5 (push) Has been cancelled
Release S-UI / build-armv6 (push) Has been cancelled
Release S-UI / build-armv7 (push) Has been cancelled
Release S-UI / build-arm64 (push) Has been cancelled
Release S-UI / build-s390x (push) Has been cancelled
Build S-UI for Windows / build-windows-amd64 (push) Has been cancelled
Build S-UI for Windows / build-windows-arm64 (push) Has been cancelled

This commit is contained in:
Alireza Ahmadi 2026-07-04 15:31:31 +02:00
parent bb81281e30
commit 655a159394
3 changed files with 44 additions and 25 deletions

@ -1 +1 @@
Subproject commit bc4cda476bfeea2986efef31727afb8c4d4bfb08
Subproject commit 1349ef579bb97c9ebe587db000773f56c949ad99

View file

@ -66,6 +66,8 @@ var defaultValueMap = map[string]string{
"subURI": "",
"subJsonExt": "",
"subClashExt": "",
"subClashNoDefGrp": "false",
"subClashSprtAll": "false",
"globalReset": "",
"globalResetLast": "0",
"config": defaultConfig,
@ -455,6 +457,20 @@ func (s *SettingService) GetSubClashExt() (string, error) {
return s.getString("subClashExt")
}
// GetSubClashNoDefGrp reports whether the default "Proxy"/"Auto" proxy-groups
// should never be injected into a Clash subscription. When true, the config is
// left with exactly the groups the user defined.
func (s *SettingService) GetSubClashNoDefGrp() (bool, error) {
return s.getBool("subClashNoDefGrp")
}
// GetSubClashSprtAll reports whether a case-insensitive "all" entry inside a
// custom proxy-group's "proxies" list should be expanded into every generated
// proxy tag.
func (s *SettingService) GetSubClashSprtAll() (bool, error) {
return s.getBool("subClashSprtAll")
}
func (s *SettingService) fileExists(path string) error {
_, err := os.Stat(path)
return err

View file

@ -389,29 +389,12 @@ func (s *ClashService) ConvertToClashMeta(outbounds *[]map[string]interface{}, b
output["proxies"] = proxies
}
// Inject default Proxy + Auto unless user already has "Proxy", or
// no custom group references it (fully custom config: don't inject).
pgSlice, _ := output["proxy-groups"].([]interface{})
inject := !hasGroupNamed(pgSlice, "Proxy")
if inject && len(pgSlice) > 0 {
inject = false
for _, item := range pgSlice {
if g, ok := item.(map[string]interface{}); ok {
if proxies, ok := g["proxies"].([]interface{}); ok {
for _, p := range proxies {
if name, ok := p.(string); ok && name == "Proxy" {
inject = true
break
}
}
}
}
if inject {
break
}
}
}
if inject {
noDefGrp, _ := s.SettingService.GetSubClashNoDefGrp()
sprtAll, _ := s.SettingService.GetSubClashSprtAll()
if !noDefGrp {
var proxyGroups []map[string]interface{}
if err := yaml.Unmarshal([]byte(ProxyGroups), &proxyGroups); err != nil {
return "", err
@ -435,6 +418,28 @@ func (s *ClashService) ConvertToClashMeta(outbounds *[]map[string]interface{}, b
}
}
if sprtAll {
if list, ok := output["proxy-groups"].([]map[string]interface{}); ok {
for _, group := range list {
proxies, ok := group["proxies"].([]interface{})
if !ok {
continue
}
newProxies := make([]interface{}, 0, len(proxies))
for _, p := range proxies {
if name, ok := p.(string); ok && strings.EqualFold(name, "all") {
for _, tag := range proxyTags {
newProxies = append(newProxies, tag)
}
} else {
newProxies = append(newProxies, p)
}
}
group["proxies"] = newProxies
}
}
}
result, err := yaml.Marshal(output)
if err != nil {
return "", err
@ -442,8 +447,6 @@ func (s *ClashService) ConvertToClashMeta(outbounds *[]map[string]interface{}, b
return string(result), nil
}
// hasGroupNamed checks whether a "proxy-groups" interface{} slice contains a group with the given name.
// Used to avoid injecting duplicate "Proxy" / "Auto" default groups when the user already defined their own.
func hasGroupNamed(pg interface{}, name string) bool {
list, ok := pg.([]interface{})
if !ok {