精简: 订阅极简输出: 去掉分流规则注入(Happ/Clash/Incy 远程规则源+remote_routing 拉取缓存+RemoteRoutingJob、Clash 远程规则合并簇、subJsonRules 注入); 保留多格式转换与默认极简模板

This commit is contained in:
Muse
2026-10-20 10:17:27 +00:00
parent 3cbf9164a4
commit 7d42b91ed6
25 changed files with 87 additions and 2100 deletions
-55
View File
@@ -81,11 +81,7 @@ var defaultValueMap = map[string]string{
"subSupportUrl": "",
"subProfileUrl": "",
"subAnnounce": "",
"subEnableRouting": "false",
"subRoutingRules": "",
"subHideSettings": "false",
"subIncyEnableRouting": "false",
"subIncyRoutingRules": "",
"subListen": "",
"subPort": "2096",
"subPath": "/sub/",
@@ -100,10 +96,7 @@ var defaultValueMap = map[string]string{
"subClashEnable": "false",
"subClashPath": "/clash/",
"subClashURI": "",
"subClashEnableRouting": "false",
"subClashRules": "",
"subJsonMux": "",
"subJsonRules": "",
"subJsonFinalMask": "",
"subJsonObservatory": "",
"subThemeDir": "",
@@ -623,26 +616,10 @@ func (s *SettingService) GetSubAnnounce() (string, error) {
return s.getString("subAnnounce")
}
func (s *SettingService) GetSubEnableRouting() (bool, error) {
return s.getBool("subEnableRouting")
}
func (s *SettingService) GetSubRoutingRules() (string, error) {
return s.getString("subRoutingRules")
}
func (s *SettingService) GetSubHideSettings() (bool, error) {
return s.getBool("subHideSettings")
}
func (s *SettingService) GetSubIncyEnableRouting() (bool, error) {
return s.getBool("subIncyEnableRouting")
}
func (s *SettingService) GetSubIncyRoutingRules() (string, error) {
return s.getString("subIncyRoutingRules")
}
func (s *SettingService) GetSubListen() (string, error) {
return s.getString("subListen")
}
@@ -711,22 +688,10 @@ func (s *SettingService) GetSubClashURI() (string, error) {
return s.getString("subClashURI")
}
func (s *SettingService) GetSubClashEnableRouting() (bool, error) {
return s.getBool("subClashEnableRouting")
}
func (s *SettingService) GetSubClashRules() (string, error) {
return s.getString("subClashRules")
}
func (s *SettingService) GetSubJsonMux() (string, error) {
return s.getString("subJsonMux")
}
func (s *SettingService) GetSubJsonRules() (string, error) {
return s.getString("subJsonRules")
}
func (s *SettingService) GetSubJsonFinalMask() (string, error) {
return s.getString("subJsonFinalMask")
}
@@ -913,26 +878,6 @@ func validateSettingsURLs(allSetting *entity.AllSetting) error {
// the scheme instead of forcing SanitizeHTTPURL's http(s)-only rule.
allSetting.SubSupportUrl = common.EnsureURLScheme(allSetting.SubSupportUrl)
allSetting.SubProfileUrl = common.EnsureURLScheme(allSetting.SubProfileUrl)
for name, value := range map[string]*string{
"Happ routing source": &allSetting.SubRoutingRules,
"Clash/Mihomo routing source": &allSetting.SubClashRules,
"Incy routing source": &allSetting.SubIncyRoutingRules,
} {
if err := validateRemoteRoutingURLSetting(name, value); err != nil {
return err
}
}
return nil
}
func validateRemoteRoutingURLSetting(name string, value *string) error {
canonical, remote, err := common.ParseRemoteRoutingURL(*value)
if err != nil {
return common.NewError(name, err.Error())
}
if remote {
*value = canonical
}
return nil
}
@@ -128,8 +128,6 @@ func TestUpdateAllSettingPersistsClashSubscriptionSettings(t *testing.T) {
settings.SubClashEnable = true
settings.SubClashPath = "/clash-custom/"
settings.SubClashURI = "https://subscriptions.example.com/clash-custom/"
settings.SubClashEnableRouting = true
settings.SubClashRules = "GEOIP,private,DIRECT"
if err := s.UpdateAllSetting(settings); err != nil {
t.Fatal(err)
@@ -172,12 +170,6 @@ func TestUpdateAllSettingPersistsClashSubscriptionSettings(t *testing.T) {
if got.SubClashURI != "https://subscriptions.example.com/clash-custom/" {
t.Fatalf("subClashURI = %q, want %q", got.SubClashURI, "https://subscriptions.example.com/clash-custom/")
}
if !got.SubClashEnableRouting {
t.Fatal("subClashEnableRouting = false, want true")
}
if got.SubClashRules != "GEOIP,private,DIRECT" {
t.Fatalf("subClashRules = %q, want %q", got.SubClashRules, "GEOIP,private,DIRECT")
}
}
func TestUpdateAllSettingRejectsInvalidClashUserAgentRegex(t *testing.T) {
@@ -1,38 +0,0 @@
package service
import (
"strings"
"testing"
"github.com/mhsanaei/3x-ui/v3/internal/web/entity"
)
func TestValidateRemoteRoutingURLSettings(t *testing.T) {
tests := []struct {
name string
value string
want string
wantError string
}{
{name: "valid HTTPS", value: " https://example.com/rules#fragment ", want: "https://example.com/rules"},
{name: "credentials", value: "https://user:pass@example.com/rules", wantError: "must not contain URL credentials"},
{name: "missing host", value: "https:///rules", wantError: "absolute HTTPS URL"},
{name: "legacy HTTP stays inline", value: "http://example.com/rules", want: "http://example.com/rules"},
{name: "multiline Clash stays inline", value: "https://example.com/rules\nMATCH,PROXY", want: "https://example.com/rules\nMATCH,PROXY"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
settings := &entity.AllSetting{SubRoutingRules: tt.value}
err := validateSettingsURLs(settings)
if tt.wantError != "" {
if err == nil || !strings.Contains(err.Error(), tt.wantError) {
t.Fatalf("err=%v, want %q", err, tt.wantError)
}
return
}
if err != nil || settings.SubRoutingRules != tt.want {
t.Fatalf("value=%q err=%v", settings.SubRoutingRules, err)
}
})
}
}