mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-08-31 15:37:14 +00:00
fix(sub): render the full remark once per subscription, not once per credential (#6198)
* fix(sub): scope full remarks to subscription identity * fix(sub): preserve configured remark whitespace --------- Co-authored-by: n0ctal <293235942+n0ctal@users.noreply.github.com>
This commit is contained in:
@@ -607,19 +607,27 @@ func appendKeptRun(runs []string, run string, leftRemoved, rightRemoved bool) []
|
|||||||
return runs
|
return runs
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SubService) effectiveTemplate(email string) string {
|
func templateInfoKey(client model.Client) string {
|
||||||
|
if client.SubID != "" {
|
||||||
|
return "sub:" + client.SubID
|
||||||
|
}
|
||||||
|
return "email:" + client.Email
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SubService) effectiveTemplate(client model.Client) string {
|
||||||
translated := translateUISingleBrackets(s.remarkTemplate)
|
translated := translateUISingleBrackets(s.remarkTemplate)
|
||||||
if s.usageShown == nil {
|
if s.usageShown == nil {
|
||||||
s.usageShown = map[string]bool{}
|
s.usageShown = map[string]bool{}
|
||||||
}
|
}
|
||||||
if s.usageShown[email] {
|
key := templateInfoKey(client)
|
||||||
|
if s.usageShown[key] {
|
||||||
remove := firstLinkOnlyBodyTokens
|
remove := firstLinkOnlyBodyTokens
|
||||||
if s.showIdentityOnAllLinks {
|
if s.showIdentityOnAllLinks {
|
||||||
remove = usageInfoTokens
|
remove = usageInfoTokens
|
||||||
}
|
}
|
||||||
return filterRemarkTemplate(translated, remove)
|
return filterRemarkTemplate(translated, remove)
|
||||||
}
|
}
|
||||||
s.usageShown[email] = true
|
s.usageShown[key] = true
|
||||||
return translated
|
return translated
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -646,7 +654,7 @@ func (s *SubService) genTemplatedRemark(inbound *model.Inbound, client model.Cli
|
|||||||
}
|
}
|
||||||
var tmpl string
|
var tmpl string
|
||||||
if s.subscriptionBody {
|
if s.subscriptionBody {
|
||||||
tmpl = s.effectiveTemplate(client.Email)
|
tmpl = s.effectiveTemplate(client)
|
||||||
} else {
|
} else {
|
||||||
tmpl = filterRemarkTemplate(translateUISingleBrackets(s.remarkTemplate), displayRemoveTokens)
|
tmpl = filterRemarkTemplate(translateUISingleBrackets(s.remarkTemplate), displayRemoveTokens)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -649,7 +649,6 @@ func TestUsageOnFirstLinkOnly_SingleBracket(t *testing.T) {
|
|||||||
}
|
}
|
||||||
client := model.Client{Email: "alice@x"}
|
client := model.Client{Email: "alice@x"}
|
||||||
first := s.genTemplatedRemark(inbound, client, "", "ws")
|
first := s.genTemplatedRemark(inbound, client, "", "ws")
|
||||||
s.usageShown["alice@x"] = true
|
|
||||||
second := s.genTemplatedRemark(inbound, client, "", "ws")
|
second := s.genTemplatedRemark(inbound, client, "", "ws")
|
||||||
if !strings.Contains(first, "📊") {
|
if !strings.Contains(first, "📊") {
|
||||||
t.Fatalf("first link should carry usage: %q", first)
|
t.Fatalf("first link should carry usage: %q", first)
|
||||||
@@ -675,7 +674,6 @@ func TestEmailOnFirstLinkOnly(t *testing.T) {
|
|||||||
}
|
}
|
||||||
client := model.Client{Email: "alice@x"}
|
client := model.Client{Email: "alice@x"}
|
||||||
first := s.genTemplatedRemark(inbound, client, "", "ws")
|
first := s.genTemplatedRemark(inbound, client, "", "ws")
|
||||||
s.usageShown["alice@x"] = true
|
|
||||||
second := s.genTemplatedRemark(inbound, client, "", "ws")
|
second := s.genTemplatedRemark(inbound, client, "", "ws")
|
||||||
if !strings.Contains(first, "alice@x") {
|
if !strings.Contains(first, "alice@x") {
|
||||||
t.Fatalf("first link should carry email: %q", first)
|
t.Fatalf("first link should carry email: %q", first)
|
||||||
@@ -724,3 +722,28 @@ func TestIdentityOnAllLinks(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSharedSubIDRemark_FullInfoOncePerSubscription(t *testing.T) {
|
||||||
|
const tmpl = "{{INBOUND}}-{{EMAIL}}"
|
||||||
|
s := &SubService{
|
||||||
|
remarkTemplate: tmpl,
|
||||||
|
subscriptionBody: true,
|
||||||
|
usageShown: map[string]bool{},
|
||||||
|
}
|
||||||
|
first := model.Client{Email: "first@example", SubID: "shared-sub"}
|
||||||
|
second := model.Client{Email: "second@example", SubID: "shared-sub"}
|
||||||
|
if got := s.genTemplatedRemark(&model.Inbound{Remark: "DE"}, first, "", "tcp"); got != "DE-first@example" {
|
||||||
|
t.Fatalf("first credential remark = %q", got)
|
||||||
|
}
|
||||||
|
if got := s.genTemplatedRemark(&model.Inbound{Remark: "FI"}, second, "", "tcp"); got != "FI" {
|
||||||
|
t.Fatalf("second credential with shared subId remark = %q, want identity suppressed", got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGenTemplatedRemarkPreservesConfiguredOuterWhitespace(t *testing.T) {
|
||||||
|
s := &SubService{remarkTemplate: " {{INBOUND}} ", subscriptionBody: true, usageShown: map[string]bool{}}
|
||||||
|
got := s.genTemplatedRemark(&model.Inbound{Remark: "DE"}, model.Client{Email: "user@example.test"}, "", "tcp")
|
||||||
|
if got != " DE " {
|
||||||
|
t.Fatalf("remark = %q, want configured outer whitespace preserved", got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -37,9 +37,8 @@ type SubService struct {
|
|||||||
// other context — the sub info page, the panel's link/QR displays — renders
|
// other context — the sub info page, the panel's link/QR displays — renders
|
||||||
// the name-only template, like Remnawave.
|
// the name-only template, like Remnawave.
|
||||||
subscriptionBody bool
|
subscriptionBody bool
|
||||||
// usageShown tracks, per client email, whether the info part of the template
|
// usageShown emits info once per subscription identity, including twins.
|
||||||
// has already been emitted this request, so it appears on the first body
|
// PrepareForRequest resets this per-request state.
|
||||||
// link only. Per-request state; reset in PrepareForRequest.
|
|
||||||
usageShown map[string]bool
|
usageShown map[string]bool
|
||||||
showIdentityOnAllLinks bool
|
showIdentityOnAllLinks bool
|
||||||
inboundService service.InboundService
|
inboundService service.InboundService
|
||||||
|
|||||||
Reference in New Issue
Block a user