mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-10-25 15:25:01 +03:00
feat(clients): bind host rules to individual clients in subscriptions (#6620)
Host rules applied per inbound only, so every client of an inbound got the same subscription endpoints (#6618). A client can now be bound to one or more host rules, and its raw, JSON and Clash subscriptions render only those hosts, in either subscription order; an unbound client is unchanged. A binding whose hosts are deleted, disabled or excluded from the format fails closed: the client gets no links there but stays in traffic accounting. Host edits keep row ids, so bindings survive them; nil leaves a stored binding alone and an empty list clears it.
This commit is contained in:
@@ -2,6 +2,7 @@ package model
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
@@ -270,3 +271,26 @@ func TestGenXrayInboundConfig_OmitsInboundXmuxButDbRowUnchanged(t *testing.T) {
|
||||
t.Fatal("inbound row streamSettings must still carry xmux for subscriptions")
|
||||
}
|
||||
}
|
||||
|
||||
// nil must survive (it means "leave the stored binding alone"), while an empty or
|
||||
// all-invalid list must come back empty so it clears.
|
||||
func TestNormalizeClientHostRuleIds(t *testing.T) {
|
||||
for _, tt := range []struct {
|
||||
name string
|
||||
in []int
|
||||
want []int
|
||||
}{
|
||||
{"nil preserves", nil, nil},
|
||||
{"empty clears", []int{}, []int{}},
|
||||
{"dedupe keeps order", []int{3, 2, 3, 2}, []int{3, 2}},
|
||||
{"non-positive dropped", []int{0, -1, 2, 0, 3}, []int{2, 3}},
|
||||
{"all non-positive clears", []int{0, -2}, []int{}},
|
||||
} {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := NormalizeClientHostRuleIds(tt.in)
|
||||
if (got == nil) != (tt.want == nil) || !reflect.DeepEqual(got, tt.want) {
|
||||
t.Fatalf("NormalizeClientHostRuleIds(%v) = %#v, want %#v", tt.in, got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user