mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-11-05 12:45:00 +03:00
feat(inbound): excludeFromSub hides links without disabling (#6463)
* feat(inbound): excludeFromSub hides links without disabling Add a per-inbound flag that omits subscription output while keeping the inbound enabled for Xray, auth, and traffic accounting. Fixes #6435. * fix(inbound): excludeFromSub review follow-ups gofumpt model.go, sync docs OpenAPI, keep excludeFromSub master-authored on node mirror, and exercise the legacy add-column migration path in tests. * fix(sub): keep excluded inbounds' clients in the usage header The excludeFromSub filter sat in getInboundsBySubId's SQL, so an excluded inbound's clients never reached seenEmails in the raw, Clash or JSON renderer. A client that lives only on a hidden inbound (one client per inbound sharing a subId) dropped out of the Subscription-Userinfo usage, quota and expiry and out of the info-node state, while the inbound kept serving it and counting its traffic. The query returns every enabled inbound again; each renderer skips an excluded inbound's links but still counts its clients, the same rule the Clash renderer already applies to external links it cannot express. --------- Co-authored-by: mrchatam <mrchatam@users.noreply.github.com> Co-authored-by: MHSanaei <ho3ein.sanaei@gmail.com>
This commit is contained in:
@@ -1864,6 +1864,7 @@ func (s *InboundService) UpdateInbound(inbound *model.Inbound) (*model.Inbound,
|
||||
oldInbound.Total = inbound.Total
|
||||
oldInbound.Remark = inbound.Remark
|
||||
oldInbound.SubSortIndex = inbound.SubSortIndex
|
||||
oldInbound.ExcludeFromSub = inbound.ExcludeFromSub
|
||||
oldInbound.Enable = inbound.Enable
|
||||
oldInbound.ExpiryTime = inbound.ExpiryTime
|
||||
oldInbound.TrafficReset = inbound.TrafficReset
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/mhsanaei/3x-ui/v3/internal/database"
|
||||
"github.com/mhsanaei/3x-ui/v3/internal/database/model"
|
||||
)
|
||||
|
||||
func TestUpdateInbound_PersistsExcludeFromSub(t *testing.T) {
|
||||
setupConflictDB(t)
|
||||
|
||||
ib := makeInboundWithSubSortIndex("in-7004-tcp", 7004, 1)
|
||||
if err := database.GetDB().Create(ib).Error; err != nil {
|
||||
t.Fatalf("create inbound: %v", err)
|
||||
}
|
||||
|
||||
update := *ib
|
||||
update.ExcludeFromSub = true
|
||||
got, _, err := (&InboundService{}).UpdateInbound(&update)
|
||||
if err != nil {
|
||||
t.Fatalf("UpdateInbound: %v", err)
|
||||
}
|
||||
if !got.ExcludeFromSub {
|
||||
t.Fatal("returned ExcludeFromSub = false, want true")
|
||||
}
|
||||
|
||||
var reloaded model.Inbound
|
||||
if err := database.GetDB().First(&reloaded, ib.Id).Error; err != nil {
|
||||
t.Fatalf("reload: %v", err)
|
||||
}
|
||||
if !reloaded.ExcludeFromSub {
|
||||
t.Fatal("persisted ExcludeFromSub = false, want true")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddInbound_PersistsExcludeFromSub(t *testing.T) {
|
||||
setupConflictDB(t)
|
||||
|
||||
ib := makeInboundWithSubSortIndex("in-7005-tcp", 7005, 1)
|
||||
ib.ExcludeFromSub = true
|
||||
got, _, err := (&InboundService{}).AddInbound(ib)
|
||||
if err != nil {
|
||||
t.Fatalf("AddInbound: %v", err)
|
||||
}
|
||||
if !got.ExcludeFromSub {
|
||||
t.Fatal("returned ExcludeFromSub = false, want true")
|
||||
}
|
||||
|
||||
var reloaded model.Inbound
|
||||
if err := database.GetDB().First(&reloaded, got.Id).Error; err != nil {
|
||||
t.Fatalf("reload: %v", err)
|
||||
}
|
||||
if !reloaded.ExcludeFromSub {
|
||||
t.Fatal("persisted ExcludeFromSub = false, want true")
|
||||
}
|
||||
}
|
||||
@@ -412,6 +412,8 @@ func adoptedWireInbound(c, snapIb *model.Inbound, adoptedSettings string) *model
|
||||
a.Enable = snapIb.Enable
|
||||
a.Remark = snapIb.Remark
|
||||
a.SubSortIndex = normalizeSubSortIndex(snapIb.SubSortIndex)
|
||||
// ExcludeFromSub stays master-authored: older nodes omit the field and
|
||||
// would otherwise reset it to false on every heartbeat mirror.
|
||||
a.Listen = snapIb.Listen
|
||||
a.Port = snapIb.Port
|
||||
a.Protocol = snapIb.Protocol
|
||||
@@ -736,6 +738,7 @@ func (s *InboundService) setRemoteTrafficLocked(nodeID int, snap *runtime.Traffi
|
||||
Enable: snapIb.Enable,
|
||||
Remark: snapIb.Remark,
|
||||
SubSortIndex: normalizeSubSortIndex(snapIb.SubSortIndex),
|
||||
ExcludeFromSub: snapIb.ExcludeFromSub,
|
||||
Total: snapIb.Total,
|
||||
ExpiryTime: snapIb.ExpiryTime,
|
||||
Up: snapIb.Up,
|
||||
|
||||
Reference in New Issue
Block a user