mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-08-17 00:31:00 +00:00
fix(sub): keep the client identity on every subscription link (#6098)
876d55f2 put EMAIL/USERNAME in the same first-link-only bucket as the usage
tokens, so a client attached to several inbounds got its email on whichever
inbound sorted first and bare inbound names on all the rest. With the shipped
default template ({{INBOUND}}-{{EMAIL}}|...) that makes every profile after
the first indistinguishable between clients — the point of the token.
The two are not alike: the usage block repeats identical numbers on every
link, while the identity is what tells one imported profile from another.
Restore identity on all body links and leave usage first-link-only.
Reported again in #6029 and #5659, which asked for the same revert.
This commit is contained in:
@@ -481,15 +481,6 @@ var connectionTokens = map[string]bool{
|
|||||||
|
|
||||||
var displayRemoveTokens = mergeTokenSets(usageInfoTokens, connectionTokens)
|
var displayRemoveTokens = mergeTokenSets(usageInfoTokens, connectionTokens)
|
||||||
|
|
||||||
// firstLinkOnlyBodyTokens are stripped from every subscription-body link after a
|
|
||||||
// client's first one: the usage/info tokens plus the per-client EMAIL/USERNAME
|
|
||||||
// identity. A client app needs the email once, so repeating it on every link of
|
|
||||||
// the same subscription is noise — show it on the first link only, like traffic.
|
|
||||||
var firstLinkOnlyBodyTokens = mergeTokenSets(usageInfoTokens, map[string]bool{
|
|
||||||
"EMAIL": true,
|
|
||||||
"USERNAME": true,
|
|
||||||
})
|
|
||||||
|
|
||||||
func mergeTokenSets(sets ...map[string]bool) map[string]bool {
|
func mergeTokenSets(sets ...map[string]bool) map[string]bool {
|
||||||
out := make(map[string]bool)
|
out := make(map[string]bool)
|
||||||
for _, set := range sets {
|
for _, set := range sets {
|
||||||
@@ -560,7 +551,7 @@ func (s *SubService) effectiveTemplate(email string) string {
|
|||||||
s.usageShown = map[string]bool{}
|
s.usageShown = map[string]bool{}
|
||||||
}
|
}
|
||||||
if s.usageShown[email] {
|
if s.usageShown[email] {
|
||||||
return filterRemarkTemplate(translated, firstLinkOnlyBodyTokens)
|
return filterRemarkTemplate(translated, usageInfoTokens)
|
||||||
}
|
}
|
||||||
s.usageShown[email] = true
|
s.usageShown[email] = true
|
||||||
return translated
|
return translated
|
||||||
|
|||||||
@@ -386,8 +386,8 @@ func TestIdentityTokenBodyVsDisplay(t *testing.T) {
|
|||||||
|
|
||||||
body := &SubService{remarkTemplate: tmpl, subscriptionBody: true, usageShown: map[string]bool{}}
|
body := &SubService{remarkTemplate: tmpl, subscriptionBody: true, usageShown: map[string]bool{}}
|
||||||
_ = body.genTemplatedRemark(inbound, client, "", "ws") // first link consumes the usage block
|
_ = body.genTemplatedRemark(inbound, client, "", "ws") // first link consumes the usage block
|
||||||
if second := body.genTemplatedRemark(inbound, client, "", "ws"); strings.Contains(second, "john@x") {
|
if second := body.genTemplatedRemark(inbound, client, "", "ws"); !strings.Contains(second, "john@x") {
|
||||||
t.Fatalf("repeat body link %q must drop the identity token", second)
|
t.Fatalf("repeat body link %q must keep the identity token", second)
|
||||||
}
|
}
|
||||||
|
|
||||||
display := &SubService{remarkTemplate: tmpl, subscriptionBody: false}
|
display := &SubService{remarkTemplate: tmpl, subscriptionBody: false}
|
||||||
@@ -624,7 +624,10 @@ func TestUsageOnFirstLinkOnly_SingleBracket(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEmailOnFirstLinkOnly(t *testing.T) {
|
// Every link of a subscription carries the client's identity, because that is
|
||||||
|
// what tells one imported profile from another in the client app. Only the
|
||||||
|
// usage block, which is identical on all of them, is first-link-only (#6098).
|
||||||
|
func TestEmailOnEveryLink(t *testing.T) {
|
||||||
s := &SubService{
|
s := &SubService{
|
||||||
remarkTemplate: "{{INBOUND}} {{EMAIL}}|📊{{TRAFFIC_LEFT}}",
|
remarkTemplate: "{{INBOUND}} {{EMAIL}}|📊{{TRAFFIC_LEFT}}",
|
||||||
subscriptionBody: true,
|
subscriptionBody: true,
|
||||||
@@ -640,15 +643,22 @@ 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") {
|
third := s.genTemplatedRemark(inbound, client, "", "ws")
|
||||||
t.Fatalf("first link should carry email: %q", first)
|
for i, remark := range []string{first, second, third} {
|
||||||
|
if !strings.Contains(remark, "alice@x") {
|
||||||
|
t.Fatalf("link %d must carry the client email: %q", i+1, remark)
|
||||||
|
}
|
||||||
|
if !strings.Contains(remark, "DE") {
|
||||||
|
t.Fatalf("link %d must carry the inbound name: %q", i+1, remark)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if strings.Contains(second, "alice@x") {
|
if !strings.Contains(first, "📊") {
|
||||||
t.Fatalf("second link must not carry email: %q", second)
|
t.Fatalf("first link should carry the usage block: %q", first)
|
||||||
}
|
}
|
||||||
if !strings.Contains(second, "DE") {
|
for i, remark := range []string{second, third} {
|
||||||
t.Fatalf("second link should still carry the inbound name: %q", second)
|
if strings.Contains(remark, "📊") {
|
||||||
|
t.Fatalf("repeat link %d must still drop the usage block: %q", i+2, remark)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user