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
+12
View File
@@ -22,6 +22,7 @@ func NewHostController(g *gin.RouterGroup) *HostController {
func (a *HostController) initRouter(g *gin.RouterGroup) {
g.GET("/list", a.list)
g.GET("/raw", a.raw)
g.GET("/get/:groupId", a.get)
g.GET("/byInbound/:inboundId", a.byInbound)
g.GET("/tags", a.tags)
@@ -45,6 +46,17 @@ func (a *HostController) list(c *gin.Context) {
jsonObj(c, hosts, nil)
}
// raw lists every host row with its numeric id for id-based pickers such as
// the per-client host rule override.
func (a *HostController) raw(c *gin.Context) {
hosts, err := a.hostService.GetRawHosts()
if err != nil {
jsonMsg(c, I18nWeb(c, "pages.hosts.toasts.list"), err)
return
}
jsonObj(c, hosts, nil)
}
func (a *HostController) get(c *gin.Context) {
groupId := c.Param("groupId")
h, err := a.hostService.GetHostGroup(groupId)