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:
Nethmina Yasas
2026-10-24 16:03:02 +05:30
committed by GitHub
parent d103c0c012
commit bc1b750e00
47 changed files with 1940 additions and 168 deletions
+10 -3
View File
@@ -38,9 +38,16 @@ func (p *LinkProvider) SubLinksForSubId(host, subId string) ([]string, error) {
func (p *LinkProvider) LinksForClient(host string, inbound *model.Inbound, email string) []string {
svc := p.build(host)
svc.projectThroughFallbackMaster(inbound)
if endpoints := svc.hostEndpoints(inbound, "raw"); len(endpoints) > 0 {
if client, ok := svc.clientForLink(inbound, email); ok {
return splitLinkLines(svc.linkFromHosts(inbound, client, endpoints))
baseEps := svc.hostEndpoints(inbound, "raw")
if client, ok := svc.clientForLink(inbound, email); ok {
effectiveEps, bound := svc.clientHostEndpoints(inbound, client, "raw", baseEps)
if bound && len(effectiveEps) == 0 {
return nil
}
if len(effectiveEps) > 0 {
if link := svc.linkFromHosts(inbound, client, effectiveEps); link != "" {
return splitLinkLines(link)
}
}
}
return splitLinkLines(svc.GetLink(inbound, email))