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:
@@ -4,6 +4,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"maps"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/goccy/go-json"
|
||||
@@ -40,6 +41,7 @@ func (s *SubClashService) GetClash(subId string, host string) (string, string, e
|
||||
|
||||
var proxies []map[string]any
|
||||
var hasInactiveExternal bool
|
||||
var hasEnabledClient bool
|
||||
|
||||
seenEmails := make(map[string]struct{})
|
||||
for _, inbound := range inbounds {
|
||||
@@ -52,11 +54,17 @@ func (s *SubClashService) GetClash(subId string, host string) (string, string, e
|
||||
injectExternalProxy(inbound, hostEps)
|
||||
}
|
||||
for _, client := range clients {
|
||||
if client.Enable {
|
||||
hasEnabledClient = true
|
||||
}
|
||||
seenEmails[client.Email] = struct{}{}
|
||||
proxies = append(proxies, s.getProxies(subReq, inbound, client, host)...)
|
||||
}
|
||||
}
|
||||
for _, ext := range externalLinks {
|
||||
if ext.Enable {
|
||||
hasEnabledClient = true
|
||||
}
|
||||
if !ext.Active {
|
||||
seenEmails[ext.Email] = struct{}{}
|
||||
hasInactiveExternal = true
|
||||
@@ -82,8 +90,25 @@ func (s *SubClashService) GetClash(subId string, host string) (string, string, e
|
||||
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(proxies) > 0); mode != infoNodeNone {
|
||||
dummyProxy := map[string]any{
|
||||
"name": remark,
|
||||
"type": "socks5",
|
||||
"server": "127.0.0.1",
|
||||
"port": 1080,
|
||||
}
|
||||
if mode == infoNodeExpired || mode == infoNodeDepleted {
|
||||
proxies = []map[string]any{dummyProxy}
|
||||
} else {
|
||||
proxies = append([]map[string]any{dummyProxy}, proxies...)
|
||||
}
|
||||
}
|
||||
|
||||
if len(proxies) == 0 {
|
||||
return "", header, nil
|
||||
}
|
||||
@@ -92,6 +117,9 @@ func (s *SubClashService) GetClash(subId string, host string) (string, string, e
|
||||
|
||||
proxyNames := make([]string, 0, len(proxies)+1)
|
||||
for _, proxy := range proxies {
|
||||
if isDummyProxy(proxy) && len(proxies) > 1 {
|
||||
continue
|
||||
}
|
||||
if name, ok := proxy["name"].(string); ok && name != "" {
|
||||
proxyNames = append(proxyNames, name)
|
||||
}
|
||||
@@ -152,6 +180,19 @@ func ensureUniqueProxyNames(proxies []map[string]any) {
|
||||
}
|
||||
}
|
||||
|
||||
func isDummyProxy(proxy map[string]any) bool {
|
||||
typ, _ := proxy["type"].(string)
|
||||
server, _ := proxy["server"].(string)
|
||||
var port int
|
||||
switch p := proxy["port"].(type) {
|
||||
case int:
|
||||
port = p
|
||||
case float64:
|
||||
port = int(p)
|
||||
}
|
||||
return typ == "socks5" && server == "127.0.0.1" && port == 1080
|
||||
}
|
||||
|
||||
func fallbackProxyName(proxy map[string]any, idx int) string {
|
||||
typ, _ := proxy["type"].(string)
|
||||
server, _ := proxy["server"].(string)
|
||||
|
||||
Reference in New Issue
Block a user