精简: 订阅极简输出: 去掉分流规则注入(Happ/Clash/Incy 远程规则源+remote_routing 拉取缓存+RemoteRoutingJob、Clash 远程规则合并簇、subJsonRules 注入); 保留多格式转换与默认极简模板
This commit is contained in:
@@ -23,8 +23,6 @@ func TestJobCadencesAreValidCronSpecs(t *testing.T) {
|
||||
"cadenceNodeHeartbeat": cadenceNodeHeartbeat,
|
||||
"cadenceNodeTraffic": cadenceNodeTraffic,
|
||||
"cadenceOutboundSub": cadenceOutboundSub,
|
||||
"cadenceRemoteRouting": cadenceRemoteRouting,
|
||||
"cadenceCheckHash": cadenceCheckHash,
|
||||
"cadenceCPUAlarm": cadenceCPUAlarm,
|
||||
}
|
||||
for name, spec := range cadences {
|
||||
|
||||
@@ -51,10 +51,6 @@ type AllSetting struct {
|
||||
SubSupportUrl string `json:"subSupportUrl" form:"subSupportUrl"`
|
||||
SubProfileUrl string `json:"subProfileUrl" form:"subProfileUrl"`
|
||||
SubAnnounce string `json:"subAnnounce" form:"subAnnounce"`
|
||||
SubEnableRouting bool `json:"subEnableRouting" form:"subEnableRouting"`
|
||||
SubRoutingRules string `json:"subRoutingRules" form:"subRoutingRules"`
|
||||
SubIncyEnableRouting bool `json:"subIncyEnableRouting" form:"subIncyEnableRouting"`
|
||||
SubIncyRoutingRules string `json:"subIncyRoutingRules" form:"subIncyRoutingRules"`
|
||||
SubListen string `json:"subListen" form:"subListen"`
|
||||
SubPort int `json:"subPort" form:"subPort" validate:"gte=1,lte=65535"`
|
||||
SubPath string `json:"subPath" form:"subPath"`
|
||||
@@ -70,10 +66,7 @@ type AllSetting struct {
|
||||
SubClashEnable bool `json:"subClashEnable" form:"subClashEnable"`
|
||||
SubClashPath string `json:"subClashPath" form:"subClashPath"`
|
||||
SubClashURI string `json:"subClashURI" form:"subClashURI"`
|
||||
SubClashEnableRouting bool `json:"subClashEnableRouting" form:"subClashEnableRouting"`
|
||||
SubClashRules string `json:"subClashRules" form:"subClashRules"`
|
||||
SubJsonMux string `json:"subJsonMux" form:"subJsonMux"`
|
||||
SubJsonRules string `json:"subJsonRules" form:"subJsonRules"`
|
||||
SubJsonFinalMask string `json:"subJsonFinalMask" form:"subJsonFinalMask"`
|
||||
SubJsonObservatory string `json:"subJsonObservatory" form:"subJsonObservatory"`
|
||||
SubThemeDir string `json:"subThemeDir" form:"subThemeDir"`
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
package job
|
||||
|
||||
import (
|
||||
"github.com/mhsanaei/3x-ui/v3/internal/logger"
|
||||
"github.com/mhsanaei/3x-ui/v3/internal/sub"
|
||||
"github.com/mhsanaei/3x-ui/v3/internal/web/service"
|
||||
)
|
||||
|
||||
// RemoteRoutingJob keeps remote Happ and Clash/Mihomo routing URLs warm: all
|
||||
// network work runs here (cron + startup warm), never in a request handler.
|
||||
type RemoteRoutingJob struct {
|
||||
settingService service.SettingService
|
||||
}
|
||||
|
||||
func NewRemoteRoutingJob() *RemoteRoutingJob {
|
||||
return &RemoteRoutingJob{}
|
||||
}
|
||||
|
||||
func (j *RemoteRoutingJob) Run() {
|
||||
happ, err := j.settingService.GetSubRoutingRules()
|
||||
if err != nil {
|
||||
logger.Warning("Could not read Happ routing source:", err)
|
||||
return
|
||||
}
|
||||
clash, err := j.settingService.GetSubClashRules()
|
||||
if err != nil {
|
||||
logger.Warning("Could not read Clash routing source:", err)
|
||||
return
|
||||
}
|
||||
sub.RefreshRemoteRoutingSources(happ, clash)
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -295,9 +295,7 @@ const (
|
||||
cadenceNodeTraffic = "@every 5s"
|
||||
cadenceOutboundSub = "@every 5m"
|
||||
cadenceReapOrphans = "@every 5m"
|
||||
cadenceRemoteRouting = "@every 5m"
|
||||
cadenceXrayLogPrune = "@every 10m"
|
||||
cadenceCheckHash = "@every 2m"
|
||||
// cpu.Percent samples over a full minute (blocking), so a finer cadence just
|
||||
// stacks overlapping samplers; subscribers rate-limit alerts to 1/min anyway.
|
||||
cadenceCPUAlarm = "@every 1m"
|
||||
@@ -348,11 +346,6 @@ func (s *Server) startTask(restartXray bool, loc *time.Location) {
|
||||
|
||||
_, _ = s.cron.AddJob(cadenceReapOrphans, job.NewReapSyncOrphansJob())
|
||||
|
||||
// Warm permanent routing URLs immediately and refresh them outside the
|
||||
// latency-sensitive subscription request path.
|
||||
remoteRoutingJob := job.NewRemoteRoutingJob()
|
||||
_, _ = s.cron.AddJob(cadenceRemoteRouting, remoteRoutingJob)
|
||||
common.GoRecover("remote-routing-warm", remoteRoutingJob.Run)
|
||||
|
||||
// check client ips from log file every day
|
||||
_, _ = s.cron.AddJob("@daily", job.NewClearLogsJob())
|
||||
|
||||
Reference in New Issue
Block a user