mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-09-17 15:47:14 +00:00
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:
@@ -83,6 +83,7 @@ func (s *SubJsonService) GetJson(subId string, host string, alwaysReturnArray bo
|
||||
|
||||
var header string
|
||||
var hasInactiveExternal bool
|
||||
var hasEnabledClient bool
|
||||
|
||||
seenEmails := make(map[string]struct{})
|
||||
entries := make([]subConfigEntry, 0, len(inbounds))
|
||||
@@ -99,6 +100,9 @@ func (s *SubJsonService) GetJson(subId string, host string, alwaysReturnArray bo
|
||||
|
||||
var inboundConfigs []json_util.RawMessage
|
||||
for _, client := range clients {
|
||||
if client.Enable {
|
||||
hasEnabledClient = true
|
||||
}
|
||||
seenEmails[client.Email] = struct{}{}
|
||||
inboundConfigs = append(inboundConfigs, s.getConfig(subReq, inbound, client, host)...)
|
||||
}
|
||||
@@ -128,6 +132,9 @@ func (s *SubJsonService) GetJson(subId string, host string, alwaysReturnArray bo
|
||||
configArray = append(configArray, entry.configs...)
|
||||
}
|
||||
for _, ext := range externalLinks {
|
||||
if ext.Enable {
|
||||
hasEnabledClient = true
|
||||
}
|
||||
if !ext.Active {
|
||||
seenEmails[ext.Email] = struct{}{}
|
||||
hasInactiveExternal = true
|
||||
@@ -162,8 +169,20 @@ func (s *SubJsonService) GetJson(subId string, host string, alwaysReturnArray bo
|
||||
for e := range seenEmails {
|
||||
emails = append(emails, e)
|
||||
}
|
||||
slices.Sort(emails)
|
||||
traffic, _ := subReq.AggregateTrafficByEmails(emails)
|
||||
traffic.Enable = hasEnabledClient
|
||||
header = fmt.Sprintf("upload=%d; download=%d; total=%d; expire=%d", traffic.Up, traffic.Down, traffic.Total, traffic.ExpiryTime/1000)
|
||||
|
||||
if mode, remark := subReq.resolveInfoNodeRemark(subId, emails, traffic, len(configArray) > 0); mode != infoNodeNone {
|
||||
dummyConfig := s.genDummySocksConfig(remark)
|
||||
if mode == infoNodeExpired || mode == infoNodeDepleted {
|
||||
configArray = []json_util.RawMessage{dummyConfig}
|
||||
} else {
|
||||
configArray = append([]json_util.RawMessage{dummyConfig}, configArray...)
|
||||
}
|
||||
}
|
||||
|
||||
if len(configArray) == 0 {
|
||||
return "", header, nil
|
||||
}
|
||||
@@ -962,6 +981,32 @@ func (s *SubJsonService) genWireguard(inbound *model.Inbound, client model.Clien
|
||||
return result
|
||||
}
|
||||
|
||||
func (s *SubJsonService) genDummySocksConfig(remark string) json_util.RawMessage {
|
||||
outbound := map[string]any{
|
||||
"protocol": "socks",
|
||||
"tag": "proxy",
|
||||
"settings": map[string]any{
|
||||
"servers": []any{
|
||||
map[string]any{
|
||||
"address": "127.0.0.1",
|
||||
"port": 1080,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
rawOutbound, _ := json.Marshal(outbound)
|
||||
newOutbounds := []json_util.RawMessage{rawOutbound}
|
||||
newOutbounds = append(newOutbounds, s.defaultOutbounds...)
|
||||
|
||||
newConfigJson := make(map[string]any)
|
||||
maps.Copy(newConfigJson, s.configJson)
|
||||
newConfigJson["outbounds"] = newOutbounds
|
||||
newConfigJson["remarks"] = remark
|
||||
|
||||
newConfig, _ := json.MarshalIndent(newConfigJson, "", " ")
|
||||
return newConfig
|
||||
}
|
||||
|
||||
func mergeFinalMask(base any, extra map[string]any) map[string]any {
|
||||
merged := map[string]any{}
|
||||
if baseMap, ok := base.(map[string]any); ok {
|
||||
|
||||
Reference in New Issue
Block a user