Files
3x-ui/internal/web/service/inbound_exclude_from_sub_test.go
T
mrchatam 6f40a75909 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>
2026-09-27 12:26:37 +02:00

58 lines
1.4 KiB
Go

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")
}
}