feat(sub): add dummy info node and status configs for subscriptions (#6412)

* feat(settings): add subInfoNodeEnable and status template settings

* feat(sub): add dummy info node and status configs for raw links

* feat(sub): support dummy info node in clash and json subscriptions

* feat(ui): add subscription info node switch and status templates to settings

* style: apply gofumpt formatting

* fix(sub): address review feedback on subscription info node

- Restore GetSubs contract to avoid unintended remark expansions on non-subscription-body calls.
- Exclude dummy info node from Clash PROXY select group when active server nodes exist.
- Track hasEnabledClient and set traffic.Enable in JSON and Clash paths so status tokens evaluate correctly.
- Deterministically sort client emails across subscriptions before selecting primaryEmail.
- Consolidate duplicated info-node evaluation logic into resolveInfoNodeRemark helper.
- Remove redundant pure-getter test from setting_sub_info_node_test.go.

Co-Authored-By: Claude Code <noreply@anthropic.com>

---------

Co-authored-by: Claude Code <noreply@anthropic.com>
This commit is contained in:
Rouzbeh†
2026-09-08 18:44:01 +03:30
committed by GitHub
parent b8597314f8
commit 9f76a66dcf
33 changed files with 1374 additions and 35 deletions
+22 -5
View File
@@ -34,11 +34,13 @@ import (
var xrayTemplateConfig string
const (
DefaultSubClashUserAgentRegex = `(?i)(clash|mihomo)`
DefaultSubJsonUserAgentRegex = ``
DefaultRemarkTemplate = "{{INBOUND}}-{{EMAIL}}|📊{{TRAFFIC_LEFT}}|⏳{{DAYS_LEFT}}D"
DefaultTrustedProxyCIDRs = "127.0.0.1/32,::1/128"
maxRegexLength = 2048
DefaultSubClashUserAgentRegex = `(?i)(clash|mihomo)`
DefaultSubJsonUserAgentRegex = ``
DefaultRemarkTemplate = "{{INBOUND}}-{{EMAIL}}|📊{{TRAFFIC_LEFT}}|⏳{{DAYS_LEFT}}D"
DefaultSubExpiredTemplate = "⛔ {{EMAIL}} | Expired: {{EXPIRE_DATE}}"
DefaultSubTrafficDepletedTemplate = "🚫 {{EMAIL}} | Traffic Depleted | {{TRAFFIC_USED}}/{{TRAFFIC_TOTAL}}"
DefaultTrustedProxyCIDRs = "127.0.0.1/32,::1/128"
maxRegexLength = 2048
)
var defaultValueMap = map[string]string{
@@ -70,6 +72,9 @@ var defaultValueMap = map[string]string{
"trafficDiff": "0",
"remarkTemplate": DefaultRemarkTemplate,
"subShowIdentityOnAllLinks": "false",
"subInfoNodeEnable": "false",
"subExpiredTemplate": DefaultSubExpiredTemplate,
"subTrafficDepletedTemplate": DefaultSubTrafficDepletedTemplate,
"timeLocation": "Local",
"tgBotEnable": "false",
"tgBotToken": "",
@@ -676,6 +681,18 @@ func (s *SettingService) GetSubShowIdentityOnAllLinks() (bool, error) {
return s.getBool("subShowIdentityOnAllLinks")
}
func (s *SettingService) GetSubInfoNodeEnable() (bool, error) {
return s.getBool("subInfoNodeEnable")
}
func (s *SettingService) GetSubExpiredTemplate() (string, error) {
return s.getString("subExpiredTemplate")
}
func (s *SettingService) GetSubTrafficDepletedTemplate() (string, error) {
return s.getString("subTrafficDepletedTemplate")
}
func (s *SettingService) GetSecret() ([]byte, error) {
secret, err := s.getString("secret")
if secret == "" || secret == defaultValueMap["secret"] {